为什么我的客户端套接字没有收到服务器套接字发送的内容

时间:2014-05-17 16:31:08

标签: .net sockets

使用阻止,流媒体.NET socket我正在连接到服务器。每当我读取少量数据时,一切都顺利,数据被接收到我的缓冲区中:

using (var socket = new Socket(SocketType.Stream, ProtocolType.IP))
{
    socket.Connect(IPAddress.Parse("127.0.0.1"), 5000);

    byte[] buffer = new byte[BufferSize];

    socket.Receive(buffer);

    // Here buffer doesn't always contain all data the server sent me?
    Console.WriteLine(Encoding.Default.GetString(buffer));
}

但在某些情况下,我没有收到服务器发给我的所有内容。数据似乎被砍掉了。造成这种情况的原因是什么?

1 个答案:

答案 0 :(得分:3)

这是documented in the Receive() method,强调我的:

  

Receive方法将数据读入buffer参数,返回成功读取的字节数。您可以从面向连接的套接字和无连接套接字调用Receive。

当忽略返回值时,您将不知道缓冲区的哪个部分实际包含相关数据。根据所使用的协议,您可能提前知道或不知道内容长度。某些协议提供此长度,其他协议在完成时关闭连接,而另一个协议可以使用消息边界。

您必须将接收的数据保存在另一个缓冲区中,并在没有更多数据可用或预期时返回或输出整个消息缓冲区。这可以这样做:

int BufferSize = 1024;

using (var socket = new Socket(SocketType.Stream, ProtocolType.IP))
{
    socket.Connect(IPAddress.Parse("127.0.0.1"), 5000);

    byte[] buffer = new byte[BufferSize];
    string message = "";

    int bytesReceived;
    do
    {
        bytesReceived = socket.Receive(buffer);
        message += Encoding.ASCII.GetString(buffer, 0, bytesReceived);

    } while (bytesReceived > 0);

    Console.WriteLine(message);
}

接收的字节是ASCII字符(如在制定的协议中定义的),因此接收的每个字节表示一个字符(您不能转换部分接收的多字节unicode字符)。字节将转换为字符串并附加到message变量。代码循环,直到服务器关闭连接。

当事先知道消息大小时,根据所使用的协议,您可以创建消息缓冲区并在每个Receive()上复制数据:

// Received using another Receive() call
int messageSize = 1234;
int totalBytesReceived = 0;
byte[] messageBuffer = new byte[messageSize];

byte[] buffer = new byte[BufferSize];

int bytesReceived;
do
{
    bytesReceived = socket.Receive(buffer);

    // Copy the receive buffer into the message buffer, appending after 
    // previously received data (totalBytesReceived).
    Buffer.BlockCopy(buffer, 0, messageBuffer, totalBytesReceived, bytesReceived);
    totalBytesReceived += bytesReceived;

} while (bytesReceived > 0);

// This assumes the connection wasn't closed prematurely.
Console.WriteLine(Encoding.ASCII.GetString(messageBuffer));

这可以反过来采用可恢复的方法:

public byte[] ReceiveMessage(Socket socket, int messageSize)
{
    byte[] messageBuffer = new byte[messageSize];

    int bytesReceived = 0;
    int totalBytesReceived = 0;
    do
    {
        byte[] buffer = new byte[BufferSize];

        // Receive at most the requested number of bytes, or the amount the 
        // buffer can hold, whichever is smaller.
        int toReceive = Math.Min(messageSize - totalBytesReceived, BufferSize);
        bytesReceived = socket.Receive(buffer, toReceive, SocketFlags.None);

        // Copy the receive buffer into the message buffer, appending after 
        // previously received data (totalBytesReceived).
        Buffer.BlockCopy(buffer, 0, messageBuffer, totalBytesReceived, bytesReceived);

        totalBytesReceived += bytesReceived;

    } while (bytesReceived > 0);

    if (totalBytesReceived < messageSize)
    {
        throw new Exception("Server closed connection prematurely");
    }

    return messageBuffer;
}

包含套接字的NetworkStream,但它has the same reading issues作为套接字本身。您必须监控返回值并继续调用Read(),直到您收到所有字节为止。同样适用于TcpClient that has a GetStream() method,其返回值也是have to continue reading until you've read all data