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

时间:2015-07-02 16:14:52

标签: c# multithreading sockets memorystream

我正在尝试使用套接字和内存流通过网络发送一些文本。我的示例中的完整数据长度为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);
        }
    }
}

我使用完全相同的算法来发送/接收具有更大尺寸(超过1 GB)的文件,并且传输工作正常,没有文件被破坏(我使用文件流)。 有趣的是,如果我在发送方添加断点,则此代码在调试器中有效。

也适用于此修改:

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个字节。

2 个答案:

答案 0 :(得分:1)

我发现了......当我希望收到标题时,我还没有准备好软件来接收标题大小的数据。

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

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

答案 1 :(得分:0)

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

发件人

使用Stream.CopyTo Method

,发件人的实现非常简单
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...
}

重要提示

请不要忘记调用Dispose()类实例的Socket方法(对于发件人和接收者)。缺少方法调用可能是问题的根本原因。