C#线程在套接字上接收数据之前退出

本文关键字:数据 退出 线程 套接字 | 更新日期: 2023-09-27 18:00:54

我正在尝试使用套接字和内存流通过网络发送一些文本。在我的示例中,完整的数据长度为20480字节。缓冲区大小为8192。在我可以接收最后4096个字节之前,套接字只接收3088个字节,整个线程在接收最后一块数据之前退出,而不会引发异常。

// Send
while (sentBytes < ms.Length)
{
    if (streamSize < Convert.ToInt64(buffer.Length))
    {
        ms.Read(buffer, 0, Convert.ToInt32(streamSize));
        count = socket.Send(buffer, 0, Convert.ToInt32(streamSize), SocketFlags.None);
        sentBytes += Convert.ToInt64(count);
        streamSize -= Convert.ToInt64(count);
    }
    else
    {
        ms.Read(buffer, 0, buffer.Length);
        count = socket.Send(buffer, 0, buffer.Length, SocketFlags.None);
        sentBytes += Convert.ToInt64(count);
        streamSize -= Convert.ToInt64(count);
    }
}
// Receive
while (readBytes < size)
{
    if (streamSize < Convert.ToInt64(buffer.Length))
    // exits after this, before receiving the last 1008 bytes
    {
        count = socket.Receive(buffer, 0, Convert.ToInt32(streamSize), SocketFlags.None);
        if (count > 0)
        {
            ms.Write(buffer, 0, count);
            readBytes += Convert.ToInt64(count);
            streamSize -= Convert.ToInt64(count);
        }
    }
    else
    {
        count = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);
        if (count > 0)
        {
            ms.Write(buffer, 0, count);
            readBytes += Convert.ToInt64(count);
            streamSize -= Convert.ToInt64(count);
        }
    }
}

我使用完全相同的算法来发送/接收大小更大(超过1GB(的文件,传输效果很好,没有文件损坏(我使用文件流(。有趣的是,如果我在发送方添加一个断点,那么这段代码就可以在调试器中工作。

也适用于此修改:

if (streamSize < Convert.ToInt64(buffer.Length))
{
    if (count > 0)
    {
        ms.Write(buffer, 0, Convert.ToInt32(streamSize));
        readBytes += streamSize;
        streamSize -= streamSize;
    }
}

但这不需要检查接收到的数据量,也不适用于传输文件。有人能指出这里发生了什么吗?


线程开始时是这样的:

public ClientConnection(Socket clientSocket, Server mainForm)
{
  this.clientSocket = clientSocket;
  clientThread = new Thread(ReceiveData);
  clientConnected = true;
  this.mainForm = mainForm;
  clientThread.Start(clientSocket);
}

从OP的评论中添加

// text is 10240 characters long 
MemoryStream ms = new MemoryStream(UnicodeEncoding.Unicode.GetBytes(text));
// streamsize is 20480, which is sent prior to text in a header to the receiver 
long streamSize = ms.Length; 

更新:测试了更多的文件,现在文件传输也失败了。在所有情况下,问题都出在最后1008个字节上。

C#线程在套接字上接收数据之前退出

我找到了……当我期望接收标头时,我没有准备好软件来接收标头大小的数据。

//byte[] buffer = new byte[1024];
byte[] buffer = new byte[16];
readBytes = socket.Receive(buffer, 0, buffer.Length, SocketFlags.None);

这在某种程度上导致了每次我接收负载的最后一块时,套接字上都会写入16字节的数据,套接字断开连接,线程退出,没有抛出任何异常。我希望这个答案有一天能帮助其他遇到同样问题的人。所有数据传输现在都能正常工作。

请考虑使用NetworkStream类而不是Socket类来执行I/O操作的功能的简化实现:NetworkStream类允许略微提高抽象级别。可以使用Socket类的实例来创建NetworkStream类的实例。

发件人

Sender的实现使用Stream.CopyTo方法非常简单:

private static void CustomSend(Stream inputStream, Socket socket)
{
    using (var networkStream = new NetworkStream(socket))
    {
        inputStream.CopyTo(networkStream, BufferSize);
    }
}

接收器

让我们介绍Stream类的以下扩展方法,它使用指定的缓冲区将Stream类的一个实例中的确切字节数复制到另一个实例:

using System;
using System.IO;
public static class StreamExtensions
{
    public static bool TryCopyToExact(this Stream inputStream, Stream outputStream, byte[] buffer, int bytesToCopy)
    {
        if (inputStream == null)
        {
            throw new ArgumentNullException("inputStream");
        }
        if (outputStream == null)
        {
            throw new ArgumentNullException("outputStream");
        }
        if (buffer.Length <= 0)
        {
            throw new ArgumentException("Invalid buffer specified", "buffer");
        }
        if (bytesToCopy <= 0)
        {
            throw new ArgumentException("Bytes to copy must be positive", "bytesToCopy");
        }
        int bytesRead;
        while (bytesToCopy > 0 && (bytesRead = inputStream.Read(buffer, 0, Math.Min(buffer.Length, bytesToCopy))) > 0)
        {
            outputStream.Write(buffer, 0, bytesRead);
            bytesToCopy -= bytesRead;
        }
        return bytesToCopy == 0;
    }
    public static void CopyToExact(this Stream inputStream, Stream outputStream, byte[] buffer, int bytesToCopy)
    {
        if (!TryCopyToExact(inputStream, outputStream, buffer, bytesToCopy))
        {
            throw new IOException("Failed to copy the specified number of bytes");
        }
    }
}

因此,接收器可以实现如下:

private static void CustomReceive(Socket socket)
{
    // It seems your receiver implementation "knows" the "size to receive".
    const int SizeToReceive = 20480;
    var buffer = new byte[BufferSize];
    var outputStream = new MemoryStream(new byte[SizeToReceive], true);
    using (var networkStream = new NetworkStream(socket))
    {
        networkStream.CopyToExact(outputStream, buffer, SizeToReceive);
    }
    // Use the outputStream instance...
}

重要提示

请不要忘记调用Socket类实例的Dispose()方法(对于Sender和Receiver(。方法调用的缺失可能是问题的根本原因。