Неблокирующая операция сокета не может быть завершена немедленно при отправке - PullRequest
4 голосов
/ 23 марта 2012

Я пишу сервер для игры и хочу иметь возможность обрабатывать тысячи одновременно работающих пользователей.По этой причине я пошел с неблокирующими сокетами и использовал метод poll.Тем не менее, я создаю несколько потоков для обработки базы данных и веб-вызовов, и некоторые из этих потоков отправят ответ пользователю.В одном из этих потоков при отправке я получаю сообщение об ошибке «Операция с неблокирующим сокетом не может быть завершена немедленно».Что может вызвать эту проблему?Я полагаю, это потому, что опрос происходит одновременно с вызовом send.Если бы я использовал beginAsync, остановил бы эту ошибку?Я думал о блокировке сокета, но не хочу, чтобы мой главный поток блокировался для этого.

Ответы [ 4 ]

4 голосов
/ 23 марта 2012

Я не знаю, какие виды сокет-вызовов без блокировки вы используете, но я бы порекомендовал использовать асинхронные вызовы (вместо Begin ).Для получения дополнительной информации о разнице между асинхронными вызовами и Begin см. В чем разница между BeginConnect и ConnectAsync?

Асинхронные вызовы автоматически выполняют "опрос" в ОСуровень, который будет намного эффективнее , чем ваш опрос.На самом деле они используют порты завершения ввода-вывода, которые, вероятно, являются самой быстрой и эффективной из тех, которые вы можете использовать в 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

2 голосов
/ 23 марта 2012

Когда неблокирующий сокет пытается прочитать данные, но не находит ни одного, вы получаете эту ошибку: сокет хотел бы ждать данных, но не может, потому что он должен вернуться немедленно, будучи неблокирующим.

Я бы посоветовал вам переключиться на блокирующие сокеты, выяснить, почему отсутствуют данные, настроить соответствующим образом, а затем вернуться к неблокирующим.Или вы можете обработать ошибку и повторить операцию.

1 голос
/ 19 марта 2013

Я также получал это исключение при отправке данных и только что нашел решение.

Вы получаете исключение, потому что буфер отправки сокета заполнен.Поскольку вы пытаетесь отправить данные через неблокирующую отправку, возникает исключение, чтобы вы знали, что вы ДОЛЖНЫ отправить их через блокирующую отправку.

Данные не отправляются после возникновения исключения, поэтому вам необходимо отправить его повторно.Ваш индивидуальный вызов для отправки теперь становится:

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 сокета.По умолчанию я думаю, что это 8 КБ.Для моих нужд мне пришлось увеличить его до 2 МБ, и после этого при отправке вызова больше не возникало это исключение.

0 голосов
/ 16 июня 2015

Это исключение слишком общее. За MSDN,

Если вы получаете SocketException, используйте свойство SocketException.ErrorCode для получения конкретного кода ошибки. После получения этого кода см. Подробное описание ошибки в документации по коду ошибок Windows Socket версии 2 в библиотеке MSDN.

Коды ошибок сокетов здесь .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...