在服务器或客户端上的Java NIO套接字期间,套接字连接准备好发送数据



我正在使用Java NIO,这是我第一次建立工作的TCP连接(到目前为止,我只做过UDP,很久以前还做过一个简单的TCP测试程序(。

现在,我不确定我能在什么时候可靠地开始向客户端发送数据,以便他们知道另一端有一个活动连接(假设没有出现问题(。


假设一切都是非阻塞的。

客户端:

1( 打开一个没有绑定的新套接字通道,以便将其设置为异步

s = SocketChannel.open()

2( 将其设置为非阻塞

s.configureBlocking(false)
  • 我们需要在这里绑定吗?或者这只是为了我们是否想引用本地端口

3( 尝试连接到正在侦听的服务器

s.connect(someAddr)
  • 如果返回true,则javadocs表示连接已建立。这是否意味着我不需要调用finishConnect((?据我所知,这是针对本地连接的,但它没有指定远程连接是否可能立即返回true。

  • 这是客户端向服务器发送SYN的地方吗?


服务器:

4( 服务器通过一些serverSocketChannel.accept()获得传入连接,我们认为它是非阻塞的,并且在这种情况下会假装它返回一个有效的套接字对象(而不是null(。

  • 这是否意味着一旦服务器获得连接,它就接受连接(假设一切顺利(并发送回SYN-ACK

客户端:

5( 客户端现在是否调用finishConnect()

  • 客户端何时知道何时继续调用finishConnect((?从步骤(3(调用s.connect(...)后,我是否立即循环X秒?

  • 这是它发送ACK的时候吗?我是不是应该循环X秒直到它返回真。。。如果由于出现问题而在X秒内没有响应,则终止"半成型连接"?

  • 对于步骤(3(中的connect((,s.isConnected()是否返回true,或者finishConnect((是否返回true?


我不确定我是否做得正确,也不确定服务器或客户端在什么时候发送是安全的。。。服务器为(4(,客户端为(5(?

在连接到服务器后,我是否需要让客户端发送心跳数据包,以便我的应用程序知道可以开始发送数据?我不知道服务器怎么会知道它已经完全连接,因为我看不出服务器会以任何方式知道最终确认何时完成。。。除了客户端知道连接已经建立并且它发送某种"第一个数据包"数据之外。

服务器知道的唯一方法是,我是否能以某种方式弄清楚它何时收到ACK数据包,但我目前看不到Java让我知道的方法。

注意:我可能缺少知识,我可能说了一些不正确的话,如果你能指出他们错在哪里,我会非常高兴更新我的帖子,这样它就事实上是正确的。

链接,引导我的知识/创建这篇文章:

TCP协议

SocketChannel Javadocs

我们需要在这里绑定吗?或者这只是为了我们是否想引用本地端口?

您不需要绑定客户端SocketChannel

s.connect(someAddr)

如果返回true,则javadocs表示连接已建立。这是否意味着我不需要给finishConnect()打电话?

正确。

根据我所读到的,这是用于本地连接

你从哪里读到的?这不是事实。

但它没有指定远程连接是否可能立即返回true。

它可以随时返回true。你必须检查一下。它与本地或远程无关。

这是客户端向服务器发送SYN的地方吗?

是的。

服务器通过一些serverSocketChannel.accept()获得传入连接,我们认为它是非阻塞的,并且在这种情况下会假装它返回一个有效的套接字对象(而不是null(。这是否意味着一旦服务器获得连接,它就接受连接(假设一切顺利(并发送回SYN-ACK?

没有。TCP/IP堆栈已经发送了SYN-ACK。这并不取决于应用程序代码何时调用accept()

客户端现在是否调用finishConnect()

同样,这并不取决于服务器应用程序何时调用accept()。TCP握手由服务器内核完成。如果connect()没有返回true,并且随后的select()指示信道现在是可连接的,则客户端应该调用finishConnnect()

请注意,finishConnect()可以返回true,在这种情况下您只需继续,也可以返回false,在这种情形下您只需要继续等待OP_CONNECT:或者它可以引发异常,在这种状况下它失败了,您必须关闭通道。

客户端什么时候知道何时继续呼叫finishConnect()?从步骤(3(调用s.connect(...)后,我是否立即循环X秒?

请参见上文。但是,如果你准备只循环,为什么要在非阻塞模式下连接呢?在阻塞模式下执行,成功后进入非阻塞模式。简单多了。

这是它发送ACK的时候吗?

没有。这一切都是由内核异步完成的

我是不是应该循环X秒直到它返回真。。。如果由于出现问题而在X秒内没有响应,则终止"半成型连接"?

否,请参阅上文。

对于后续步骤(3(中的connect()或后续finishConnect()s.isConnected()是否返回true?

两者都是相互排斥的。

我不确定我是否做得正确,也不确定服务器或客户端在什么时候发送是安全的。。。服务器为(4(,客户端为(5(?

只要服务器有一个可接受的套接字,它就可以发送。客户端可以在connect()finishConnect(中的任何一个(返回true时立即发送。

在连接到服务器后,我是否需要让客户端发送心跳数据包,以便我的应用程序知道可以开始发送数据?

没有。

我不知道服务器如何知道它已经完全连接,因为我看不出服务器如何知道最终确认何时完成。。。除了客户端知道连接已经建立并且它发送某种"第一个数据包"数据之外。

请参见上文。发送一个数据包以查看是否可以发送数据包的想法是没有意义的。

服务器知道的唯一方法是,如果我能以某种方式弄清楚它什么时候得到ACK包

accept()返回时,服务器已经具有ACK数据包。

注意:我可能缺少知识。。。

您忽略的是服务器上是否存在侦听囤积队列。

注意:我已经使用套接字而不是套接字通道进行了套接字编程。这个答案是基于阅读SocketChannel javadoc,知道SocketChannel在幕后使用套接字,并且熟悉套接字。

根据javadoc,如果connect((返回true,则连接已建立,不需要调用finishConnect((。将finishConnect((看作是一种检查连接建立状态的方法,而不是一个执行建立连接所需的任何操作的函数。

在服务器上,如果accept((返回SocketChannel,则表示已从客户端接收到连接尝试。来自服务器的SYN ACK很可能已经发送,尽管Java文档故意没有指定这一点,以便将来进行优化。Java很可能认为连接已经"建立",不过您可以调用finishConnect((来确保;Java可能会等到服务器收到客户端的ACK后才认为连接已完全建立。请注意,Javadoc指定返回的SocketChannel最初将处于阻塞模式,即使ServerSocketChannel处于非阻塞模式。

在客户端,只要connect((或finishConnect((返回true,就可以开始发送数据。在服务器端,当finishConnect((返回true时,您当然可以开始发送数据;对finishConnect((的调用可能是多余的,accept((返回了一个具有已建立连接的SocketChannel——我本来是这么写的——但我还没有充分使用SocketChannel来确定。在连接建立期间,您不需要发送心跳数据包,这样做可能会浪费精力,因为TCP协议本身处理所有与连接相关的内容,包括建立连接的三方握手(SYN、SYN-ACK、ACK(。

您需要使用选择器来等待CONNECT和ACCEPT事件。

NIO并没有那么难。但是Java的NIO API比必要的更难。享受折磨自己的乐趣:(


Java API调用和TCP握手步骤之间的确切映射不是很清楚。然而,我会在下面的模型中考虑它(如果它不完全是现实中发生的事情,那也没什么关系(

 connect() 
                     -- SYN -->
                    <-- ACK --
                    <-- SYN --  
 CONNECT event
 finishConnect()        
                     -- ACK -->
                                   ACCEPT event
                                   accept()

序列:

  • 客户端应用程序调用connect(serverAddress)-客户端向服务器发送SYN

如果connect()返回true,则套接字通道可以立即使用。然而,我从未在实践中观察到这种情况,甚至在本地环回连接中也没有。

  • 服务器接收客户端SYN;以ACK/SYN 响应

  • 客户端接收服务器ACK/SYN。

  • 向客户端应用程序引发CONNECT事件。

  • cilent应用程序随后调用finishConnect()-客户端向服务器发送ACK

这里,由于之前的CONNECT事件,finishConnect()不会返回false。不过,它可能会抛出异常。

现在,客户端认为TCP握手已完成。客户端套接字通道可以立即使用。

  • 服务器接收客户端ACK。服务器认为TCP握手已完成。连接被放入backlog中
  • 向服务器应用程序引发ACCEPT事件。

  • 然后服务器应用程序调用accept()。返回的套接字通道可以立即使用。

最新更新