试图重用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关闭连接时,这将破坏你的重用套接字解决方案,因为一个开放的流永远不会导致一个 nullreadLine() 调用,但会阻塞。

你需要修正关于读取响应的算法(为什么不使用已实现的http客户端?content-length当从body中读取所需的数据量时,通过保持socket的活力进行下一个循环。flagtrue读取数据时,必须知道要读取的是什么数据(考虑到mimecontent-type),除此之外,还要知道数据的长度,所以读取数据时要使用 readLine() 在这里可能不是一个好的做法。

同时确保服务器允许持久化连接,通过检查它是否尊重它,通过响应相同的 connection:keep-alive 头部。

以上是关于试图重用Java客户端套接字时不成功。的主要内容,如果未能解决你的问题,请参考以下文章

重用异步套接字:后续连接尝试失败

WSA UDP 套接字无法重用,因为它强制关闭连接

套接字是不是在重新连接事件中重用?

如何使用 Java 客户端和 Python 服务器通过套接字创建 IPC?

如何重用 socket.io 发射功能?试图将套接字发射功能附加到 SwiftUI 按钮(操作)?

C#异步套接字服务器没有收到来自Java客户端的响应