我想为练习编写一个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
次调用,代理似乎会“处理”更多的请求,但不是所有请求。答案 0 :(得分:4)
乔纳森走在正确的轨道上。问题部分在于您使用available()
。方法available
没有说“它完成了吗?”它说“目前有没有可用的数据?”。因此,在您提出请求后,将立即没有可用的数据,并且取决于处理过程中可能发生的网络时间,但这并不意味着不再有任何数据,因此您的break
是过早。
此外,InputStream.read(byte[] ...)
系列方法始终允许返回的字节数少于您要求的字节数。数组长度或偏移量,长度对约束最大值,但总是可以减少。所以,你的这段代码:
def buffer = new byte[available]
def bytesRead = inStream.read(buffer,0,available)
actualBuffer << new String(buffer)
可以创建一个大数组,但是只能在读取中获得半数数据,但仍然将完整的缓冲区(带有其未跟踪的数组元素)附加到String上。
这是一个依赖于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()
}
那就是说,你还有其他一些问题:
答案 1 :(得分:3)
答案 2 :(得分:1)
Ry4an提出了一些好处。如果你想看看如何构造一个小但完美形成的代理,看看用Python编写的Tiny HTTP Proxy - 你可以看到所有需要解决的问题,并且移植代码相当简单到Groovy。我已经将代理用于测试目的并且运行良好。
答案 3 :(得分:0)
我建议你熟悉HTTP protocol specification。 HTTP比单独的TCP连接上的单个请求 - 响应更复杂 - 即如果客户端或服务器尝试使用持久连接,则实现将失败。
答案 4 :(得分:0)
readClientData(套接字)中是否存在竞争条件?您似乎正在立即检查数据是否可用,但有可能尚未收到数据;你只需退出循环而不是等待接收第一个数据。
答案 5 :(得分:0)
客户端套接字是否阻塞?如果是这样,您可能想尝试非阻塞I / O或设置套接字超时。