【问题标题】:C# thread exits before receiving data on socketC# 线程在套接字上接收数据之前退出
【发布时间】:2015-07-02 16:14:52
【问题描述】:

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

【问题讨论】:

  • 你是怎么得到那段代码的。你是如何启动你的线程的?
  • streamSize如何计算?
  • // 文本长度为 10240 个字符 MemoryStream ms = new MemoryStream(UnicodeEncoding.Unicode.GetBytes(text)); // streamsize 为 20480,在 header 中的文本之前发送给接收者 long streamSize = ms.Length;
  • @Laz80UK - 将来,请编辑您的问题以添加代码,因为代码在 cmets 中的格式不正确并且难以阅读/遵循。
  • 是的,我想到了。第一次使用这里,感谢您帮助我。 :)

标签: c# multithreading sockets memorystream


【解决方案1】:

我找到了...当我希望收到标头时,我还没有准备软件来接收完全标头大小的数据。

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

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

【讨论】:

  • 我在行为上得到了类似的东西 - 有人可以解释一下 @Laz80UK 的“流氓 16 字节数据”是什么意思吗?
  • 另外,你确定线程正在退出并且没有在 socket.receive 处阻塞吗?我看不出线程没有任何异常退出的理由
  • 线程基本崩溃了,所以没有抛出异常。因为套接字需要 1024 个字节,而只发送了 16 个字节,所以在线程退出之前,缓冲区中出现了一些未初始化的 16 个字节。希望这会有所帮助。
【解决方案2】:

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

发件人

使用Stream.CopyTo Method,Sender 的实现非常简单:

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

所以,Receiver可以如下实现:

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() 方法(对于发送者和接收者)。缺少方法调用可能是问题的根本原因。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-08
    • 1970-01-01
    • 1970-01-01
    • 2018-08-11
    • 1970-01-01
    • 1970-01-01
    • 2010-11-10
    相关资源
    最近更新 更多