尝试重用Java客户端套接字失败
我有一个与第三方控制器通信的软件驱动程序;我有一个使用后者的API,但它的源代码不可见,供应商在试图改进方面也不合作 情况如下 为了向控制器发送请求,我将一个XML数据包作为HTTP POST的内容发送到servlet,然后servlet向我发送响应。由以前的开发人员实现的原始代码使用java.net.Socket稳定地工作。然而,我们的驱动程序是这样实现的,即为每个发送的请求创建一个新的套接字,并且,如果驱动程序变得繁忙,第三方控制器将难以跟上套接字处理的步伐。事实上,他们的支持人员对我说:“你真的需要在每个请求之间留出5秒钟……”。这在商业上是不可接受的 为了提高性能,我想尝试让套接字的一端保持打开状态,并无限期地重用套接字(当然,考虑到连接可能意外中断,但这是我最不关心的问题,也是可以管理的)。然而,无论我做什么,结果是如果我使用Comms.getSocket(false),就会为每个请求创建一个新的套接字,并且一切正常,但在繁忙时会出现瓶颈。如果使用Comms.getSocket(true),则会发生以下情况:尝试重用Java客户端套接字失败,java,sockets,http,servlets,Java,Sockets,Http,Servlets,我有一个与第三方控制器通信的软件驱动程序;我有一个使用后者的API,但它的源代码不可见,供应商在试图改进方面也不合作 情况如下 为了向控制器发送请求,我将一个XML数据包作为HTTP POST的内容发送到servlet,然后servlet向我发送响应。由以前的开发人员实现的原始代码使用java.net.Socket稳定地工作。然而,我们的驱动程序是这样实现的,即为每个发送的请求创建一个新的套接字,并且,如果驱动程序变得繁忙,第三方控制器将难以跟上套接字处理的步伐。事实上,他们的支持人员对我说:“
- 控制器被发送第一个请求
- 控制器响应第一个请求
- 控制器收到第二个请求(可能5秒后)
- 控制器从不响应第二个请求或之后的任何请求
- postRequest()不断被调用:在前12秒,控制台输出“Input Shutdown?false”,但在那之后,代码不再到达那里,也无法通过bw.write()和bw.flush()调用
private String postRequest() throws IOException {
String resp = null;
String logMsg;
StringBuilder sb = new StringBuilder();
StringBuilder sbWrite = new StringBuilder();
Comms comms = getComms();
Socket socket = comms.getSocket(true);
BufferedReader br = comms.getReader();
BufferedWriter bw = comms.getWriter();
if (null != socket) {
System.out.println("Socket closed ? " + socket.isClosed());
System.out.println("Socket bound ? " + socket.isBound());
System.out.println("Socket connected ? " + socket.isConnected());
// Write the request
sbWrite
.append("POST /servlet/receiverServlet HTTP/1.1\r\n")
.append("Host: 192.168.200.100\r\n")
.append("Connection: Keep-Alive\r\n")
.append("Keep-Alive: timeout=10\r\n")
.append("Content-Type: text/xml\r\n")
.append("Content-Length: " + requestString.length() + "\r\n\r\n")
.append(requestString);
System.out.println("Writing:\n" + sbWrite.toString());
bw.write(sbWrite.toString());
bw.flush();
// Read the response
System.out.println("Input shut down ? " + socket.isInputShutdown());
String line;
boolean flag = false;
while ((line = br.readLine()) != null) {
System.out.println("Line: <" + line + ">");
if (flag) sb.append(line);
if (line.isEmpty()) flag = true;
}
resp = sb.toString();
}
else {
System.out.println("Socket not available");
}
return resp; // Another method will parse the response
}
有人能帮忙吗?如果我们假设keep-alive东西按预期工作,我认为行
而((line=br.readLine())!=null)
是错误的,因为这是一种无限循环
readline()
在没有更多数据可读取时返回null
,例如EOF
,或者当服务器/客户端关闭连接时,这将中断您的重用套接字解决方案,因为开放流决不会导致readline()
调用的null
,而是阻塞
您需要修复有关读取响应的alg(为什么不使用已实现的http客户端?),检查内容长度
,以及从正文中读取所需数据量时,通过保持套接字活动来进行下一个循环。
在将flag
设置为true
之后,您必须知道应该读取什么类型的数据(考虑mime/内容类型),除此之外,还要知道数据的长度,因此使用readLine()
读取数据在这里可能不是一个好的做法
还要确保服务器允许持久性连接,通过响应相同的
连接:keep-alive
头来检查它是否尊重持久性连接。如果我们假设keep-alive东西按预期工作,我认为行而((line=br.readLine())!=null)
是错误的,因为这是一种无限循环
readline()
在没有更多数据可读取时返回null
,例如EOF
,或者当服务器/客户端关闭连接时,这将中断您的重用套接字解决方案,因为开放流决不会导致readline()
调用的null
,而是阻塞
您需要修复有关读取响应的alg(为什么不使用已实现的http客户端?),检查内容长度
,以及从正文中读取所需数据量时,通过保持套接字活动来进行下一个循环。
在将flag
设置为true
之后,您必须知道应该读取什么类型的数据(考虑mime/内容类型),除此之外,还要知道数据的长度,因此使用readLine()
读取数据在这里可能不是一个好的做法
另外,通过响应相同的连接:keep-alive
头,检查服务器是否尊重持久性连接,确保服务器允许持久性连接
public Comms(String ip, int port) {
this.ip = ip;
this.port = port;
initSocket();
}
private void initSocket() {
try {
socket = new Socket(ip, port);
socket.setKeepAlive(true);
socket.setPerformancePreferences(1, 0, 0);
socket.setReuseAddress(true);
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8));
br = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8));
System.out.println("@@@ CREATED NEW SOCKET");
}
catch (UnknownHostException uhe) {
System.out.println("@@@ UNKNOWN HOST FOR SOCKET");
}
catch (IOException ioe) {
System.out.println("@@@ SOCKET I/O EXCEPTION");
}
}
public BufferedReader getReader() { return br; }
public BufferedWriter getWriter() { return bw; }
public Socket getSocket(boolean reuse) {
if (! reuse) initSocket();
return socket;
}