为什么socket connect()方法超时?netcat(nc)工作正常

zbsbpyhn  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(428)

我有一个更大的java代码块,但重要的一行是:

public static String tcp(String hostName, Number port, Number connectionTimeOutMs, Number readTimeOutMs, String message) {
    String errmsg = "";
    try (
        Socket socket = new Socket();
    ) {
        Inet4Address address = (Inet4Address) Inet4Address.getByName(hostName);
        System.out.println("IP address:" + address.getHostAddress());
        socket.connect(new InetSocketAddress(address, port.intValue()), connectionTimeOutMs.intValue());
        socket.setSoTimeout(readTimeOutMs.intValue());

当我在表单中提供ip地址时 "45.79.112.203" 或者 "tcpbin.com" ,代码给出 SocketTimeoutException .
在后一种情况下

System.out.println("IP address:" + address.getHostAddress());

提供正确的ip地址,以便正确解析主机名;匹配什么 ping tcpbin.com 返回。
我希望能够使用ipv4地址(字符串格式)或主机名调用函数。
我做错什么了?为什么即使有60000毫秒的高超时,套接字也无法建立连接?
笔记: tcpbin.com 是一个“echo”服务器,用于测试套接字连接。它仅用作示例,不应成为问题的原因。
请尝试以下操作:

echo "Text to send to TCP" | nc tcpbin.com 4242

你应该拿回刚才发来的那条线。
在tcp()函数中,我以 Number 对象,因为java代码是通过java inter-op和javascript从空手道测试框架调用的。javascript的类型 Number ,但不是 int 或者 double .

更新:
这里是一个简单的tcp服务器tcpserver.java

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer {
    public static void main(String[] args) {
        System.out.println("Listening on port 4242");
        ServerSocket listener = null;
        try {
            do {
                listener = new ServerSocket(4242);
                Socket other = listener.accept();
                System.out.println(">>> got a new connection from "
                        + other.getInetAddress().toString() + " <<<");
                other.getOutputStream().write("Blah blah".getBytes());
                other.close();
                listener.close();
            } while (true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

===
这里有一个测试类来测试 tcp() 功能。如果host!=本地主机。
testtcpfunction.java文件:

import java.io.*;
import java.net.*;

public class TestTcpFunction {
    public static void main(String[] args) {
        String sendMessage = "Blah blah";

        String host = (args.length==0)
                    ? "localhost"
                    : "tcpbin.com";

        String result = tcp(host, 4242, 30000, 30000, sendMessage);
        System.out.println("result = " + result);
        System.out.println("matches = " + result.equals(sendMessage));
    }

    public static String tcp(String hostName, Number port, Number connectionTimeOutMs, Number readTimeOutMs, String message) {
        String errmsg = "";
        try (
            Socket socket = new Socket();
        ) {
            Inet4Address address = (Inet4Address) Inet4Address.getByName(hostName);
            System.out.println("trying to connect to:" + address.getHostAddress());
            socket.connect(new InetSocketAddress(address, port.intValue()), connectionTimeOutMs.intValue()); // <<< times out if not localhost
            socket.setSoTimeout(readTimeOutMs.intValue());

            try (
                PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // autoflush
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            ) {
                out.print(message);
                StringBuilder sb = new StringBuilder();
                String line;
                boolean addNewline = false;

                while ((line = in.readLine()) != null) {
                    if (addNewline)
                        sb.append('\n');
                    sb.append(line);
                    if (line.lastIndexOf("</response>") >= 0)
                        break;

                    addNewline = true;
                }

                return sb.toString(); // The xml may not be well formed, for instance missing </response>
            } finally {}

        } catch (UnknownHostException e) {
            errmsg = "Unknown host " + hostName;
        } catch (SocketTimeoutException e) {
            errmsg = "Socket connection timeout (before connection got established)";
        } catch (SocketException e) {
            errmsg = "Socket error: " + e.getMessage();
        } catch (IOException e) {
            errmsg = "Couldn't get I/O for the connection to " + hostName;
        } catch (Exception e) {
            errmsg = "Unknown socket error " + e.getMessage();
        }

        System.err.println(errmsg);
        return "<Error> function tcp (Utils.java): " + errmsg + "</Error>";
    }
}

===
用javac编译两者。然后用启动服务器 java TcpServer . 下次跑步 java TestTcpFunction 在不同的shell中,没有参数。
第一次(与本地主机)它应该正常工作。然后再次运行,但使用任何参数,如 java TestTcpFunction 1 这次我尝试连接时超时。
代码已经在我的机器上构建和测试。

wbgh16ku

wbgh16ku1#

客户端在connect中未超时。一个简单的输出 connect 显示连接实际上已成功:

socket.connect(new InetSocketAddress(address, port.intValue()), connectionTimeOutMs.intValue()); // <<< times out if not localhost
        System.out.println("connected successfully");

相反,程序在从服务器读取时挂起。对于当前代码,它将等待服务器关闭连接或发送一条带有 </response> . 但是服务器tcpbin。com:4242 will 不要做这种事。它只会读取任何内容并将其回显。得到一个 </response> 字符串一实际上必须发送这个字符串-这还没有完成。
因此,根据设置的超时,读取将在一段时间后超时 socket.setSoTimeout . 结果 SocketTimeoutException 错误地解释为连接超时,但它是读取超时。
假设代码期望回显消息包含字符串 </response> 必须将其添加到已发送的消息中:

String sendMessage = "Blah blah</response>";

但这仍然不够,tcpdump显示消息甚至没有被发送。这是因为期望 out.print(message); 受autoflush影响是完全错误的-请看我创建了一个带有autoflush的printwriter;为什么不是自动冲洗?。因此,必须明确地刷新写入程序:

out.print(message);
            out.flush();

tcpdump显示消息现在实际上已发送,但没有任何回显。这是因为echo服务器实际上希望读取行,但尚未发送行结束。添加它实际上有助于发送消息、获得回显消息并打破循环:

String sendMessage = "Blah blah</response>\n";

为什么要用localhost呢?因为示例服务器的行为实际上与tcpbin.com上的echo服务器不同。它没有读取任何内容,只是发送了一条固定消息并关闭了连接。

相关问题