gpt4 book ai didi

C# 套接字.BeginReceive/EndReceive

转载 作者:太空狗 更新时间:2023-10-29 17:31:04 28 4
gpt4 key购买 nike

Socket.BeginReceive/EndReceive 函数的调用顺序是什么?

例如,我两次调用 BeginReceive,一次是为了获取消息长度,第二次是为了获取消息本身。现在的场景是这样的,对于我发送的每条消息,我都开始等待它的完成(实际上是对发送消息的确认,我也在收到确认后等待操作完成),所以我调用 BeginReceive 对于每个 BeginSend,但在每个 BeginReceive 的回调中,我检查我是否收到了长度或消息。如果我正在接收消息并且已经完全接收到它,那么我会调用另一个 BeginReceive 来接收操作的完成。现在这就是事情不同步的地方。因为我的接收回调之一正在接收字节,它解释为消息的长度,而实际上它是消息本身。

现在我该如何解决?

编辑:这是一个 C#.NET 问题 :)

这是代码,基本上它太大了,抱歉

public void Send(string message)
{
try
{
bytesSent = 0;

writeDataBuffer = System.Text.Encoding.ASCII.GetBytes(message);
writeDataBuffer = WrapMessage(writeDataBuffer);
messageSendSize = writeDataBuffer.Length;

clientSocket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
new AsyncCallback(SendComplete), clientSocket);
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}

public void WaitForData()
{
try
{
if (!messageLengthReceived)
{
clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived,
SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
}
}

public void Send(string message)
{
try
{
bytesSent = 0;

writeDataBuffer = System.Text.Encoding.ASCII.GetBytes(message);
writeDataBuffer = WrapMessage(writeDataBuffer);
messageSendSize = writeDataBuffer.Length;

clientSocket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
new AsyncCallback(SendComplete), clientSocket);
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}

public void WaitForData()
{
try
{
if (! messageLengthReceived)
{
clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, MESSAGE_LENGTH_SIZE - bytesReceived,
SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
}
else
{
clientSocket.BeginReceive(receiveDataBuffer, bytesReceived, messageLength - bytesReceived,
SocketFlags.None, new AsyncCallback(RecieveComplete), clientSocket);
}
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}

public void RecieveComplete(IAsyncResult result)
{
try
{
Socket socket = result.AsyncState as Socket;
bytesReceived = socket.EndReceive(result);

if (! messageLengthReceived)
{
if (bytesReceived != MESSAGE_LENGTH_SIZE)
{
WaitForData();
return;
}

// unwrap message length
int length = BitConverter.ToInt32(receiveDataBuffer, 0);
length = IPAddress.NetworkToHostOrder(length);

messageLength = length;
messageLengthReceived = true;

bytesReceived = 0;

// now wait for getting the message itself
WaitForData();
}
else
{
if (bytesReceived != messageLength)
{
WaitForData();
}
else
{
string message = Encoding.ASCII.GetString(receiveDataBuffer);

MessageBox.Show(message);

bytesReceived = 0;
messageLengthReceived = false;

// clear buffer
receiveDataBuffer = new byte[AsyncClient.BUFFER_SIZE];

WaitForData();
}
}
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}

}

public void SendComplete(IAsyncResult result)
{
try
{
Socket socket = result.AsyncState as Socket;
bytesSent = socket.EndSend(result);

if (bytesSent != messageSendSize)
{
messageSendSize -= bytesSent;

socket.BeginSend(writeDataBuffer, bytesSent, messageSendSize, SocketFlags.None,
new AsyncCallback(SendComplete), clientSocket);
return;
}

// wait for data
messageLengthReceived = false;
bytesReceived = 0;

WaitForData();
}
catch (SocketException socketException)
{
MessageBox.Show(socketException.Message);
}
}

最佳答案

时间顺序应该是:

  1. BeginReceive消息长度
  2. EndReceive完成#1
  3. BeginReceive对于邮件正文
  4. EndReceive完成#3

例如不使用你可能拥有的回调:

var sync = socket.BeginReceive(....);
sync.AsyncWaitHandle.WaitOne();
var res = socket.EndReceive(sync);
sync = socket.BeginReceive(....);
sync.AsyncWaitHandle.WaitOne();
var res2 = socket.EndReceive(sync);

但是,你最好只使用 Receive !

我认为您可能会发现为两个不同的接收使用单独的处理程序更容易:

... Start(....) {
sync = socket.BeginReceive(.... MessageLengthReceived, null);
}

private void MessageLengthReceived(IAsyncResult sync) {
var len = socket.EndReceive(sync);
// ... set up buffer etc. for message receive

sync = socket.BeginReceive(... MessageReceived, null);
}

private void MessageReceived(IAsyncResult sync) {
var len = socket.EndReceive(sync);
// ... process message
}

最终将所有关联的对象放入一个状态对象中并从 BeginReceive 传递它(在通过 IAsyncResult.AsyncState 的完成委托(delegate)访问中)可以使事情变得更容易,但确实改变了命令式代码的线性思维并完全拥抱事件驱动方法。


2012 年附录:

.NET 4.5版本

有了 C#5 中的异步支持,就有了一个新选项。这使用编译器从内联代码生成手动延续(单独的回调方法)和闭包(状态)。但是,有两件事需要解决:

  1. 同时 System.Net.Sockets.Socket有各种…Async这些方法用于基于事件的异步模式,而不是 Task基于 C#5 的 await 的模式使用。解决方案:使用TaskFactory.FromAsync得到一个Task<T>来自Begin… End…对。

  2. TaskFactory.FromAsync仅支持将最多三个附加参数(除了回调和状态)传递给 Begin… .解决方案:采用零个附加参数的 lambda 具有正确的签名,而 C# 将为我们提供正确的闭包来传递参数。

因此(并且更充分地实现了 Message 是另一种类型,它处理从初始发送的长度的转换,该长度以固定数量的字节编码,然后将内容字节转换为内容缓冲区的长度):

private async Task<Message> ReceiveAMessage() {
var prefix = new byte[Message.PrefixLength];

var revcLen = await Task.Factory.FromAsync(
(cb, s) => clientSocket.BeginReceive(prefix, 0, prefix.Length, SocketFlags.None, cb, s),
ias => clientSocket.EndReceive(ias),
null);
if (revcLen != prefix.Length) { throw new ApplicationException("Failed to receive prefix"); }

int contentLength = Message.GetLengthFromPrefix(prefix);
var content = new byte[contentLength];

revcLen = await Task.Factory.FromAsync(
(cb, s) => clientSocket.BeginReceive(content, 0, content.Length, SocketFlags.None, cb, s),
ias => clientSocket.EndReceive(ias),
null);
if (revcLen != content.Length) { throw new ApplicationException("Failed to receive content"); }

return new Message(content);
}

关于C# 套接字.BeginReceive/EndReceive,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1388561/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com