我正在尝试使用套接字和内存流通过网络发送一些文本。我的示例中的完整数据长度为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个字节。
答案 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
类的实例。
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
方法(对于发件人和接收者)。缺少方法调用可能是问题的根本原因。