试图重用Java客户端套接字时不成功。
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了试图重用Java客户端套接字时不成功。相关的知识,希望对你有一定的参考价值。
我有一个与第三方控制器通信的软件驱动程序;我有一个用于使用后者的API,但看不到它的源代码,而供应商也不合作,试图改进事情!我的情况如下。
情况是这样的。
为了向控制器发送请求,我发送一个XML包作为HTTP POST的内容给一个servlet,然后servlet给我发送响应。原来的代码,由之前的开发人员实现,使用java.net.Socket可以稳定工作。然而,我们的驱动是这样实现的,每发送一个请求都会创建一个新的套接字,如果驱动忙起来,第三方控制器在套接字处理方面就很难跟上。事实上,他们的支持人员对我说:"你真的需要留出5秒钟的时间。"你真的需要在每个请求之间留出5秒的时间..." 这在商业上根本无法接受。
为了提高性能,我想尝试让我们的套接字端打开,并无限期地重复使用套接字(当然考虑到连接可能会意外掉落,但这是我最不关心的问题,是可以控制的)。然而,无论我看起来做什么,效果都是,如果我使用Comms.getSocket(false),每一个请求都会创建一个新的套接字,一切都能正常工作,但忙的时候会出现瓶颈。如果我使用Comms.getSocket(true),会发生以下情况。
- 控制器发送了第一个请求
- 控制器对第一个请求作出回应
- 控制器被发送第二个请求(可能5秒后)。
- 控制员从不响应第二个请求或之后的任何事情。
- postRequest()一直被调用:在前12秒,控制台输出 "Input shut down ? false",但之后,代码不再到达那里,也没有通过bw.write()和bw.flush()调用。
控制器允许HTTP 1.0和1.1,但他们的文档中没有提到keep-alive。我尝试了这两种模式,下面的代码显示我也添加了Keep-Alive头信息,但是作为服务器的控制器,我猜测是忽略了它们 -- 我想我没有办法知道,是吗?在HTTP 1.0模式下,控制器肯定会返回 "Connection: close",但在HTTP 1.1模式下不会这样做。
那么很可能是服务器端坚持 "每个请求一个socket "的方法。
然而,我想知道,在下面的代码中,我是否可能做错了什么(或者遗漏了什么)来实现我想要的东西。
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 reuse)的方法来提供套接字,我可以选择总是创建一个新的套接字或者重用Comms为我创建的套接字,如下所示。
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;
谁能帮帮我?
如果我们假设keep-alive的工作符合预期,我认为这一行是这样的 while ((line = br.readLine()) != null)
是一个错误的,因为这是一种无限循环。
readline()
返回 null
当没有更多的数据可读时,例如a EOF
,或者当serverclient关闭连接时,这将破坏你的重用套接字解决方案,因为一个开放的流永远不会导致一个 null
到 readLine()
调用,但会阻塞。
你需要修正关于读取响应的算法(为什么不使用已实现的http客户端?content-length
当从body中读取所需的数据量时,通过保持socket的活力进行下一个循环。flag
到 true
读取数据时,必须知道要读取的是什么数据(考虑到mimecontent-type),除此之外,还要知道数据的长度,所以读取数据时要使用 readLine()
在这里可能不是一个好的做法。
同时确保服务器允许持久化连接,通过检查它是否尊重它,通过响应相同的 connection:keep-alive
头部。
以上是关于试图重用Java客户端套接字时不成功。的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Java 客户端和 Python 服务器通过套接字创建 IPC?