0

SocketAsyncEventArgs を使用した受信ソケット パケットの順序付けに問題があります。私の問題の核心は、クライアントがサーバーにパケットを送信すると、サーバーが非標準サイズのフラグメントでパケットを受信し、それらがランダムな順序で処理されることです。これは明らかに、アプリがパケットをデコードできず、会話全体が台無しになることを意味します。

たとえば、クライアントは Socket.NetworkStream.Write() メソッドを使用して完全なパケットを送信します。

 [-------PACKET-------]

SocketAsyncEventArgs を使用するサーバーは、2 つの個別のパケットで非同期コールバックを受信しますが、パケットの最後のチャンクが最初に処理されます。

 First packet:  ET-------]
 Second packet: [-----PACK--

これはすべてのパケットで発生するわけではなく、パケット サイズまたはタイミングに基づいて正確に再現することはできませんでした。最後のパケットが正常に受信されたことをサーバーが確認するまで、クライアントが別のパケットを送信しないように、送信/確認通信プロトコルを実装します。

最もイライラする部分は、サーバー上の Socket.Available が常にゼロであることです。これは、ドキュメントによると、何も読み取ることができないことを意味します

 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 は価値のあるものを何も提供していないようであり、オフセットは実際のデータストリームに基づいた場所ではなく、受信バッファーに関係している必要があります。これらのフラグメントをどのように配置できるかわかりません注文。

問題は、パケットの最初の部分の非同期コールバックが、完全に処理されてから最初の部分に戻る 2 番目のコールバックによってプリエンプトされることだと思います。問題は、コールバック全体を同期できないことです (.NET に Java のような同期機能があればいいのに)。たとえ私がそうしたとしても、そもそも非同期コールバックの利点が無効になるようです。

これらが間違った順序で来る原因、またはそれらを正しく処理するにはどうすればよいですか?

4

1 に答える 1

0

いくつかの発言から、あなたが何をしているのかよくわかりません。あなたは使用していると書いていますが、またはSocketAsyncEventArgsのような奇妙な API を処理しようとしています。ソケットの種類が TCP の場合、パケットは常に正しい順序で処理されるため、何か間違ったことをしている可能性があります。それらはわずか 1 バイト サイズのチャンクに断片化されることもありますが、順序は正しくなります。それがTCPのすべてです。.Count.Available

コードを提供しておらず、ステートメントに基づいているため、開始するにはSSCEを提供するのが最善だと思います。

サンプルは C# ですが、VB.net に適用する必要があります。コード内のコメントをチェックして、実際に受信したデータをどこから適切に取得するかを確認してください。実装は、受信したデータをコンソールに書き込み、クライアントに送り返します。Echo サーバーはすばらしいサンプルを作成します。

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が常に出発点として適しています。

于 2015-03-20T07:22:35.507 に答える