当以 1024 字节的 block 读取数据时,我如何继续从接收大于 1024 字节的消息的套接字中读取数据,直到没有数据为止?我是否应该只使用 BeginReceive 只读取数据包的长度前缀,然后一旦检索到,使用 Receive() (在异步线程中)读取数据包的其余部分?还是有别的办法?
编辑:
我认为 Jon Skeet 的链接有解决方案,但该代码有一点速度障碍。我使用的代码是:
public class StateObject
{
public Socket workSocket = null;
public const int BUFFER_SIZE = 1024;
public byte[] buffer = new byte[BUFFER_SIZE];
public StringBuilder sb = new StringBuilder();
}
public static void Read_Callback(IAsyncResult ar)
{
StateObject so = (StateObject) ar.AsyncState;
Socket s = so.workSocket;
int read = s.EndReceive(ar);
if (read > 0)
{
so.sb.Append(Encoding.ASCII.GetString(so.buffer, 0, read));
if (read == StateObject.BUFFER_SIZE)
{
s.BeginReceive(so.buffer, 0, StateObject.BUFFER_SIZE, 0,
new AyncCallback(Async_Send_Receive.Read_Callback), so);
return;
}
}
if (so.sb.Length > 0)
{
//All of the data has been read, so displays it to the console
string strContent;
strContent = so.sb.ToString();
Console.WriteLine(String.Format("Read {0} byte from socket" +
"data = {1} ", strContent.Length, strContent));
}
s.Close();
}
现在这个更正在大多数时候都可以正常工作,但是当数据包的大小是缓冲区的倍数时它会失败。这样做的原因是如果缓冲区在读取时被填满,则假定有更多数据;但是和以前一样发生同样的问题。例如,一个 2 字节的缓冲区在一个 4 字节的数据包上被填充了两次,并假设有更多的数据。然后它会阻塞,因为没有什么可读的了。 问题是接收函数不知道数据包结束的时间。
这让我想到了两种可能的解决方案:我可以使用数据包结束定界符,或者我可以读取数据包 header 以找到长度,然后准确接收该长度(正如我最初建议的那样)。
不过,这些都存在问题。我不喜欢使用定界符的想法,因为用户可能会以某种方式将其处理成来自应用程序的输入字符串中的数据包并将其搞砸。它对我来说也有点草率。
长度 header 听起来不错,但我打算使用 Protocol Buffer - 我不知道数据的格式。有长度标题吗?它有多少字节?这是我自己实现的吗?等等。
我该怎么办?
最佳答案
否 - 从回调处理程序再次调用 BeginReceive
,直到 EndReceive
返回 0。基本上,您应该继续异步接收,假设您想要异步 IO 的最大好处.
如果您查看 Socket.BeginReceive
的 MSDN 页面你会看到一个这样的例子。 (诚然,它并不像它可能的那样容易理解。)
关于C# Begin/EndReceive - 如何读取大数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/582550/