繁体   English   中英

尝试重用Java客户端socket失败

[英]Unsuccessful in trying to reuse Java client socket

我有一个与第三方 controller 通信的软件驱动程序; 我有一个 API 用于使用后者,但无法查看其源代码,并且供应商不合作尝试改进!

情况如下。

为了向 controller 发送请求,我将 XML 数据包作为 HTTP POST 的内容发送到 servlet,然后将响应发送给我。 原始代码由以前的开发人员实现,使用 java.net.Socket 可以稳定运行。 但是,我们的驱动程序是这样实现的,即为每个发送的请求创建一个新的套接字,如果驱动程序很忙,第三方 controller 很难跟上套接字处理的速度。 事实上,他们的支持人员对我说:“你真的需要在每个请求之间留出 5 秒......”。 这在商业上根本不可接受。

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

  • Controller 发送第一个请求
  • Controller 响应第一个请求
  • Controller 发送第二个请求(可能在 5 秒后)
  • Controller 从不响应第二个请求或之后的任何内容
  • postRequest() 不断被调用:在前 12 秒内,控制台输出“输入关闭?假”,但在那之后,代码不再到达那里并且没有通过 bw.write() 和 bw.flush () 调用。

controller 允许 HTTP 1.0 和 1.1 但他们的文档说 zilch 关于保活。 我都试过了,下面的代码显示我也添加了 Keep-Alive 标头,但是 controller 作为服务器,我猜是忽略它们——我想我没有任何办法知道,做我? 在 HTTP 1.0 模式下,controller 肯定会返回“连接:关闭”,但在 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 助手 class 和一个名为 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 ,例如EOF ,或者当服务器/客户端关闭连接时,这将破坏您的重用套接字解决方案,因为打开的readLine()永远不会导致null readLine()调用,但阻塞。

您需要修复有关读取响应的 alg(为什么不使用已实现的 http 客户端?),检查content-length ,并在从正文中读取所需数据量时, go 通过保持套接字活动来进行下一个循环。 flag设置为true之后,您必须知道应该读取哪种数据(考虑 mime/content-type),除此之外,数据的长度,因此使用readLine()读取数据可能不是一个好习惯。

还要确保服务器允许持久连接,通过响应相同的connection:keep-alive header。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM