我有一个与第三方控制器通信的软件驱动程序;我有一个使用后者的API,但没有其源代码的可见性,而且供应商不合作尝试改善问题!
情况如下。
要向控制器发送请求,我将XML数据包作为HTTP POST的内容发送到servlet,然后该servlet向我发送响应。由以前的开发人员实现的原始代码可以使用java.net.Socket稳定地工作。但是,我们的驱动程序的实现方式是为每个发送的请求创建一个新的套接字,并且,如果驱动程序变得繁忙,则第三方控制器将在套接字处理方面努力跟上。实际上,他们的支持人员对我说:“您确实需要在每次请求之间留出5秒的时间...”。这根本在商业上是不可接受的。
为了提高性能,我想尝试使套接字的末端保持打开状态,并无限期地重用套接字(当然,鉴于连接可能会意外断开,但这是我最不担心的事情,并且是可管理的)。但是,无论我做什么,都会产生影响,如果我使用Comms.getSocket(false),则会为每个请求创建一个新的套接字,并且一切正常,但在繁忙时会出现瓶颈。如果我使用Comms.getSocket(true),则会发生以下情况:
该控制器同时允许HTTP 1.0和1.1,但他们的文档对保持连接表示zilch。我都尝试过,下面的代码显示我也添加了Keep-Alive标头,但作为服务器的控制器,我想是忽略了它们-我想我没有办法知道,一世 ?在HTTP 1.0模式下,控制器当然会返回“连接:关闭”,但在HTTP 1.1模式下不会这样做。
然后,服务器端可能会坚持“每个请求一个套接字”的方法。
但是,我想知道我是否可能在以下代码中做错了(或缺少某些东西)来实现我想要的目标:
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
}
为了简化测试,我使用一个额外的Comms帮助器类和一个名为getSocket(boolean复用)的方法来提供套接字,在这里我可以选择始终创建一个新的套接字或重用Comms为我创建的套接字,如下所示:< / p>
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;
}
任何人都可以帮忙吗?
答案 0 :(得分:0)
如果我们假设保持活动状态按预期运行,那么我认为while ((line = br.readLine()) != null)
行是有缺陷的,因为这是无限循环。
readline()
返回null
。 EOF
,或者当服务器/客户端关闭连接时,这将破坏您重复使用的套接字解决方案,因为开放流永远不会导致null
到readLine()
的调用,但是会阻塞
您需要修复有关读取响应(为什么不使用已实现的http客户端?),检查content-length
的算法,并在从主体读取所需数据量时,通过保持套接字处于活动状态进行下一个循环。
将flag
设置为true
之后,您必须知道应该读取哪种数据(考虑mime / content-type),此外还要知道数据的长度,因此使用{{1 }}在这里可能不是一个好习惯。
还要通过响应相同的readLine()
标头来检查服务器是否遵守持久连接,从而确保服务器允许持久连接。