我想写一个 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/