首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >SocketAsyncEventArgs片段顺序

SocketAsyncEventArgs片段顺序
EN

Stack Overflow用户
提问于 2014-11-01 01:23:48
回答 1查看 619关注 0票数 0

我有一个问题,接收套接字包排序使用SocketAsyncEventArgs。我问题的症结在于,当客户端向服务器发送数据包时,服务器将以非标准大小的片段接收数据包,并按随机顺序处理。这显然意味着数据包不能被我的应用程序解码,而应用程序则会破坏整个对话。

例如,客户机将使用Socket.NetworkStream.Write()方法发送一个完整的数据包:

代码语言:javascript
复制
 [-------PACKET-------]

使用SocketAsyncEventArgs的服务器将在两个单独的数据包上接收异步回调,但将首先处理数据包的最后一块:

代码语言:javascript
复制
 First packet:  ET-------]
 Second packet: [-----PACK--

这并不是在所有的数据包上都会发生,而且我还没有能够根据数据包大小或时间精确地再现它。我实现了send/ack通信协议,以便在服务器成功接收最后一个数据包之前,客户端不会再发送另一个数据包,所以不可能是我重载了服务器。

最令人沮丧的是,服务器上的Socket.Available总是为零,根据文档,这意味着没有任何可读取的东西。

代码语言:javascript
复制
 If you are using a non-blocking Socket, Available is a good way to determine whether data is
 queued for reading, before calling Receive. The available data is the total amount of data
 queued in the network buffer for reading. If no data is queued in the network buffer, 
 Available returns 0.

在零可用的情况下,SocketEventArgs.Count似乎没有提供任何有价值的东西,偏移量与接收缓冲区有关,而不是基于实际数据流的位置--我不知道如何将这些片段排序。

我猜问题是数据包的第一部分的异步回调被第二个回调抢占,第二个回调完全处理,然后返回到第一个部分。问题是我不能同步整个回调(希望.NET有一个像Java这样的同步函数)。即使我这么做了,这似乎从一开始就否定了异步回调的好处。

我做错了什么,使这些错误的顺序,或我可以做什么,使他们正确处理?

EN

回答 1

Stack Overflow用户

发布于 2015-03-20 07:22:35

我不太清楚你的一些发言是怎么做的。您编写时使用的是SocketAsyncEventArgs,但是正在尝试处理.Count.Available之类的奇怪API。如果套接字类型是TCP,您可能做错了什么,因为数据包的顺序总是正确的。它们甚至可能被分割成一个字节大小的块,但顺序将是正确的。这几乎就是TCP的意义所在。

由于您没有提供任何代码,并且基于您的声明,我想最好为您提供一些SSCE来帮助您入门。

该示例在C#中,但应适用于VB.net。检查代码中的注释,看看从哪里可以正确地获取接收到的数据。实现将接收到的数据写入控制台并将其发送回客户端。回送服务器制作了很棒的样本!

代码语言:javascript
复制
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace SaeaSample
{
    public class Program
    {
        static void Main()
        {
            var server = new Server(new IPEndPoint(IPAddress.Any, 12345));

            // ugly sample clients
            Parallel.For(0, 4, i =>
            {
                using (var client = new TcpClient("localhost", 12345))
                using (var stream = client.GetStream())
                using (var writer = new BinaryWriter(stream))
                using (var reader = new BinaryReader(stream))
                {
                    var text = "Hello Async-Server!";
                    var message = Encoding.UTF8.GetBytes(text);
                    Console.WriteLine("s: {0}: {1}", i, text);
                    writer.Write(message);
                    var roundtrip = reader.ReadBytes(message.Length);
                    Console.WriteLine("r: {0}: {1}", i, Encoding.UTF8.GetString(roundtrip));
                }
            });

            Console.ReadLine();
        }
    }

    public class Server
    {
        private const int readBufferSize = 8192;
        private const int sendBufferSize = readBufferSize;

        // just have a fixed number of clients instead of
        // pooling for the sake of being an example
        private const int maxClients = 4;
        private const int maxQueue = 10;

        private readonly byte[] buffer = new byte[maxClients * (readBufferSize + sendBufferSize)];
        private readonly Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

        public Server(IPEndPoint localEndPoint)
        {
            socket.Bind(localEndPoint);
            socket.Listen(maxQueue);

            for (int i = 0; i < maxClients; i++)
            {
                var client = new UserToken(i);
                client.RecvArgs.Completed += completed;
                client.SendArgs.Completed += completed;

                Console.WriteLine("accepting on client slot {0}", client.Slot);

                if (!socket.AcceptAsync(client.RecvArgs))
                {
                    completed(this, client.RecvArgs);
                }
            }
        }

        private void completed(object sender, SocketAsyncEventArgs e)
        {
            var client = (UserToken)e.UserToken;

            // socket operation had success
            if (e.SocketError == SocketError.Success)
            {
                // new client connected
                if (e.LastOperation == SocketAsyncOperation.Accept)
                {
                    onAccept(client);
                }
                // either send or received worked
                else if (e.BytesTransferred > 0)
                {
                    if (e.LastOperation == SocketAsyncOperation.Receive)
                    {
                        onReceived(client);
                    }
                    else if (e.LastOperation == SocketAsyncOperation.Send)
                    {
                        onSend(client);
                    }
                    // should never happen, handle gracefully
                    else
                    {
                        onOther(client);
                    }
                }
                // don't handle anything else
                else
                {
                    onOther(client);
                }
            }
            // socket error occured
            else
            {
                onOther(client);
            }
        }

        private void onAccept(UserToken client)
        {
            Console.WriteLine("client slot {0} connected client from {1}", client.Slot, client.RecvArgs.AcceptSocket.RemoteEndPoint);

            // once accepted, start receiving
            client.RecvArgs.SetBuffer(buffer, client.Slot * (readBufferSize + sendBufferSize), readBufferSize);

            if (!client.RecvArgs.AcceptSocket.ReceiveAsync(client.RecvArgs))
            {
                completed(this, client.RecvArgs);
            }
        }

        private void onReceived(UserToken client)
        {
            // echo whatever we got
            var builder = new StringBuilder();

            // here is the important part
            for (int i = 0; i < client.RecvArgs.BytesTransferred; i++)
            {
                // offset the buffer and echo in hex
                builder.Append(client.RecvArgs.Buffer[client.Slot * (readBufferSize + sendBufferSize) + i].ToString("x2"));
            }
            Console.WriteLine("received {0} bytes from client slot {1}: {2}", client.RecvArgs.BytesTransferred, client.Slot, builder.ToString());

            // send data back ... this is an echo server after all
            client.SendArgs.SetBuffer(client.RecvArgs.Buffer, client.Slot * (readBufferSize + sendBufferSize) + readBufferSize, client.RecvArgs.BytesTransferred);
            Buffer.BlockCopy(client.RecvArgs.Buffer, client.RecvArgs.Offset, client.SendArgs.Buffer, client.SendArgs.Offset, client.RecvArgs.BytesTransferred);
            if (!client.RecvArgs.AcceptSocket.SendAsync(client.SendArgs))
            {
                completed(this, client.SendArgs);
            }
        }

        private void onSend(UserToken client)
        {
            Console.WriteLine("sent {0} bytes back to client slot {1}", client.SendArgs.BytesTransferred, client.Slot);

            // start receiving again
            if (!client.RecvArgs.AcceptSocket.ReceiveAsync(client.RecvArgs))
            {
                completed(this, client.RecvArgs);
            }
        }

        private void onOther(UserToken client)
        {
            Console.WriteLine("disconnecting client slot {0}", client.Slot);

            // just close the connection and accept again
            client.RecvArgs.SetBuffer(null, 0, 0);
            if (client.RecvArgs.AcceptSocket != null) {
                client.RecvArgs.AcceptSocket.Dispose();
                client.RecvArgs.AcceptSocket = null;
            }

            Console.WriteLine("accepting on client slot {0}", client.Slot);

            if (!socket.AcceptAsync(client.RecvArgs))
            {
                completed(this, client.RecvArgs);
            }
        }
    }

    public class UserToken
    {
        public readonly int Slot;
        public readonly SocketAsyncEventArgs RecvArgs = new SocketAsyncEventArgs();
        public readonly SocketAsyncEventArgs SendArgs = new SocketAsyncEventArgs();

        public UserToken(int slot)
        {
            Slot = slot;
            RecvArgs.UserToken = this;
            SendArgs.UserToken = this;
        }
    }
}

还请注意,由于这段代码是异步的,控制台输出可能总是有序的,也可能不是总是有序的。您可以将读和写缓冲区的大小常数从8192降到1。数据包将在两个方向逐字节发送,但肯定仍然是有序的。

对于更深入的解释,MSDN始终是一个很好的起点。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26685438

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档