在 C# 中使用 Socket api 时 TCP 数据包如何到达 [英] How does a TCP packet arrive when using the Socket api in C#

查看:73
本文介绍了在 C# 中使用 Socket api 时 TCP 数据包如何到达的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直在阅读有关 TCP 数据包以及它们如何在航行期间被多次拆分的信息.我认为我必须在用于实际网络流量的缓冲区的顶部实现某种缓冲区,以便存储每个 ReceiveAsync() 直到足够的数据可用于解析消息.顺便说一句,我正在通过 TCP 发送带长度前缀的 protobuf 序列化消息.

I have been reading about TCP packet and how they can be split up any number of times during their voyage. I took this to assume I would have to implement some kind of buffer on top of the buffer used for the actual network traffic in order to store each ReceiveAsync() until enough data is available to parse a message. BTW, I am sending length-prefixed, protobuf-serialized messages over TCP.

然后我读到低层(以太网?,IP?)实际上会透明地重新组装数据包.

Then I read that the lower layers (ethernet?, IP?) will actually re-assemble packets transparently.

我的问题是,在 C# 中,我能保证通过 TCP 收到完整的消息"吗?换句话说,如果我发送 32 个字节,我是否一定会一次性"接收这 32 个字节(一次调用 ReceiveAsync())?还是我必须存储"每个接收,直到接收到的字节数等于长度前缀?

My question is, in C#, am I guaranteed to receive a full "message" over TCP? In other words, if I send 32 bytes, will I necessarily receive those 32 bytes in "one-go" (one call to ReceiveAsync())? Or do I have to "store" each receive until the number of bytes received is equal to the length-prefix?

此外,我可以在对 ReceiveAsync() 的一次调用中接收更多条消息吗?假设一个protobuf 消息"是 32 个字节.我发送了其中的 2 个.我是否可以一次"接收 48 个字节,然后再接收 16 个字节?

Also, could I receive more than one message in a single call to ReceiveAsync()? Say one "protobuf message" is 32 bytes. I send 2 of them. Could I potentially receive 48 bytes in "one go" and then 16 in another?

我知道这个问题很容易在 google 上出现,但我永远无法判断它是否在正确的上下文中(谈论实际的 TCP 协议,或者 C# 将如何向程序员公开网络流量).

I know this question shows up easily on google, but I can never tell if it's in the correct context (talking about the actual TCP protocol, or how C# will expose network traffic to the programmer).

谢谢.

推荐答案

TCP 是一种流协议 - 它传输字节流.就这样.绝对没有暗示消息框架/分组.事实上,在使用 TCP 套接字编写代码时,您甚至应该忘记以太网数据包或 IP 数据报的存在.

TCP is a stream protocol - it transmits a stream of bytes. That's all. Absolutely no message framing / grouping is implied. In fact, you should forget that Ethernet packets or IP datagrams even exist when writing code using a TCP socket.

您可能会发现自己有 1 个可用字节或 10,000 个可用字节可供读取.(同步)Berkeley 套接字 API 的美妙之处在于,您作为应用程序程序员无需担心这一点.由于您使用的是带长度前缀的消息格式(干得好!),只需 recv() 和您期望的一样多的字节.如果可用的字节数比应用程序请求的多 个字节,内核将保留其余的字节数,直到下一次调用为止.如果可用字节数少于所需的 字节,线程将阻塞或调用将指示接收到的字节数较少.在这种情况下,您可以简单地再次休眠,直到有数据可用.

You may find yourself with 1 byte available, or 10,000 bytes available to read. The beauty of the (synchronous) Berkeley sockets API is that you, as an application programmer don't need to worry about this. Since you're using a length-prefixed message format (good job!) simply recv() as many bytes as you're expecting. If there are more bytes available than the application requests, the kernel will keep the rest buffered until the next call. If there are fewer bytes available than required, the thread will either block or the call will indicate that fewer bytes were received. In this case, you can simply sleep again until data is available.

异步 ​​API 的问题在于它需要应用程序自己跟踪更多的状态.即使是这个 Microsoft 异步客户端套接字示例 远比它需要的复杂.使用异步 API,您仍然可以控制从内核请求的数据量,但是当异步回调被触发时,您需要知道下一个要请求的数据量.

The problem with async APIs is that it requires the application to track a lot more state itself. Even this Microsoft example of Asynchronous Client Sockets is far more complicated than it needs to be. With async APIs, you still control the amount of data you're requesting from the kernel, but when your async callback is fired, you then need to know the next amount of data to request.

请注意,4.5 中的 C# async/await 使异步处理更容易,因为您可以以同步方式执行此操作.看看作者评论的这个答案:

Note that the C# async/await in 4.5 make asynchronous processing easier, as you can do so in a synchronous way. Have a look at this answer where the author comments:

Socket.ReceiveAsync 是一个奇怪的.它与 .net4.5 中的 async/await 特性无关.它被设计为一种替代套接字 API,不会像 BeginReceive/EndReceive 那样严重地抖动内存,并且只需要在最核心的服务器应用程序中使用.

Socket.ReceiveAsync is a strange one. It has nothing to do with async/await features in .net4.5. It was designed as an alternative socket API that wouldn't thrash memory as hard as BeginReceive/EndReceive, and only needs to be used in the most hardcore of server apps.

这篇关于在 C# 中使用 Socket api 时 TCP 数据包如何到达的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆