我正在使用C#套接字(异步模式),需要从流中读取确切的字节数以正确解析消息。由于我们系统中的消息很长,看起来像socket.EndRead操作返回的字节数少于socket.BeginRead中请求的字节数。只有在读取确切的字节数时才有机会完成c#套接字标记操作???是不是网络流是??? \
IAsyncRes ar = socket.BeginRead(1Mb byte message)
ar.Handle.Wait() // --> will signal ONLY when 1Mb us read !!!!
socket.EndRead()
UPD:
我用c#迭代器解决了这个问题。 (运行irator循环并负责执行MoveNext的线程未在此处显示)
protected IEnumerator<IAsyncResult> EnumReceiveExact(byte[] array)
{
int offset = 0;
while (offset < array.Length)
{
SocketError err = SocketError.Success;
IAsyncResult ar = _socket.BeginReceive(array, offset, array.Length - offset, SocketFlags.None, out err, null, null);
Console.WriteLine("{0}:err:{1}", this, err);
if (err != SocketError.Success)
{
_socket.Close();
throw new Exception("Error " + err);
}
yield return ar;
while (!ar.IsCompleted)
{
yield return ar;
}
offset += _socket.EndReceive(ar, out err);
if (err != SocketError.Success)
{
_socket.Close();
throw new Exception("Error " + err);
}
}
}
答案 0 :(得分:0)
与调查员进行良好的调用,虽然我希望你的外部代码不只是在asyncresults上调用WaitOne,因为这会阻止正在运行的线程。
如果您喜欢这种异步编码风格,请查看NuGet上的Wintellect AsyncEnumerator - 它也使用迭代器,使代码非常节省资源,并添加更简单的方法来处理取消和异常,同时确保所有APM端方法都被调用。
我之前通过以下方式解决了确切的阅读问题:
1)为插座上发送的数据添加长度前缀
2)使用以下方法定义一个在Socket上工作的辅助类:
public IAsyncResult BeginRead(AsyncCallback callback)
// Calculate whether to read length header or remaining payload bytes
// Issue socket recieve and return its IAsyncResult
public MemoryStream EndRead(IAsyncResult result)
// Process socket read, if payload read completely return as a memorystream
// If length header has been recieved make sure buffer is big enough
// If 0 bytes recieved, throw SocketException(10057) as conn is closed
public IAsyncResult BeginSend(AsyncCallback callback, MemoryStream data)
// Issue sends for the length header or payload (data.GetBuffer()) on the first call
public Boolean EndSend(IAsyncResult result)
// Process bytes sent, return true if payload sent completely.
// If 0 bytes sent, throw SocketException(10057)
因此它仍然需要在循环中调用,但看起来像普通的异步操作,例如通过asyncenumerator调用(没有取消检查和异常处理):
do
{
socketHelper.BeginSend(ae.End(1, ar => socketHelper.EndSend(ar)), sendData);
yield return 1;
doneSend = socketHelper.EndSend(ae.DequeueAsyncResult());
}
while (!doneSend);