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

2023-09-02 10:33:12 作者:』浅爱~不忧伤』

用堵,流 .NET插座我连接到服务器。每当我读小数据位,一切顺利的话,数据被接收到我的缓存:

Using a blocking, streaming .NET socket I'm connecting to a server. Whenever I'm reading small bits of data, all goes well and the data is received into my buffer:

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

在某些情况下,虽然,我没有收到所有的服务器发送了我。数据似乎被砍断。有什么可以这样做的原因是什么?

In some cases though, I'm not receiving everything the server sends me. Data seems to be chopped off. What can be the cause of this?

推荐答案

这是记录在接收()方法,重点煤矿:

This is documented in the Receive() method, emphasis mine:

Receive方法将数据读入缓冲区参数和返回的字节数成功读取。您可以拨打接收来自面向连接和无连接的插座。

The Receive method reads data into the buffer parameter and returns the number of bytes successfully read. You can call Receive from both connection-oriented and connectionless sockets.

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

When ignoring the return value, you will not know what portion of your buffer actually contains relevant data. Depending on the protocol being used, you may or may not know the content length in advance. Some protocols provide this length, others close the connection when done, yet another can use a message boundary.

您将不得不持有另一个缓冲区接收到的数据,并返回或输出整个消息缓冲区,当没有更多的数据可用或预期。这可以这样进行:

You'll have to hold the received data in another buffer, and return or output the entire message buffer when no more data is available or expected. This can be done like this:

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字符(如虚构的协议定义),因此接收到的每个字节表示一个字符(你不能将全部接收到的多字节UNI code字符)。该字节被转换成字符串,并追加到的消息变量。在code循环,直至服务器关闭连接。

The received bytes are ASCII characters (as defined in the made-up protocol), so each byte received indicates one character (you can't convert partially received multibyte unicode characters). The bytes are converted to a string and appended to the message variable. The code loops until the server closes the connection.

当消息大小是已知的在事先,再根据正在使用的协议,您可以创建一个消息缓冲区并有复制的数据中的每个接收()

When the message size is known on beforehand, again, depending on the protocol being used, you can create a message buffer and copy the data there on each 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));

这又可以放在一个可重复使用的方法:

This can in turn be put in a resuable method:

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相同的阅读问题作为插座本身。您必须监控返回值和保持通话阅读(),直到你收到的所有字节。这同样适用于 的TcpClient 具有 GetStream()方法,在其返回值为您也马上要继续读书,直到你读过的所有数据。

There's the NetworkStream which wraps a socket, but it has the same reading issues as the socket itself. You will have to monitor the return value and keep calling Read() until you've received all bytes. Same goes for the TcpClient that has a GetStream() method, on whose return value you'll also have to continue reading until you've read all data.