尝试重用Java客户端套接字失败

时间:2020-06-03 15:25:36

标签: java sockets http servlets

我有一个与第三方控制器通信的软件驱动程序;我有一个使用后者的API,但没有其源代码的可见性,而且供应商不合作尝试改善问题!

情况如下。

要向控制器发送请求,我将XML数据包作为HTTP POST的内容发送到servlet,然后该servlet向我发送响应。由以前的开发人员实现的原始代码可以使用java.net.Socket稳定地工作。但是,我们的驱动程序的实现方式是为每个发送的请求创建一个新的套接字,并且,如果驱动程序变得繁忙,则第三方控制器将在套接字处理方面努力跟上。实际上,他们的支持人员对我说:“您确实需要在每次请求之间留出5秒的时间...”。这根本在商业上是不可接受的。

为了提高性能,我想尝试使套接字的末端保持打开状态,并无限期地重用套接字(当然,鉴于连接可能会意外断开,但这是我最不担心的事情,并且是可管理的)。但是,无论我做什么,都会产生影响,如果我使用Comms.getSocket(false),则会为每个请求创建一个新的套接字,并且一切正常,但在繁忙时会出现瓶颈。如果我使用Comms.getSocket(true),则会发生以下情况:

  • 向控制器发送第一个请求
  • 控制器响应第一个请求
  • 向控制器发送第二个请求(可能在5秒后)
  • 控制器从不响应第二个请求或之后的任何请求
  • postRequest()一直被调用:在最初的12秒钟中,控制台输出“ Input shutdown down?false”,但是此后,代码不再到达那里并且不会越过bw.write()和bw.flush()调用。

该控制器同时允许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;
}

任何人都可以帮忙吗?

1 个答案:

答案 0 :(得分:0)

如果我们假设保持活动状态按预期运行,那么我认为while ((line = br.readLine()) != null)行是有缺陷的,因为这是无限循环。

当没有更多数据要读取时,例如

readline()返回nullEOF,或者当服务器/客户端关闭连接时,这将破坏您重复使用的套接字解​​决方案,因为开放流永远不会导致nullreadLine()的调用,但是会阻塞

您需要修复有关读取响应(为什么不使用已实现的http客户端?),检查content-length的算法,并在从主体读取所需数据量时,通过保持套接字处于活动状态进行下一个循环。 将flag设置为true之后,您必须知道应该读取哪种数据(考虑mime / content-type),此外还要知道数据的长度,因此使用{{1 }}在这里可能不是一个好习惯。

还要通过响应相同的readLine()标头来检查服务器是否遵守持久连接,从而确保服务器允许持久连接。