我正在使用Java NIO,这是我第一次建立一个有效的TCP连接(到目前为止我只完成了UDP,很久以前就是一个简单的TCP测试程序)。
现在,我不确定我可以可靠地开始向客户端发送数据的确切时刻,以便他们知道另一端有活动连接(假设某些事情没有出错)。
假设一切都是非阻塞的。
客户端:
1)打开一个没有绑定的新套接字通道,以便将其设置为async
s = SocketChannel.open()
2)将其设置为非阻塞
s.configureBlocking(false)
3)尝试连接到正在侦听的服务器
s.connect(someAddr)
如果返回true,则javadocs表示已建立连接。这是否意味着我不需要调用finishConnect()?从我读到的,这是本地连接,但它没有指定远程连接是否可能立即返回true。
这是客户端向服务器发送SYN的地方吗?
SERVER:
4)服务器通过一些serverSocketChannel.accept()
获取传入连接,我们假设它是非阻塞的,并假装在这种情况下它返回一个有效的套接字对象(而不是null)。
客户端:
5)客户端现在是否呼叫finishConnect()
?
客户端何时知道何时继续调用finishConnect()?从步骤(3)调用s.connect(...)
后,我是否只是立即循环X秒?
这是发送ACK的时候吗?我应该循环X秒,直到它返回true ...如果由于出现问题而在X秒内没有响应,则终止“半成形连接”?
s.isConnected()
对于步骤(3)中的connect()成功返回true,还是finishConnect()成功?
我不确定我是否正确地执行此操作,而且我也不确定服务器发送或客户端发送的安全性是什么...是(4)服务器,(5)客户端?
我是否需要让客户端在连接到服务器后发送心跳包,以便我的应用程序知道可以开始发送数据?我不知道服务器如何知道它是完全连接的,因为我看不出任何方式服务器会知道最后的确认何时完成...除了客户端知道连接已建立它会发送某种“第一个数据包”数据。
服务器知道的唯一方法是,我是否能够以某种方式弄清楚何时获得ACK数据包,但我看不到当前让我知道的Java方法。
注意:我可能缺少知识,我可能会说一些不正确的东西,如果你能指出他们错在哪里我会非常乐意更新我的帖子所以它实际上是正确的
指导我的知识/创建此帖子的链接:
答案 0 :(得分:2)
警告:我使用套接字完成了套接字编程,而不是socketchannels。这个答案基于读取SocketChannel javadoc,知道socketchannels在幕后使用套接字并熟悉套接字。
根据javadoc,如果connect()返回true,则建立连接,并且您不需要调用finishConnect()。可以将finishConnect()视为检查连接建立状态的一种方式,而不是作为执行建立连接所需的任何操作的函数。
在服务器上,如果accept()返回SocketChannel,则表明已从客户端收到连接尝试。虽然Java文档故意没有指定,但是很可能已经发送了来自服务器的SYN ACK,以便允许将来进行优化。 Java很可能认为连接是“已建立的”,尽管您可以调用finishConnect()来确保;在服务器认为连接完全建立之前,Java可能会等待服务器获取客户端的ACK。请注意,即使ServerSocketChannel处于非阻塞模式,Javadoc也指定返回的SocketChannel最初将处于阻塞模式。
在客户端,只要connect()或finishConnect()返回true,就可以开始发送数据。在服务器端,当finishConnect()返回true时,您当然可以开始发送数据;可能是对finishConnect()的调用是多余的,而accept()返回一个已建立连接的SocketChannel - 这就是我写它的方式 - 但我还没有使用SocketChannel足以确定。您不需要在连接建立期间发送心跳包,这样做可能会浪费精力,因为TCP协议本身会处理所有与连接相关的内容,包括三次握手(SYN,SYN-ACK,ACK)建立联系。
答案 1 :(得分:2)
我们需要绑定吗?或者仅仅是因为我们想要引用本地端口?
您不需要绑定客户端SocketChannel。
s.connect(someAddr)
如果返回true,则javadocs表示已建立连接。这是否意味着我不需要调用finishConnect()?
正确。
根据我的阅读,这是针对本地连接的,但它没有指定远程连接是否可能立即返回true。
任何时候它都可以返回true,你必须检查。
这是客户端向服务器发送SYN的地方吗?
是
服务器通过一些serverSocketChannel.accept()获取传入连接,我们假设它是非阻塞的,并假装在这种情况下它返回一个有效的套接字对象(而不是null)。这是否意味着一旦服务器获得连接,它就接受了连接(假设一切顺利)并发送回SYN-ACK?
没有。 SYN-ACK已由TCP / IP堆栈发送。它并不取决于应用程序代码何时调用accept()。
客户端现在是否调用了finishConnect()?
同样,这并不取决于服务器应用程序何时调用accept。 TCP握手由服务器内核完成。如果connect()没有返回true,则客户端应调用finishConnnect(),后续的select()表示该通道现在可以连接。
注意,finishConnect()可以返回true,在这种情况下你只是继续,或者假,在这种情况下你只是等待OP_CONNECT,或者抛出异常,在这种情况下它失败了,你应该关闭通道。 / p>
客户什么时候知道何时继续调用finishConnect()?从步骤(3)调用s.connect(...)后,我是否只是立即循环X秒?
见上文。
这是发送ACK的时候吗?
没有。这一切都是由内核asynchronosusly
完成的我应该循环X秒直到它返回true ...并且杀掉半成形连接'如果由于出现问题而在X秒内没有响应?
不,见上文。
对于步骤(3)中的connect()成功,或者connectConnect()成功,s.isConnected()是否返回true?
两者:他们互相排斥。
我不确定我是否正确地执行此操作,而且我也不确定服务器发送或客户端发送的安全性是什么时候...它在(4)表示服务器,(5)表示客户端?
服务器可以在拥有已接受的套接字后立即发送。只要connect()或finishConnect()返回true,客户端就可以发送。
我是否需要让客户端在与服务器建立连接后发送心跳包,以便我的应用程序知道可以开始发送数据?
没有
我不知道服务器如何知道它是完全连接的,因为我无法看到服务器在完成最终确认时知道的任何方式......除了客户端知道建立连接并发送某种第一个数据包'数据
见上文。发送数据包以查看是否可以发送数据包的想法是没有意义的。
服务器知道的唯一方法是我能否以某种方式弄清楚何时获得ACK数据包
当accept()返回时,服务器已经有ACK数据包。
注意:我可能缺少知识......
您忽视的是服务器上存在侦听backlog队列。
答案 2 :(得分:0)
您需要使用Selector来等待CONNECT和ACCEPT事件。
NIO并不那么难。但是Java的NIO API比必要的要困难一些。玩得开心折磨自己:)
Java API调用和TCP握手步骤之间的确切映射不是很清楚。但是,我会在下面的模型中考虑它(如果不是现实中发生的事情并不重要)
connect()
-- SYN -->
<-- ACK --
<-- SYN --
CONNECT event
finishConnect()
-- ACK -->
ACCEPT event
accept()
序列:
如果connect(serverAddress)
返回true,则可以毫不费力地使用套接字通道。但是,我在实践中从未观察到这种情况,甚至在本地环回连接上也没有。
服务器接收客户端SYN;用ACK / SYN响应
客户端收到服务器ACK / SYN。
将CONNECT事件引发至客户端应用程序。
cilent app然后调用connect()
- 客户端向服务器发送ACK
由于之前的CONNECT事件,finishConnect()
不会返回false。它可能会抛出异常。
现在,客户端认为TCP握手已完成。客户端套接字通道可以立即使用。
将ACCEPT事件引发至服务器应用程序。
服务器应用然后调用finishConnect()
。返回的套接字通道可以立即使用。