简体   繁体   English

在服务器端或客户端的 Java NIO Socket 期间,socket 连接准备好发送数据的时间是什么时候?

[英]At what point during Java NIO Socket on the server or client end is the socket connection ready to send data?

I'm working with Java NIO and this is my first time setting up a working TCP connection (I've only done UDP up until this point and a simple TCP test program a long time ago).我正在使用 Java NIO,这是我第一次设置有效的 TCP 连接(到目前为止,我只做过 UDP,很久以前还做过一个简单的 TCP 测试程序)。

Right now, I'm unsure the exact moment where I can reliably start sending data to the clients such that they know there is an active connection on the other end ( assuming something hasn't gone wrong ).现在,我不确定我可以可靠地开始向客户端发送数据的确切时刻,以便他们知道另一端有活动连接(假设没有出现问题)。


Assume everything is non-blocking.假设一切都是非阻塞的。

CLIENT:客户:

1) Open a new socket channel with no bindings so it can be set to async 1)打开一个没有绑定的新套接字通道,以便将其设置为异步

s = SocketChannel.open()

2) Set it to be non-blocking 2)设置为非阻塞

s.configureBlocking(false)
  • Do we need to bind here?这里需要绑定吗? Or is that just for if we want to refer to a local port?还是只是为了引用本地端口?

3) Attempt to connect to a server that is listening 3) 尝试连接到正在侦听的服务器

s.connect(someAddr)
  • If this returns true, the javadocs say the connection is established.如果返回 true,则 javadoc 表示连接已建立。 Does that mean I don't need to call finishConnect()?这是否意味着我不需要调用finishConnect()? From what I read, this is for local connections, but it does not specify if remote connections could possibly return true immediately or not.从我读到的内容来看,这是针对本地连接的,但它没有指定远程连接是否可能立即返回 true。

  • Is this where the client sends a SYN to the server?这是客户端向服务器发送 SYN 的地方吗?


SERVER:服务器:

4) Server gets an incoming connection through some serverSocketChannel.accept() , which we assume is non blocking and will pretend in this case it returns a valid socket object (and not null). 4) 服务器通过一些serverSocketChannel.accept()获取传入连接,我们假设它是非阻塞的,并在这种情况下假装它返回一个有效的套接字对象(而不是 null)。

  • Does this mean that as soon as the server gets the connection, it accepted the connection (assuming all went well) and sends back a SYN-ACK?这是否意味着一旦服务器获得连接,它就接受连接(假设一切顺利)并发回 SYN-ACK?

CLIENT:客户:

5) Does the client call finishConnect() now? 5) 客户端现在调用finishConnect()了吗?

  • When does the client know when to keep calling finishConnect()?客户端什么时候知道什么时候继续调用finishConnect()? Do I just loop for X seconds immediately after calling s.connect(...) from step (3)?从步骤 (3) 调用s.connect(...)后,我是否立即循环 X 秒?

  • Is this when it sends the ACK?这是发送ACK的时候吗? Am I supposed to loop for X seconds until it returns true... and kill the 'half formed connection' if it doesn't respond within X seconds due to something going wrong?我是否应该循环 X 秒直到它返回 true ......如果由于出现问题而在 X 秒内没有响应,则终止“半连接”?

  • Does s.isConnected() return true for the connect() in step (3) succeeding, or finishConnect() succeeding? s.isConnected()是否为步骤 (3) 中的 connect s.isConnected()返回 true 或 finishConnect() 成功?


I'm not sure if I'm doing this properly, and I'm also not sure at what point is it safe for the server to send, or the client to send... is it at (4) for the server, and (5) for the client?我不确定我是否正确地执行此操作,并且我也不确定在什么时候服务器发送或客户端发送是安全的......服务器是否在 (4) 处, (5) 为客户?

Do I need to have the client send a heartbeat packet after the connection is done to the server so my application knows it's okay to start sending data?我是否需要在与服务器建立连接后让客户端发送心跳包,以便我的应用程序知道可以开始发送数据? I don't know how the server would know it's fully connected since I can't see any way the server would know when the final acknowledgement is done... except for the client knowing the connection is established and it sends some kind of 'first packet' data.我不知道服务器如何知道它已完全连接,因为我看不到服务器知道何时完成最终确认的任何方式......除了客户端知道连接已建立并发送某种 '第一个数据包的数据。

The only way the server would know is if I can somehow figure out when it gets the ACK packet, but I can't see a way of Java letting me know currently.服务器会知道的唯一方法是我是否能以某种方式弄清楚它何时收到 ACK 数据包,但我目前看不到 Java 的方式让我知道。

NOTE: I may be missing knowledge, I may have said some incorrect stuff, if you can point out where they are wrong I'd be more than glad to update my post so it's factually correct.注意:我可能缺少知识,我可能说了一些不正确的东西,如果你能指出他们错在哪里,我会很高兴更新我的帖子,所以它实际上是正确的。

Links that guided my knowledge/creation of this post:指导我了解/创作这篇文章的链接:

TCP protocol TCP协议

SocketChannel Javadocs SocketChannel Javadocs

Do we need to bind here?这里需要绑定吗? Or is that just for if we want to refer to a local port?还是只是为了引用本地端口?

You don't need to bind the client SocketChannel .您不需要绑定客户端SocketChannel

s.connect(someAddr)

If this returns true, the javadocs say the connection is established.如果返回 true,则 javadoc 表示连接已建立。 Does that mean I don't need to call finishConnect() ?这是否意味着我不需要调用finishConnect()

Correct.正确的。

From what I read, this is for local connections从我读到的,这是用于本地连接

From what you read where?你从哪里读到的? No truth in that.没有真相。

but it does not specify if remote connections could possibly return true immediately or not.但它没有指定远程连接是否可能立即返回 true。

It can return true any time.它可以随时返回 true。 You have to check.你必须检查。 And it has nothing to do with local or remote.它与本地或远程无关。

Is this where the client sends a SYN to the server?这是客户端向服务器发送 SYN 的地方吗?

Yes.是的。

Server gets an incoming connection through some serverSocketChannel.accept() , which we assume is non blocking and will pretend in this case it returns a valid socket object (and not null).服务器通过一些serverSocketChannel.accept()获取传入连接,我们假设它是非阻塞的,并且在这种情况下会假装它返回一个有效的套接字对象(而不是 null)。 Does this mean that as soon as the server gets the connection, it accepted the connection (assuming all went well) and sends back a SYN-ACK?这是否意味着一旦服务器获得连接,它就接受连接(假设一切顺利)并发回 SYN-ACK?

No. The SYN-ACK has already been sent by the TCP/IP stack.否。TCP/IP 堆栈已经发送了 SYN-ACK。 It doesn't depend on when the application code calls accept() .它不取决于应用程序代码何时调用accept()

Does the client call finishConnect() now?客户端现在调用finishConnect()了吗?

Again this doesn't depend on when the server application called accept() .同样,这不取决于服务器应用程序何时调用accept() The TCP handshake is completed by the server kernel. TCP 握手由服务器内核完成。 The client should call finishConnnect() if connect() didn't return true and a subsequent select() indicates that the channel is now connectable.如果connect()没有返回 true 并且后续的select()指示通道现在可以连接,则客户端应该调用finishConnnect()

Note that finishConnect() can return true, in which case you just proceed, or false, in which case you just keep waiting for OP_CONNECT : or it can throw an exception, in which case it failed and you must close the channel.请注意, finishConnect()可以返回 true,在这种情况下您只需继续,或者返回 false,在这种情况下您只需等待OP_CONNECT :或者它可以抛出异常,在这种情况下它失败并且您必须关闭通道。

When does the client know when to keep calling finishConnect() ?客户端什么时候知道什么时候继续调用finishConnect() Do I just loop for X seconds immediately after calling s.connect(...) from step (3)?从步骤 (3) 调用s.connect(...)后,我是否立即循环 X 秒?

See above.看上面。 But if you're prepared to just loop, why do the connect in non-blocking mode at all?但是如果你准备只是循环,为什么要在非阻塞模式下连接呢? Do it in blocking mode and go into non-blocking mode when it succeeds.以阻塞模式执行,成功时进入非阻塞模式。 Much simpler.简单多了。

Is this when it sends the ACK?这是发送ACK的时候吗?

No. That is all done by the kernel asynchronosusly不,这都是由内核异步完成的

Am I supposed to loop for X seconds until it returns true... and kill the 'half formed connection' if it doesn't respond within X seconds due to something going wrong?我是否应该循环 X 秒直到它返回 true ......如果由于出现问题而在 X 秒内没有响应,则终止“半连接”?

No, see above.不,见上文。

Does s.isConnected() return true for the connect() in step (3) succeeding, or finishConnect() succeeding? s.isConnected()是否为步骤 (3) 中的connect() s.isConnected()返回 true 或finishConnect()成功?

Both: they're mutually exclusive.两者:它们是相互排斥的。

I'm not sure if I'm doing this properly, and I'm also not sure at what point is it safe for the server to send, or the client to send... is it at (4) for the server, and (5) for the client?我不确定我是否正确地执行此操作,并且我也不确定在什么时候服务器发送或客户端发送是安全的......服务器是否在 (4) 处, (5) 为客户?

The server can send as soon as it has an accepted socket.服务器可以在接受套接字后立即发送。 The client can send as soon as either connect() or finishConnect( ) returns true.一旦connect()finishConnect( ) 返回true,客户端就可以发送。

Do I need to have the client send a heartbeat packet after the connection is done to the server so my application knows it's okay to start sending data?我是否需要在与服务器建立连接后让客户端发送心跳包,以便我的应用程序知道可以开始发送数据?

No.不。

I don't know how the server would know it's fully connected since I can't see any way the server would know when the final acknowledgement is done... except for the client knowing the connection is established and it sends some kind of 'first packet' data.我不知道服务器如何知道它已完全连接,因为我看不到服务器知道何时完成最终确认的任何方式......除了客户端知道连接已建立并发送某种 '第一个数据包的数据。

See above.看上面。 The idea of sending a packet to see if you can send a packet doesn't make sense.发送数据包以查看您是否可以发送数据包的想法没有意义。

The only way the server would know is if I can somehow figure out when it gets the ACK packet服务器知道的唯一方法是我是否能以某种方式弄清楚它何时收到 ACK 数据包

The server already has the ACK packet when accept() returns.accept()返回时,服务器已经有了 ACK 数据包。

NOTE: I may be missing knowledge ...注意:我可能缺少知识......

What you're overlooking is the existence of the listen backlog queue at the server.您忽略的是服务器上存在侦听积压队列。

Caveat: I've done my socket programming using sockets, and not socketchannels.警告:我已经使用套接字而不是套接字通道完成了我的套接字编程。 This answer is based on reading the SocketChannel javadoc, knowing that socketchannels use sockets behind the scenes and being familiar with sockets.这个答案基于阅读 SocketChannel javadoc,知道 socketchannels 在幕后使用套接字并熟悉套接字。

According to the javadoc, if connect() returns true, the connection is established, and you don't need to call finishConnect().根据javadoc,如果connect() 返回true,则连接已建立,您不需要调用finishConnect()。 Think of finishConnect() as a way of checking on the status of connection establishment, rather than as a function that does anything required to establish a connection.将finishConnect() 视为检查连接建立状态的一种方式,而不是执行建立连接所需的任何操作的函数。

On the server, if accept() returns a SocketChannel, then the connection attempt has been received from the client.在服务器上,如果 accept() 返回一个 SocketChannel,则表示已从客户端接收到连接尝试。 The SYN ACK from the server has most likely been sent, although the Java documentation purposely does not specify this, in order to permit future optimizations.来自服务器的 SYN ACK 很可能已经发送,尽管 Java 文档故意没有指定这一点,以便允许未来的优化。 Most likely the connection is considered by Java to be "established", though you could call finishConnect() to make sure; Java 认为该连接很可能是“已建立”,但您可以调用 finishConnect() 来确保; it's possible Java waits until the server gets the client's ACK before it considers the connection fully established. Java 可能会等到服务器收到客户端的 ACK,然后才会认为连接已完全建立。 Note that the Javadoc specifies that the returned SocketChannel will initially be in blocking mode, even if the ServerSocketChannel was in nonblocking mode.请注意,Javadoc 指定返回的 SocketChannel 最初将处于阻塞模式,即使 ServerSocketChannel 处于非阻塞模式。

On the client side, you can start sending data as soon as either connect() or finishConnect() returns true.在客户端,您可以在 connect() 或 finishConnect() 返回 true 后立即开始发送数据。 On the server side, you can certainly start sending data when finishConnect() returns true;在服务器端,当finishConnect() 返回true 时,您当然可以开始发送数据; it may be that the call to finishConnect() is superfluous and accept() returns a SocketChannel with an established connection - that's how I would have written it - but I haven't used SocketChannels enough to be sure.可能是对 finishConnect() 的调用是多余的,accept() 返回一个带有已建立连接的 SocketChannel - 这就是我编写它的方式 - 但我没有使用足够的 SocketChannels 来确定。 You do not need to send a heartbeat packet during connection establishment and doing so would probably be a waste of effort, as the TCP protocol itself handles all the connection related stuff, including the three way handshake (SYN, SYN-ACK, ACK) to establish the connection.您不需要在连接建立期间发送心跳包,这样做可能会浪费精力,因为 TCP 协议本身会处理所有与连接相关的内容,包括三向握手(SYN、SYN-ACK、ACK)到建立连接。

You need to use a Selector to wait on CONNECT and ACCEPT events.您需要使用 Selector 来等待 CONNECT 和 ACCEPT 事件。

NIO is not that hard.蔚来没那么难。 But Java's NIO API is a little harder than necessary.但是 Java 的 NIO API 比必要的要困难一些。 Have fun torturing yourself:)享受折磨自己的乐趣吧:)


The exact mapping between Java API calls and TCP handshake steps is not very clear. Java API 调用和 TCP 握手步骤之间的确切映射不是很清楚。 However, I would think of it in the following model (it doesn't matter much if it is not exactly what's happening in reality)但是,我会在以下模型中考虑它(如果它不完全是现实中发生的事情,则无关紧要)

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

Sequences:序列:

  • client app calls connect(serverAddress) - client sends SYN to server客户端应用程序调用connect(serverAddress) - 客户端将 SYN 发送到服务器

if connect() returns true, the socket channel can be used immeidately.如果connect()返回 true,则可以立即使用套接字通道。 however, I've never observed this case in practice, not even on local loopback connections.但是,我从未在实践中观察到这种情况,甚至在本地环回连接上也没有。

  • server receives client SYN;服务器接收客户端SYN; responds with ACK/SYN以 ACK/SYN 响应

  • client receives server ACK/SYN.客户端接收服务器 ACK/SYN。

  • a CONNECT event is raised to client app.向客户端应用程序引发 CONNECT 事件。

  • cilent app then calls finishConnect() - client sends ACK to server cilent 应用程序然后调用finishConnect() - 客户端向服务器发送 ACK

Here, finishConnect() will not return false, because of the previous CONNECT event.在这里, finishConnect()不会返回 false,因为之前的 CONNECT 事件。 It could throw exception though.虽然它可能会抛出异常。

Now, client considers TCP handshake complete.现在,客户端认为 TCP 握手完成。 The client socket channel can be used immediately.可以立即使用客户端套接字通道。

  • server receives client ACK.服务器收到客户端 ACK。 server considers TCP handshake complete.服务器认为 TCP 握手完成。 the connection is put in the backlog.连接被放在积压中。
  • an ACCEPT event is raised to server app.向服务器应用程序引发 ACCEPT 事件。

  • server app then calls accept() .服务器应用程序然后调用accept() The returned socket channel can be used immediately.返回的套接字通道可以立即使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM