簡體   English   中英

為什么Socket.AcceptAsync沒有觸發SocketAsyncEventArgs已完成的事件?

[英]Why Socket.AcceptAsync isn't firing SocketAsyncEventArgs Completed event?

我正在開發一個服務器應用程序,它將接收消息並做出響應。 沒什么新鮮的。 所以,實際上我正在關注這個答案這篇文章 ,但我無法讓AcceptAsync()方法觸發Completed事件。 在互聯網上隨處搜索,嘗試了類似問題的解決方案,但似乎沒有什么對我有用。

我也試着撥打server.Start()Task.Run()但沒有運氣。 我知道服務器聽起來很好,因為我可以在netstat -an上看到它,如果我在Listen()之后斷開,我也可以使用telnet連接。

根據我的理解,如果AcceptAsync()方法返回true,它將引發SocketAsyncEventArgs.Completed事件,該事件又將再次調用StartAccept()方法並循環直到我強行退出。

SocketAsyncEventArgs.Completed也是正確的: http//prntscr.com/8l3x8p ,但仍然不起作用。

這是我的一段代碼:

public class TTSServer
{
    private Socket m_serverSocket;
    private IPEndPoint m_serverEndPoint;

    [DefaultValue(39454)]
    public int Port { get; set; }
    [DefaultValue(100)]
    public int IncommingQueueSize { get; set; }
    [DefaultValue(512)]
    public int BufferSize { get; set; }

    //Listeners to hold event when something happens.

    public static void Main(string[] args)
    {
        TTSServer server = new TTSServer(39454, 100, 512);
        server.Start();
    }

    public TTSServer(int port, int queueSize, int bufferSize)
    {
        Port = port;
        IncommingQueueSize = queueSize;
        BufferSize = bufferSize;
    }


    public void Start()
    {
        Console.WriteLine("Starting TTS Server (Port: {0}, QueueSize: {1}, BufferSize: {2})", Port, IncommingQueueSize, BufferSize);
        m_serverEndPoint = new IPEndPoint(IPAddress.Any, Port);
        m_serverSocket = new Socket(m_serverEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
        Console.WriteLine("Binding ({0})", m_serverEndPoint.ToString());
        m_serverSocket.Bind(m_serverEndPoint);
        Console.WriteLine("Listening");
        m_serverSocket.Listen(IncommingQueueSize);

        StartAccept(null);
    }

    public void Stop()
    {

    }

    /// <summary>
    /// Receive a incomming connection attemp in an asynchronous way.
    /// </summary>
    /// <param name="socketEvent">If is null, a new object is created, else, it'll be used for cache friendly reason.</param>
    private void StartAccept(SocketAsyncEventArgs socketEvent)
    {
        //If received reference points to null, let's create a new object.
        if (socketEvent == null)
        {
            Console.WriteLine("Accepting new connections...");
            socketEvent = new SocketAsyncEventArgs();
            socketEvent.Completed += AcceptCompleted; //Add a callback on completed accept incomming connections attemp.
        }
        else
        {
            //Clear current incomming connection socket, so object may be reused.
            socketEvent.AcceptSocket = null;
        }

        //If there is an incomming connection pending(pooled), this method will receive the connection in a async way (witch returns true) 
        //and will call SocketAsyncEventArgs.Completed callback when done. 
        //Else, it waits for a new connection, returns false, and don't won't SocketAsyncEventArgs.Completed callback when done, so we have to 
        //call it manually.
        bool async = true;

        //When I debug this code, async receive true from AcceptAsync.
        async = m_serverSocket.AcceptAsync(socketEvent);

        if (!async)
        {
            AcceptCompleted(this, socketEvent);
        }
    }

    /// <summary>
    /// Handles a incomming connection after it's fully received. This function will do the business logic for incomming connections and prepare
    /// to receive data.
    /// </summary>
    /// <param name="sender">Object who posted this function</param>
    /// <param name="socketEvent">Information of the incomming connection</param>
    private void AcceptCompleted(object sender, SocketAsyncEventArgs socketEvent)
    {
        Connection connection = new Connection(this, socketEvent.AcceptSocket, BufferSize);

        SocketAsyncEventArgs connectedSocketEvent = new SocketAsyncEventArgs();
        connectedSocketEvent.UserToken = connection;

        //Add a receive callback, to be called whenever the Receive function is finished.
        connectedSocketEvent.Completed += FlushBuffer;
        ReceiveData(connectedSocketEvent);

        //Accept next incomming connection.
        StartAccept(socketEvent);
    }

不知道為什么,即使AcceptAsync()方法返回trueAcceptCompleted也永遠不會被觸發。

似乎根本原因是默認緩沖:

可選地,可以提供緩沖器 ,其中在ConnectAsync方法成功之后在套接字上接收初始數據塊。 在這種情況下, SocketAsyncEventArgs.Buffer屬性需要被設置為包含該數據的緩沖器以接收和SocketAsyncEventArgs.Count屬性需要被設置為數據的字節的最大數目,以接收在所述緩沖器中。 可以使用SocketAsyncEventArgs.SetBuffer方法設置這些屬性。 傳入的部分緩沖區將在內部使用,供底層Winsock AcceptEx調用使用。 這意味着返回的數據量始終小於提供的System.Net.Sockets.SocketAsyncEventArgs實例上的SocketAsyncEventArgs.Count屬性的值。 內部使用的緩沖區數量取決於套接字的地址族。 所需的最小緩沖區大小為288個字節。 如果指定了更大的緩沖區大小,那么Socket將期望除Winsock AcceptEx調用接收的地址數據之外的一些額外數據,並將等待直到收到這些額外數據。 如果發生超時,則重置連接。 因此,如果預期特定數量的額外數據,則應將緩沖區大小設置為最小緩沖區大小加上此數量。

- Socket.AcceptAsync方法(SocketAsyncEventArgs),MSDN

假設不需要緩沖,禁用緩沖似乎是一個解決方案

SocketAsyncEventArgs args = ...;
args.SetBuffer(null, 0, 0);

編輯:在您的示例中的server.Start之后放置阻塞代碼行。

一時興起你會考慮換一行。

改變這個

socketEvent.Completed += AcceptCompleted; //Add a callback on completed accept incomming connections attemp. 

對此

socketEvent.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptCompleted); //Add a callback on completed accept incomming connections attemp.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM