简体   繁体   English

接收正确数量的数据TCP

[英]Receive correct amount of data TCP

I'm bringing up a TCP async server using C# and I'm struggling on the right way to receive the correct amount of data from the server. 我正在使用C#启动TCP异步服务器,而我正在努力寻找从服务器接收正确数据量的正确方法。 My question is this one: due to the nature of TCP being a STREAM protocol, there is no delimiter at the end of each message received so the only thing I can do is add at the beginning of the message the upcoming message size and react consequently; 我的问题是这样的:由于TCP是STREAM协议的性质,所以在收到的每条消息的末尾都没有定界符,因此,我唯一能做的就是在消息的开头添加即将到来的消息大小并做出反应。 ; the thing is, how can I recv and be sure that I'm not reading the "next" message in the stream? 问题是,我该如何接收并确保我没有阅读流中的“下一条”消息?

My pseudo code looks like this: 我的伪代码如下所示:

// client accepted, begin receiving
client.BeginReceive(state.buffer, 0, StateObject.bufferSize, 0, new AsyncCallback(_cbck_Read), state);

private void _cbck_Read(IAsyncResult ar)
{

    StateObject state  = (StateObject)ar.AsyncState;
    Socket client      = state.clientSocket; 

    int bytesRead = client.EndReceive(ar);

        // if data have been received
        if (bytesRead > 0)
        {
            state.bytesReceived += bytesRead;

            // no message header received so far go on reading
            if (state.bytesReceived < Marshal.SizeOf(header))
            {
                client.BeginReceive(state.buffer, 0, StateObject.bufferSize, 0, new AsyncCallback(_cbck_Read), state);
            }

 // ... go ahead reading

If the first recv does not get the whole message, could the next recv go far beyond the boundaries of the first and possibly add some unwanted bytes to the message I'm actually wanting to read? 如果第一个recv不能获得完整的消息,那么下一个recv是否可以超出第一个recv的范围,并可能在我实际上想要读取的消息中添加一些不需要的字节?

Here is how it can be done using "async/await" with some helper extension methods. 这是使用“异步/等待”和一些辅助扩展方法来完成的。

Socket s = new Socket(SocketType.Stream, ProtocolType.Tcp);
await s.ConnectTaskAsync("stackoverflow.com", 80);

await s.SendTaskAsync(Encoding.UTF8.GetBytes("GET /\r\n\r\n"));


var buf1 = await s.ReceiveExactTaskAsync(100); //read exactly 100 bytes
Console.Write(Encoding.UTF8.GetString(buf1));

var buf2 = await s.ReceiveExactTaskAsync(100); //read exactly 100 bytes
Console.Write(Encoding.UTF8.GetString(buf2));

public static class SocketExtensions
{
    public static Task<int> ReceiveTaskAsync(this Socket socket, byte[] buffer, int offset, int count)
    {
        return Task.Factory.FromAsync<int>(
                         socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket),
                         socket.EndReceive);
    }

    public static async Task<byte[]> ReceiveExactTaskAsync(this Socket socket, int len)
    {
        byte[] buf = new byte[len];
        int totalRead = 0;
        do{
            int read = await ReceiveTaskAsync(socket, buf, totalRead, buf.Length - totalRead);
            if (read <= 0) throw new SocketException();
            totalRead += read;
        }while (totalRead != buf.Length);
        return buf;
    }

    public static Task ConnectTaskAsync(this Socket socket, string host, int port)
    {
        return Task.Factory.FromAsync(
                         socket.BeginConnect(host, port, null, null),
                         socket.EndConnect);
    }

    public static Task SendTaskAsync(this Socket socket, byte[] buffer)
    {
        return Task.Factory.FromAsync<int>(
                         socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, null, socket),
                         socket.EndSend);
    }
}

As you observe, TCP provides no native framing. 如您所见,TCP没有提供本机帧。 Worse, the a sync I/O events are reported for each TCP segment received from the network stack, and may not even match the send calls. 更糟糕的是,将从网络堆栈接收到的每个TCP段报告一个同步I / O事件,甚至可能与发送调用不匹配。

While building up the contents of the received stream, you will need to use one of: 建立接收到的流的内容时,您将需要使用以下之一:

  • fixed length messages, possibly with some leading type discrimination, 固定长度的消息,可能带有一些前导类型的区别,
  • explicit length prefix indication, or 明确的长度前缀指示,或
  • an unambiguous message delimeter. 明确的消息定界符。

The choice will generally depend on non-technical factors. 该选择通常取决于非技术因素。

if you know the pending length (which you say you do based on protocol header of some sort) then only read the known pending length. 如果您知道挂起的长度(您说您是根据某种协议头做的),则仅读取已知的挂起的长度。 IN your case Marshal.Sizeof(header) - state.bytesReceived 在您的情况下Marshal.Sizeof(header)-state.bytesReceived

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

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