Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么在不尝试I/O的情况下,不可能检测到TCP套接字被对等方正常关闭?_Java_Sockets_Tcp_Network Programming - Fatal编程技术网

Java 为什么在不尝试I/O的情况下,不可能检测到TCP套接字被对等方正常关闭?

Java 为什么在不尝试I/O的情况下,不可能检测到TCP套接字被对等方正常关闭?,java,sockets,tcp,network-programming,Java,Sockets,Tcp,Network Programming,作为对a的后续,我想知道为什么在Java中,如果不尝试在TCP套接字上读/写,就不可能检测到该套接字已被对等方优雅地关闭?无论是使用NIO之前的Socket还是NIOSocketChannel,情况似乎都是如此 当对等方优雅地关闭TCP连接时,连接两侧的TCP堆栈都知道这一事实。服务器端(启动关机的那一方)最终处于状态FIN_WAIT2,而客户端(没有明确响应关机的那一方)最终处于状态CLOSE\u WAIT。为什么在Socket或SocketChannel中没有可以查询TCP堆栈以查看底层TC

作为对a的后续,我想知道为什么在Java中,如果不尝试在TCP套接字上读/写,就不可能检测到该套接字已被对等方优雅地关闭?无论是使用NIO之前的
Socket
还是NIO
SocketChannel
,情况似乎都是如此

当对等方优雅地关闭TCP连接时,连接两侧的TCP堆栈都知道这一事实。服务器端(启动关机的那一方)最终处于状态
FIN_WAIT2
,而客户端(没有明确响应关机的那一方)最终处于状态
CLOSE\u WAIT
。为什么在
Socket
SocketChannel
中没有可以查询TCP堆栈以查看底层TCP连接是否已终止的方法?TCP堆栈是否不提供此类状态信息?还是避免对内核进行代价高昂的调用是一种设计决策

在已经发布了这个问题的一些答案的用户的帮助下,我想我看到了问题的来源。未明确关闭连接的一方最终处于TCP状态
close\u WAIT
,这意味着连接正在关闭过程中,并等待该方发出自己的
close
操作。我认为,
isConnected
返回
true
isClosed
返回
false
是很公平的,但是为什么没有类似
isClosed
的东西呢

下面是使用NIO前套接字的测试类。但使用NIO获得了相同的结果

import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
  public static void main(String[] args) throws Exception {
    final ServerSocket ss = new ServerSocket(12345);
    final Socket cs = ss.accept();
    System.out.println("Accepted connection");
    Thread.sleep(5000);
    cs.close();
    System.out.println("Closed connection");
    ss.close();
    Thread.sleep(100000);
  }
}


import java.net.Socket;

public class MyClient {
  public static void main(String[] args) throws Exception {
    final Socket s = new Socket("localhost", 12345);
    for (int i = 0; i < 10; i++) {
      System.out.println("connected: " + s.isConnected() + 
        ", closed: " + s.isClosed());
      Thread.sleep(1000);
    }
    Thread.sleep(100000);
  }
}

底层套接字API没有这样的通知


发送TCP堆栈直到最后一个数据包才会发送FIN位,因此在发送数据之前,当发送应用程序逻辑上关闭其套接字时,可能会有大量数据被缓冲。同样,由于网络比接收应用程序快,所以缓冲的数据(我不知道,可能是通过较慢的连接进行中继)可能对接收方很重要,并且您不希望接收应用程序仅仅因为堆栈已接收到FIN位就放弃它。

底层套接字API没有此类通知


发送TCP堆栈直到最后一个数据包才会发送FIN位,因此在发送数据之前,当发送应用程序逻辑上关闭其套接字时,可能会有大量数据被缓冲。同样,由于网络比接收应用程序快(我不知道,可能是通过较慢的连接进行中继)而被缓冲的数据可能对接收器很重要,并且您不会希望接收应用程序仅仅因为堆栈接收到FIN位就丢弃它。

这是一个有趣的话题。我刚才已经翻遍了java代码以进行检查。根据我的发现,有两个截然不同的问题:第一个是TCP RFC本身,它允许远程关闭的套接字以半双工方式传输数据,因此远程关闭的套接字仍然是半开的。根据RFC,RST不关闭连接,您需要发送一个显式中止命令;所以Java允许通过半封闭的套接字发送数据

(有两种方法可以读取两个端点处的关闭状态。)


另一个问题是,实现说这种行为是可选的。当Java努力实现可移植性时,它们实现了最好的通用特性。我想维护(操作系统,半双工的实现)的映射可能是个问题。

这是一个有趣的话题。我刚才已经翻遍了java代码以进行检查。根据我的发现,有两个截然不同的问题:第一个是TCP RFC本身,它允许远程关闭的套接字以半双工方式传输数据,因此远程关闭的套接字仍然是半开的。根据RFC,RST不关闭连接,您需要发送一个显式中止命令;所以Java允许通过半封闭的套接字发送数据

(有两种方法可以读取两个端点处的关闭状态。)


另一个问题是,实现说这种行为是可选的。当Java努力实现可移植性时,它们实现了最好的通用特性。我想维护(操作系统,半双工的实现)的映射可能是个问题。

我认为这更像是一个套接字编程问题。Java只是遵循套接字编程的传统

发件人:

TCP提供可靠的、有序的 从一个服务器传送字节流 将一台计算机上的程序连接到另一台计算机上 另一台计算机上的程序

握手完成后,TCP不会对两个端点(客户端和服务器)进行任何区分。术语“客户机”和“服务器”主要是为了方便。因此,“服务器”可以发送数据,“客户端”可以同时向彼此发送一些其他数据

“关闭”一词也具有误导性。只有FIN声明,意思是“我不会再给你寄东西了。”但这并不意味着航班上没有包裹,或者对方没有更多的话要说。如果您将snail mail实现为数据链路层,或者如果您的数据包经过不同的路径,那么接收方可能会以错误的顺序接收数据包。TCP知道如何为您解决此问题


此外,作为一个程序,您可能没有时间继续检查缓冲区中的内容。因此,在您方便的时候,您可以检查缓冲区中的内容。总而言之,当前的套接字实现并没有那么糟糕。如果确实存在isPeerClosed(),那么每次调用read时都必须进行额外的调用。

我认为这更像是一个套接字编程问题。Java只是遵循套接字编程的传统

发件人:

TCP提供可靠的、有序的 b流的交付
connected: true, closed: false
connected: true, closed: false
...
struct timeval tp;  
fd_set in;  
fd_set out;  
fd_set err;  

FD_ZERO (in);  
FD_ZERO (out);  
FD_ZERO (err);  

FD_SET(socket_handle, err);  

tp.tv_sec = 0; /* or however long you want to wait */  
tp.tv_usec = 0;  
select(socket_handle + 1, in, out, err, &tp);  

if (FD_ISSET(socket_handle, err) {  
   /* handle closed socket */  
}  
public void synchronizedClose(Socket sok) {
    InputStream is = sok.getInputStream();
    sok.shutdownOutput(); // Sends the 'FIN' on the network
    while (is.read() > 0) ; // "read()" returns '-1' when the 'FIN' is reached
    sok.close(); // or is.close(); Now we can close the Socket
}