C# 异步套接字服务器接收问题

发布于 2024-11-05 17:40:08 字数 2695 浏览 0 评论 0原文

我已经在这里实现了关于这篇文章的服务器应用程序: http: //www.codeguru.com/csharp/csharp/cs_network/sockets/article.php/c8781#Client1

总结:我正在使用异步套接字阿拉 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());
}

所以,我认为这个代码片段一定足以让您了解我正在尝试使用的想法! 如果您需要更多详细信息或代码片段,请告诉我,我会发布它!

谢谢!

I have implemented my Server application regarding this post here: http://www.codeguru.com/csharp/csharp/cs_network/sockets/article.php/c8781#Client1

Sum up: I am using async Sockets ala BeginAccept(..), BeginReceive(..).
My Server is capable of handling mutliple clients and everything works fine until a client performas two or more synchronous send operation without waiting some time. The client does not get any error and so is not notified, that the server does not get the second message! If the client waits approx. 100ms after the first send operation, everything works fine.
I thought that when i use TCP i can ensure that the server receives the message. (Except there is an exception thrown)!
Could you provide me a solution to fix this.

Here are the WaitForData(..) & OnDataReceive(..) Methods that i implemented in the server

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);
    }
}

The CLIENT calls a synchronous SEND METHOD TWICE or MORE! server 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());
}

so, i think this code snippets must be enough for you to get the idea i was trying to use!
If you need further details or code snippets, just tell me i will post it!

Thanx!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

风透绣罗衣 2024-11-12 17:40:08

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);

TCP is stream based and not message based. One Read can contain any of the following alternatives:

  • A teeny weeny part of message
  • A half message
  • Excactly one message
  • One and a half message
  • Two messages

Thus you need to use some kind of method to see if a complete message have arrived. The most common methods are:

  • Add a footer (for instance an empty line) which indicates end of message
  • Add a fixed length header containing the length of the message

Update

Simple example having just length as header.

Server side:

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

Client side:

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);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文