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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9844263/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 10:52:17  来源:igfitidea点击:

A non-blocking socket operation could not be completed immediately on send

c#sockets

提问by Nikhil

I'm writing a server for a game, and I want to be able to handle thousands of concurrent users. For this reason, I went with non-blocking sockets and use the poll method. However, I do create multiple threads to handle database and web calls, and some of these threads will send a response to the user. In one of these threads, on send, I get the error "A non-blocking socket operation could not be completed immediately". What could cause this problem? I imagine it's because a poll is occurring at the same time as send is called. If I used beginAsync, would it take stop this error? I thought about locking the socket, but I don't want my main thread to be blocked for this.

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

采纳答案by Kiril

I don't know what kind of non-blocking-polling socket calls are you using, but I would recommend that you use the Async socket calls (instead of the Begin). For more information on the difference between Async calls vs Beginsee: What's the difference between BeginConnect and ConnectAsync?

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

The asynchronous calls automatically do "polling" on the OS level, which will be much more efficientthan your polling. As a matter of fact, they use IO completion ports, which are probably the fastest and most efficient thing you can use on Windows to handle a large amount of client connections/requests.

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

As far as the error, I would consider this to be the normal operation of non-blocking sockets, so you just have to handle it gracefully.

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

Update

更新

Your server should probably do something like this:

您的服务器可能应该执行以下操作:

// 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);
    }
}

Code sample courtesy of code project: http://www.codeproject.com/Articles/22918/How-To-Use-the-SocketAsyncEventArgs-Class

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

回答by Alex

When a non-blocking socket tries to read data but finds none you get that error: the socket would like to wait for data but can't because it has to return immediately, being non-blocking.

当非阻塞套接字尝试读取数据但未找到任何数据时,您会收到该错误:套接字想要等待数据但不能,因为它必须立即返回,是非阻塞的。

I'd suggest you switch to blocking sockets, find out why data is missing, adjust accordingly then revert to non-blocking ones. Or, you could handle the error and retry the operation.

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

回答by whalebiologist

I was also receiving this exception on sending data and just found the solution.

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

You get the exception because the socket's send buffer is full. Because you are trying to send the data via a non-blocking send, the exception is raised to let you know that you MUST send it via a blocking send.

您会收到异常,因为套接字的发送缓冲区已满。因为您试图通过非阻塞发送来发送数据,所以会引发异常以告知您必须通过阻塞发送来发送数据。

The data is not sent once the exception is raised, so you have to resend it. Your individual send call now becomes;

一旦引发异常,数据就不会发送,因此您必须重新发送它。您的个人发送呼叫现在变为;

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;
    }
}

It would also be a good idea to increase the socket's SendBufferSize. By default I think it is 8kb. For my needs I had to increase it to 2MB, and afterwards the Send call no longer threw that exception.

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

回答by Sherlock

This exception is too general. Per MSDN,

这个例外太笼统了。根据 MSDN,

If you receive a SocketException, use the SocketException.ErrorCodeproperty to obtain the specific error code. After you have obtained this code, refer to the Windows Sockets version 2 API error code documentation in the MSDN library for a detailed description of the error.

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

Sockets error codes are here.

套接字错误代码在这里