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

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

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

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

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

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

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

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

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

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

  1. import java.io.*;
  2. import java.net.ServerSocket;
  3. import java.net.Socket;
  4. public class TcpServer {
  5. public static void main(String[] args) {
  6. System.out.println("Listening on port 4242");
  7. ServerSocket listener = null;
  8. try {
  9. do {
  10. listener = new ServerSocket(4242);
  11. Socket other = listener.accept();
  12. System.out.println(">>> got a new connection from "
  13. + other.getInetAddress().toString() + " <<<");
  14. other.getOutputStream().write("Blah blah".getBytes());
  15. other.close();
  16. listener.close();
  17. } while (true);
  18. } catch (IOException e) {
  19. e.printStackTrace();
  20. }
  21. }
  22. }

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

  1. import java.io.*;
  2. import java.net.*;
  3. public class TestTcpFunction {
  4. public static void main(String[] args) {
  5. String sendMessage = "Blah blah";
  6. String host = (args.length==0)
  7. ? "localhost"
  8. : "tcpbin.com";
  9. String result = tcp(host, 4242, 30000, 30000, sendMessage);
  10. System.out.println("result = " + result);
  11. System.out.println("matches = " + result.equals(sendMessage));
  12. }
  13. public static String tcp(String hostName, Number port, Number connectionTimeOutMs, Number readTimeOutMs, String message) {
  14. String errmsg = "";
  15. try (
  16. Socket socket = new Socket();
  17. ) {
  18. Inet4Address address = (Inet4Address) Inet4Address.getByName(hostName);
  19. System.out.println("trying to connect to:" + address.getHostAddress());
  20. socket.connect(new InetSocketAddress(address, port.intValue()), connectionTimeOutMs.intValue()); // <<< times out if not localhost
  21. socket.setSoTimeout(readTimeOutMs.intValue());
  22. try (
  23. PrintWriter out = new PrintWriter(socket.getOutputStream(), true); // autoflush
  24. BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
  25. ) {
  26. out.print(message);
  27. StringBuilder sb = new StringBuilder();
  28. String line;
  29. boolean addNewline = false;
  30. while ((line = in.readLine()) != null) {
  31. if (addNewline)
  32. sb.append('\n');
  33. sb.append(line);
  34. if (line.lastIndexOf("</response>") >= 0)
  35. break;
  36. addNewline = true;
  37. }
  38. return sb.toString(); // The xml may not be well formed, for instance missing </response>
  39. } finally {}
  40. } catch (UnknownHostException e) {
  41. errmsg = "Unknown host " + hostName;
  42. } catch (SocketTimeoutException e) {
  43. errmsg = "Socket connection timeout (before connection got established)";
  44. } catch (SocketException e) {
  45. errmsg = "Socket error: " + e.getMessage();
  46. } catch (IOException e) {
  47. errmsg = "Couldn't get I/O for the connection to " + hostName;
  48. } catch (Exception e) {
  49. errmsg = "Unknown socket error " + e.getMessage();
  50. }
  51. System.err.println(errmsg);
  52. return "<Error> function tcp (Utils.java): " + errmsg + "</Error>";
  53. }
  54. }

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

wbgh16ku

wbgh16ku1#

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

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

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

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

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

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

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

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

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

展开查看全部

相关问题