【问题标题】:C# Async Sockets Server Receive ProblemsC# 异步套接字服务器接收问题
【发布时间】:2011-05-09 08:36:23
【问题描述】:

我已经在此处实现了有关此帖子的服务器应用程序:http://www.codeguru.com/csharp/csharp/cs_network/sockets/article.php/c8781#Client1

总结:我正在使用异步套接字 ala BeginAccept(..)、BeginReceive(..)。 我的服务器能够处理多个客户端并且一切正常,直到客户端执行两个或多个同步发送操作而无需等待一段时间。客户端没有收到任何错误,因此不会通知服务器没有收到第二条消息!如果客户等待大约。第一次发送操作后 100 毫秒,一切正常。 我认为当我使用 TCP 时,我可以确保服务器接收到消息。 (除非抛出异常)! 你能给我一个解决这个问题的方法吗?

这是我在服务器中实现的 WaitForData(..) & OnDataReceive(..) 方法

public void WaitForData(MyClient client)
{
    try
    {
        if (pfnCallBack == null)
        {
            pfnCallBack = new AsyncCallback(OnDataReceived);
        }

        iarResult = client.Socket.BeginReceive(client.DataBuffer,
                                                0, client.DataBuffer.Length,
                                                SocketFlags.None,
                                                pfnCallBack,
                                                client);
    }
    catch (SocketException se)
    {
        MessageBox.Show("SocketException@WaitForData" + se.Message);
    }
}
public void OnDataReceived(IAsyncResult asyn)
{
    try
    {
        MyClient user= (MyClient)asyn.AsyncState;
        int iRx = user.Socket.EndReceive(asyn);

        byte[] receivedData = user.DataBuffer;

        MemoryStream memStream = new MemoryStream();
        BinaryFormatter binForm = new BinaryFormatter();
        memStream.Write(receivedData, 0, receivedData.Length);
        memStream.Seek(0, SeekOrigin.Begin);
        MyMessage msg = (MyMessage)binForm.Deserialize(memStream);

        switch (msg.Command)
        {
            case (MyMessage.MyCommand.ConnId):
                this.connId = (int) msg.MyObject;
                tsslConnStatus.Text += " | ID: " + connId.ToString();
            break;

            case (MyMessage.MyCommand.Text):
                MessageBox.Show(msg.MyObject.ToString());
                break;
        }
        WaitForData(server);
    }
    catch (ObjectDisposedException ode)
    {
        MessageBox.Show("ObjectDisposedException@OnReceiveData" + ode.Message);
    }
    catch (SocketException se)
    {
        MessageBox.Show("SocketException@OnReceiveData" + se.Message);
    }
}

客户端调用同步发送方法两次或更多次!服务器 INSTANCEOF MyClient

if (server.Socket.Connected)
{
    BinaryFormatter bf = new BinaryFormatter();
    MemoryStream ms = new MemoryStream();
    bf.Serialize(ms, message);
    MyMessage = new MyMessage(something);
    server.Socket.Send(ms.ToArray());
}

所以,我认为这段代码 sn-ps 必须足以让您了解我试图使用的想法! 如果您需要更多详细信息或代码 sn-ps,请告诉我,我会发布它!

谢谢!

【问题讨论】:

  • 通过我了解到的 SerialPort.OnDataReceived 句柄,当发生转换“空闲”->“已接收”时调用了该处理程序。我愿意阅读,直到没有可用的数据。这里可能类似。
  • 我不希望在处理程序中收到完整的消息。请注意不完整的消息。
  • 说实话,我不知道如何访问和阅读这些“过渡”?
  • 只是猜测:我会尝试使用 Socket.Available 属性。

标签: c# sockets asynchronous client


【解决方案1】:

TCP 是基于流的而不是基于消息的。一个Read 可以包含以下任一选项:

  • 消息的一小部分
  • 半条消息
  • 正是一条消息
  • 一条半消息
  • 两条消息

因此您需要使用某种方法来查看是否有完整的消息到达。最常用的方法有:

  • 添加一个表明消息结束的页脚(例如一个空行)
  • 添加包含消息长度的固定长度标头

更新

仅将长度作为标题的简单示例。

服务器端:

var buffer = binaryFormmater.Serialize(myobj);
var length = buffer.Length;
networkStream.Send(length);
networkStream.Send(buffer, 0, buffer.Length);

客户端:

var header = new buffer[4];
// TODO: You need to make sure that 4 bytes have been read.
networkStream.Read(header, 0, 4);
var length = BitConverter.ToInt32(buffer);

var readbuffer= new byte[65535];
var bytesLeft = length;
var messageStream = new MemoryStream();
while (bytesLeft > 0)
{
     var read = networkStream.Read(readbuffer, 0, bytesLeft);
     messageStream.Write(readbuffer, 0, read);
     bytesLeft -= read,
}

messageStream.Seek(0, SeekOrigin.Begin);
MyMessage msg = (MyMessage)binForm.Deserialize(messageStream);

【讨论】:

  • +1 这个答案非常正确,如果可以的话,我会投票两次。基于消息的应用层协议不在 TCP 的范围内。您可以自己实现一个,也可以使用众多可用框架之一。
  • 嗯,谢谢你的回答。但我现在不知道从哪里开始。只要不调用 OnReceiveData(..) 方法,我就无能为力,问题是,该方法没有被调用两次。此外,正如您在代码中看到的那样,我确实将整个接收缓冲区反序列化为单个 MyMessage 对象而不会失败。如果我没记错的话,如果这个流包含第二条消息的更多信息,一定有一个例外? Mabe你可以让我更接近我现在真正要做的事情!谢谢!
  • 在解决上述问题之前,我不会做任何其他事情。当您执行Read 时,您可能会收到两条完整的消息,并且由于您只反序列化一条消息,因此可能会丢弃另一条消息。
  • 好的,我明白了。不知道这一次读取事件中可能有两条消息。但是,您能否向我发布一些示例,说明如何以最佳方式实现这种固定长度或页脚?当前的 MyMessage 也应该受支持,据我所知,它是创建 Ojbect MyMessage 然后存储它的大小。如果你能给我一些关于如何实现这一目标的代码,我会很高兴
  • thnx 的代码,但 networkStream 是什么。我正在使用原始的 Socket 类!?你的代码适合异步套接字吗?
猜你喜欢
  • 2018-11-19
  • 1970-01-01
  • 2010-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-30
相关资源
最近更新 更多