发送时无法立即完成非阻塞套接字操作

本文关键字:操作 套接字 | 更新日期: 2023-09-27 18:29:01

我正在为游戏编写一个服务器,我希望能够处理数千个并发用户。出于这个原因,我选择了非阻塞套接字,并使用了轮询方法。但是,我确实创建了多个线程来处理数据库和web调用,其中一些线程将向用户发送响应。在其中一个线程中,在发送时,我收到错误"非阻塞套接字操作无法立即完成"。是什么原因导致了这个问题?我想这是因为在调用send的同时发生了轮询。如果我使用beginAsync,是否需要停止此错误?我想过锁定套接字,但我不希望我的主线程因此而被阻塞。

发送时无法立即完成非阻塞套接字操作

我不知道您使用的是哪种非阻塞轮询套接字调用,但我建议您使用异步套接字调用(而不是Begin)。有关异步调用与Begin之间差异的更多信息,请参阅:What';BeginConnect和ConnectAsync之间的区别是什么?

异步调用会在操作系统级别上自动进行"轮询",这将比轮询效率高得多。事实上,它们使用IO完成端口,这可能是Windows上处理大量客户端连接/请求时速度最快、效率最高的端口。

就错误而言,我认为这是非阻塞套接字的正常操作,所以您只需要优雅地处理它。

更新

您的服务器可能应该这样做:

// Process the accept for the socket listener.
private void ProcessAccept(SocketAsyncEventArgs e)
{
    Socket s = e.AcceptSocket;
    if (s.Connected)
    {
        try
        {
            SocketAsyncEventArgs readEventArgs = this.readWritePool.Pop();
            if (readEventArgs != null)
            {
                // Get the socket for the accepted client connection and put it into the 
                // ReadEventArg object user token.
                readEventArgs.UserToken = new Token(s, this.bufferSize);
                Interlocked.Increment(ref this.numConnectedSockets);
                Console.WriteLine("Client connection accepted. 
            There are {0} clients connected to the server",
                    this.numConnectedSockets);
                if (!s.ReceiveAsync(readEventArgs))
                {
                    this.ProcessReceive(readEventArgs);
                }
            }
            else
            {
                Console.WriteLine("There are no more available sockets to allocate.");
            }
        }
        catch (SocketException ex)
        {
            Token token = e.UserToken as Token;
            Console.WriteLine("Error when processing data received from {0}:'r'n{1}", 
            token.Connection.RemoteEndPoint, ex.ToString());
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.ToString());
        }
        // Accept the next connection request.
        this.StartAccept(e);
    }
}

代码项目提供的代码示例:http://www.codeproject.com/Articles/22918/How-To-Use-the-SocketAsyncEventArgs-Class

当一个非阻塞套接字试图读取数据但没有找到时,你会得到错误:套接字想等待数据,但不能,因为它必须立即返回,这是非阻塞的。

我建议您切换到阻塞套接字,找出数据丢失的原因,进行相应调整,然后恢复到非阻塞套接字。或者,您可以处理错误并重试该操作。

我在发送数据时也收到了这个异常,刚刚找到了解决方案。

由于套接字的发送缓冲区已满,因此会出现异常。因为您试图通过非阻塞发送发送数据,所以会引发异常,让您知道必须通过阻塞发送发送。

一旦出现异常,数据就不会被发送,所以你必须重新发送。你的个人发送呼叫现在变成;

try
{
    m_socket.Send(buffer, bufferSize, SocketFlags.None);
}
catch (SocketException e)
{
    if(e.SocketErrorCode == WouldBlock)
    {
        m_socket.Blocking = true;
        m_socket.Send(buffer, bufferSize, SocketFlags.None);
        m_socket.Blocking = false;
    }
}

增加套接字的SendBufferSize也是一个好主意。默认情况下,我认为它是8kb。为了满足我的需要,我不得不将其增加到2MB,之后Send呼叫不再抛出该异常。

此异常过于普遍。根据MSDN,

如果收到SocketException,请使用SocketException.ErrorCode属性获取特定的错误代码。获得此代码后,请参阅MSDN库中的Windows Sockets版本2 API错误代码文档,以了解错误的详细说明。

套接字错误代码在这里。