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个字节上。
我找到了……当我期望接收标头时,我没有准备好软件来接收标头大小的数据。
//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(。方法调用的缺失可能是问题的根本原因。