套接字connect()方法为什么超时?netcat(nc)运行良好



我有一个较大的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());

当我以"45.79.112.203""tcpbin.com"的形式提供IP地址时,代码会给出SocketTimeoutException

在后一种情况下,线

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

提供了正确的IP地址,因此主机名被正确解析;它与CCD_ 4返回的内容相匹配。

我希望能够使用IPv4地址(字符串格式(或主机名来调用函数。

我做错了什么为什么即使超时时间高达60000毫秒,套接字也无法建立连接?

注:

CCD_ 5是一个";回声;服务器来测试套接字连接。它只是一个例子,不应该是问题的原因。

尝试以下操作:

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

你应该取回刚刚发送的字符串。

在tcp((函数中,我以Number对象的形式传入数字,因为Java代码是通过Java inter-op和JavaScript从Karate测试框架调用的。JavaScript的类型为Number,但没有intdouble

===

更新:

这里是一个简单的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()函数。在case host!=中超时的是connect((语句localhost。

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启动服务器。接下来,在不带参数的其他shell中运行java TestTcpFunction

第一次(使用本地主机(它应该能正常工作。然后再次运行,但使用任何参数,如java TestTcpFunction 1这次我在尝试连接时超时了。

代码已经在我的机器上构建并测试过了。

客户端在连接时不会超时。connect之后的一个简单输出表明连接实际上是成功的:

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

相反,程序在从服务器读取时挂起。对于当前代码,它将等待,直到服务器关闭连接或发送了一行</response>。但是服务器tcpbin.com:4242不会做这样的事情。它将简单地读取任何内容并将其回显。要获得一个</response>字符串,实际上必须发送这个字符串——这还没有完成。

因此,基于socket.setSoTimeout设置的超时,读取将在一段时间后超时。生成的SocketTimeoutException被错误地解释为连接超时,但它是一个读取超时。

假设代码期望返回的消息包含字符串</response>,则必须将其添加到发送的消息中:

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

然而,这仍然不够,tcpdump显示消息甚至没有被发送。这是因为out.print(message);受到自动冲洗影响的预期是错误的——请参阅我创建了一个启用自动冲洗的PrintWriter;为什么它不自动冲洗?。因此,必须明确刷新写入程序:

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

tcpdump显示消息现在实际上已经发送,但没有任何响应。这是因为echo服务器实际上希望读取行,但尚未发送行末尾。添加它实际上有助于发送消息,获得回复消息并脱离循环:

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

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

最新更新