java - 请帮我弄清楚这个网络代理代码有什么问题

标签 java sockets groovy

我想写一个 web 代理来练习,这是我目前的代码:


// returns a map that contains the port and the host
def parseHostAndPort(String data) {
    def objMap // this has host and port as keys
    data.eachLine { line ->
        if(line =~ /^(?i)get|put|post|head|trace|delete/) {
            println line
            def components = line.split(" ")
            def resource = components[1]
            def colon = resource.indexOf(":")
            if(colon != -1) {
                URL u = new URL(resource)
                def pHost = u.host
                def pPort = u.port
                return (objMap = [host:pHost,port:pPort])
            }
            else {
                return (objMap = [host:resource,port:80])
            }
        }
    }
    return objMap
}

// reads a http request from a client
def readClientData(Socket clientSocket) {
    def actualBuffer = new StringBuilder()
    InputStream inStream = clientSocket.inputStream
    while(true) {
        def available = inStream.available()
        if(available == 0)
        break;
        println "available data $available"
        def buffer = new byte[available]
        def bytesRead = inStream.read(buffer,0,available)
        actualBuffer << new String(buffer)
    }
    return actualBuffer.toString()
}

def sock = new ServerSocket(9000)
sock.reuseAddress = true
while(true) {
    sock.accept { cli ->
        println "got a client"
        def data = readClientData(cli)
        def parsed = parseHostAndPort(data)
        def host = parsed["host"]
        def port = parsed["port"]

        println "got from client $data"

        def nsock = new Socket(host,port)
        nsock << data // send data received from client to the socket
        nsock.outputStream.flush() 
        def datax = readClientData(nsock)
        println "got back $datax"
        cli << datax // send the client the response
        cli.outputStream.flush()
        cli.close()
    }
}

现在,它所做的只是:

  • 读取我的浏览器发送的 HTTP 请求

  • 解析主机和端口

  • 连接到该主机,并写入从客户端接收到的数据

  • 客户端将从主机接收到的数据发回

但是……它并不是一直都有效。有时它会提出一个很好的要求,有时则不然。我认为这是一个缓冲问题,我不确定。问题是,我添加了 flush 调用,但仍然没有。

你能发现我做错了什么吗?

编辑:

  • 我注意到,如果我添加一些 sleep 调用,代理似乎可以“处理”更多请求,但不是所有请求。
  • 收集赏金,帮助我找出我做错了什么。用于 Web 代理的常规“算法”是什么?我在哪里偏离它?谢谢!

最佳答案

乔纳森走在正确的轨道上。问题部分在于您对 available() 的使用。 available 方法不会说“完成了吗?”它说“目前有可用的数据吗?”。因此,在您发出请求后,将不会立即有任何数据可用,并且取决于处理过程中可能发生的网络时间,但这并不意味着不会再有数据了,所以您的中断 为时过早。

此外,InputStream.read(byte[] ...) 系列方法始终允许返回的字节数少于您要求的字节数。数组长度或偏移量、长度对限制了最大值,但您总是可以得到更少。所以,你的这段代码:

    def buffer = new byte[available]
    def bytesRead = inStream.read(buffer,0,available)
    actualBuffer << new String(buffer)

可以创建一个大数组,但在读取时只得到它一半的数据,但仍然将完整的缓冲区(及其尾随的未读数组元素)追加到字符串上。

这里的修订依赖于 InputStream.read(...) 永远不会返回的事实,除非它是流的结尾或有一些可用的数据(但不一定像你问的那么多) .

// reads a http request from a client
def readClientData(Socket clientSocket) {
    def actualBuffer = new StringBuilder()
    InputStream inStream = clientSocket.inputStream
    int bytesRead = 0;
    byte[] buffer = new byte[16 * 1024];
    while((bytesRead = inStream.read(buffer)) >= 0) { // -1 on EOF
        def bytesRead = inStream.read(buffer,0,bytesRead); // only want newly read bytes
        actualBuffer << new String(buffer)
    }
    return actualBuffer.toString()
}

也就是说,您还有其他一些问题:

  • 您正在将整个响应拉入内存,而您应该在字节泵循环中将其直接复制到客户端的响应输出流中(如果它是数 GB 的响应会发生什么情况)
  • 您正在使用字符串来存储二进制数据——假设所有字节在默认字符编码中都能正常工作,这在 UTF-8 或 US-ASCII 中可能是正确的,但不适用于其他语言环境

关于java - 请帮我弄清楚这个网络代理代码有什么问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1262007/

相关文章:

java - Android/LibGDX 上 OpenGL 2 中的着色器和变量

java - 如何在 JSF 中关闭浏览器时使 session 无效

java - 通过java套接字传输文件客户端-服务器

c - 使用 icmp 套接字时是否应该将 id 写入 icmphdr id 字段?

grails - 在grails Job中执行脚本

java - Groovy 中的日期格式匹配

java - 如何设置文本格式?

java - 使用 Maven 将 gwt 客户端代码编译为 javascript

vxworks - 如何在 VxWorks 中为单个套接字设置 keepalive 选项

groovy - 将 GString 存储在变量中并在运行时求值