【问题标题】:Why Socket.AcceptAsync isn't firing SocketAsyncEventArgs Completed event?为什么 Socket.AcceptAsync 没有触发 SocketAsyncEventArgs Completed 事件?
【发布时间】:2015-09-27 17:26:34
【问题描述】:

我正在开发一个服务器应用程序,它将接收消息并做出响应。没有什么新东西。所以,实际上我正在关注this answerthis post,但我无法让AcceptAsync() 方法触发Completed 事件。在互联网上到处搜索,尝试了类似问题的解决方案,但似乎没有什么对我有用。

我也尝试从Task.Run() 拨打server.Start(),但没有成功。我知道服务器正在监听,因为我可以在 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);
    }

不知道为什么,AcceptCompleted 永远不会被触发,即使 AcceptAsync() 方法返回 true

【问题讨论】:

  • 这不是一个答案,但这个问题确实没有实际意义。您应该使用 APM 模式周围的 await 包装器来开发套接字应用程序。根本不要使用 socketeventargs。所有这些都是过时的。使用网络上现成的包装器。问题就会消失。也不是 99% 的套接字教程被破坏或被严重误导。
  • 你能推荐我一个吗?
  • 你只需要在 Socket 中添加 await 的东西。就是几十行代码。
  • 强调前面的评论。如果您可以看到它正在使用 netstat 进行侦听,则它应该在您 telnet 或任何其他连接时至少触发一次。但它每次连接只会触发一次。尝试同时连接第二个客户,看看我的意思。使用这种模式,是 userToken 中的 Connection 对象为每个消息事件执行每个连接。
  • @SqlSurfer 我想你误解了。无论连接如何,它都应该触发。即使没有连接,由于 IOCP 完成未决,它应该触发 SocketAsyncEventArgs.Completed

标签: c# sockets asyncsocket iocp


【解决方案1】:

似乎根本原因是默认缓冲:

可选地,可以提供一个缓冲区,在ConnectAsync 方法成功后,在该缓冲区中接收套接字上的初始数据块。在这种情况下,SocketAsyncEventArgs.Buffer 属性需要设置为包含要接收数据的缓冲区,SocketAsyncEventArgs.Count 属性需要设置为缓冲区中要接收的最大数据字节数。可以使用SocketAsyncEventArgs.SetBuffer 方法设置这些属性。传入的部分缓冲区将在内部使用,以供底层 Winsock AcceptEx 调用使用。这意味着返回的数据量将始终小于提供的System.Net.Sockets.SocketAsyncEventArgs 实例上的SocketAsyncEventArgs.Count 属性的值。内部使用的缓冲区数量根据套接字的地址族而有所不同。所需的最小缓冲区大小为 288 字节。如果指定了更大的缓冲区大小,则Socket 将期待一些额外的数据,而不是 Winsock AcceptEx 调用接收到的地址数据,并将等待直到接收到这些额外的数据。如果发生超时,则重置连接。因此,如果需要特定数量的额外数据,则应将缓冲区大小设置为最小缓冲区大小加上此数量。

——Socket.AcceptAsync Method (SocketAsyncEventArgs), MSDN.

假设不需要缓冲,禁用缓冲seems to be a solution

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

【讨论】:

    【解决方案2】:

    编辑:在您的示例中,在 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.
    

    【讨论】:

    • 这是我尝试过的第一件事,但没有任何区别。
    猜你喜欢
    • 1970-01-01
    • 2011-04-04
    • 1970-01-01
    • 1970-01-01
    • 2017-05-12
    • 2021-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多