【问题标题】:How to make sure if a TCP socket is closed on the other end? ( how to read ack signal)如何确定 TCP 套接字是否在另一端关闭? (如何读取 ack 信号)
【发布时间】:2016-01-19 09:13:41
【问题描述】:

考虑下面的 java 伪代码(客户端使用 IP 和端口 23(telnet)打开一个到服务器的套接字:

  static Socket skt = null;   
  skt = new Socket(IP, 23);
  do something
  skt.close()
  ALMOST NO DELAY
  skt = new Socket(IP, 23); // re-opening same socket variable (skt) right after closing it

.. 有时会发生下面的异常,因为我们正在尝试重新打开尚未完全关闭的同一个套接字 错误:java.io.IOException:连接重置

如何确保(检查)另一端的套接字已关闭?换句话说,在我们继续并在另一部分代码中重新打开该套接字之前,等待来自服务器的确认。 - 出于某种原因,我们必须重新打开同一个套接字 - 我们对在重新打开同一个套接字之前放置延迟不感兴趣。

简而言之:我们想从服务器读取确认(蓝线)

【问题讨论】:

  • 要添加到@Erik 的答案,当您执行new Socket(IP, 23) 时,您每次都使用不同的本地端口打开不同的连接。您没有重复使用相同的连接。您将不得不发布更多 Wireshark 跟踪信息,以显示在您包含数据包之后会发生什么。
  • 再看一遍,我看到客户端正在向服务器发送 RST。你得到的错误是出现在服务器还是客户端?
  • 客户端出现错误(TCP连接的发起者)
  • @Erik,我更正了我输入错误的代码(我在正确的位置添加了保留字 static)。
  • 您是否在短时间内创建了大量与服务器的连接?例如。 1 或 2 分钟内连接数千个?

标签: java sockets telnet


【解决方案1】:

套接字由两端的IP和端口标识,源和目标。您的第二个连接应该获得不同的源端口,因此服务器将其视为单独的连接。确保服务器始终接受新连接,您应该没问题。

【讨论】:

  • 谢谢,但我们已经考虑过了,就像我提到的那样,这是我们唯一的选择
  • 好吧,如果你必须像这样监控 TCP 内部,那你就做错了。
【解决方案2】:

每次创建后打印客户端套接字的本地端口。如果您为每个新的客户端套接字获取一个新的本地端口,那么“套接字重用”确实不太可能成为问题,除非 TCP 堆栈中存在错误。顺便说一句,您是否通过例如明确指定本地套接字号? Socket.bind 还是 the Socket constructor which accepts a local port?你可能不应该。只需让系统选择一个临时端口即可。


问题可能出在服务器上,而不是在客户端上。当我关闭然后立即重新创建到同一主机和端口的客户端套接字时,我没有任何问题。在我退出之前,SSCCE 毫无问题地达到了 130k 连接。环回接口可能比真实的 NIC 更宽容,但您可以轻松修改此代码以在真实网络上运行。

public class TestTcpClientServer
{
    public static final int SERVER_PORT = 10111;

    public static void main(String[] args) throws Exception
    {
        Server server = new Server();
        server.start();

        Thread.sleep(500);

        Client client = new Client();
        client.start();

        System.out.println("Press enter to shutdown...");

        System.in.read();

        client.shutdown();
        client.join();
        server.shutdown();
        server.join();

        System.out.println("Done.");
    }

    public static class Client extends Thread
    {
        private volatile boolean shutdown = false;

        public void run()
        {
            try
            {
                int totalClientConnections = 0;

                while (!shutdown)
                {
                    Socket socket = new Socket("localhost", SERVER_PORT);

                    InputStream inputStream = socket.getInputStream();
                    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));

                    OutputStream outputStream = socket.getOutputStream();
                    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));

                    bw.write("Client saying hello to server");
                    bw.newLine();
                    bw.flush();

                    String readLine = br.readLine();
                    //System.out.println("Client read from server: " + readLine);

                    System.out.println("Socket local port: " + socket.getLocalPort());

                    socket.close();

                    totalClientConnections++;
                    if (totalClientConnections % 10000 == 0)
                    {
                        System.out.println("totalClientConnections=" + totalClientConnections);
                    }

                    Thread.sleep(1000);
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }

        public void shutdown()
        {
            shutdown = true;
        }
    }

    public static class Server extends Thread
    {
        private volatile boolean shutdown = false;
        private ServerSocket serverSocket = null;

        public void run()
        {
            try
            {
                int totalServerConnections = 0;

                serverSocket = new ServerSocket(SERVER_PORT);

                while (!shutdown)
                {
                    Socket socket = serverSocket.accept();

                    InputStream inputStream = socket.getInputStream();
                    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));

                    OutputStream outputStream = socket.getOutputStream();
                    BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));

                    String readLine = br.readLine();
                    //System.out.println("Server read from client: " + readLine);

                    bw.write("Server saying hello to client");
                    bw.newLine();
                    bw.flush();

                    socket.close();

                    totalServerConnections++;
                    if (totalServerConnections % 10000 == 0)
                    {
                        System.out.println("totalServerConnections=" + totalServerConnections);
                    }
                }
            }
            catch (Exception e)
            {
                if (!shutdown)
                {
                    e.printStackTrace();
                }
            }
        }

        public void shutdown()
        {
            shutdown = true;
            try
            {
                serverSocket.close();
            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
    }
}

【讨论】:

    【解决方案3】:

    .. 有时会发生以下异常,因为我们正在尝试 重新打开尚未完全关闭的同一个套接字错误: java.io.IOException:连接重置

    您的分析不正确。这不是原因。插座是全新的。连接是全新的。如果您收到“连接重置”,那是因为服务器软件选择了重置连接。除了调查原因之外,您对此无能为力,最坏的情况是休眠并重试。

    正如其他一些答案所暗示的那样,这也与用完本地端口无关。如果这是问题所在,您将收到 BindException

    不过,您的客户发送 RST 的事实非常令人好奇。你在调用一些上面没有显示的 Socket API 吗?

    【讨论】:

      猜你喜欢
      • 2014-04-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-04-08
      • 1970-01-01
      • 1970-01-01
      • 2019-02-06
      相关资源
      最近更新 更多