接收正确数量的TCP

时间:2014-02-03 23:11:04

标签: c# sockets tcp

我正在使用C#启动TCP异步服务器,我正在努力以正确的方式从服务器接收正确数量的数据。 我的问题是这样的:由于TCP是STREAM协议的本质,在收到的每条消息的末尾没有分隔符,所以我唯一能做的就是在消息的开头添加即将发布的消息大小并因此做出反应;问题是,我怎样才能回收并确保我没有在流中阅读“下一条”消息?

我的伪代码如下所示:

// 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

如果第一个recv没有得到整个消息,那么下一个recv是否会远远超出第一个的边界,并且可能会在我实际想要阅读的消息中添加一些不需要的字节?

3 个答案:

答案 0 :(得分:2)

以下是如何使用“async / await”和一些辅助扩展方法完成的。

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);
    }
}

答案 1 :(得分:1)

正如您所看到的,TCP不提供原生框架。更糟糕的是,为从网络堆栈接收的每个TCP段报告了一个同步I / O事件,甚至可能与发送调用不匹配。

在构建收到的流的内容时,您需要使用以下之一:

  • 固定长度的消息,可能带有一些主要类型的歧视,
  • 显式长度前缀指示,或
  • 一个明确的消息分隔符。

选择通常取决于非技术因素。

答案 2 :(得分:0)

如果你知道挂起的长度(你说你根据某种协议头做了),那么只读取已知的挂起长度。在你的情况下Marshal.Sizeof(标题) - state.bytesReceived

相关问题