Асинхронные сокеты всегда читают 0 байтов при отправке большого количества пакетов одновременно - PullRequest
2 голосов
/ 26 февраля 2012

Я исследовал это много часов и попробовал много разных вещей, чтобы понять это, но я застрял.Я пытаюсь создать асинхронный сокет, к которому будет доступ из нескольких потоков.Это имеет тенденцию работать нормально, пока у меня нет нескольких потоков, которые пытаются отправить близко к одному и тому же времени.При отправке нет ошибок, единственная проблема - когда EndReceive () пытается прочитать данные, он возвращает 0 байтов и ошибка сокета - «success».Я читал, что это означает, что это было изящное завершение работы, и у сервера больше нет данных для отправки, но тип сервера, к которому я подключаюсь, никогда не должен был отправлять все данные, это не тот тип сервера.Всегда должны быть данные, которые нужно прочитать, и они не должны закрываться.

Потоки, которые я использовал для отправки, где несколько System.Threading.Timers, но я читал, что они не совсем потокобезопасны.Я также пытался с System.Timers.Timer, но это был тот же результат.Я также пытался использовать только 1 поток для отправки большого количества пакетов одновременно, спящих в течение 100 мс между ними, и я получу тот же результат.

Для получения кода ниже.Для отправки я попробовал просто обычную блокировку Send, а также BeginSend, и это не имело значения.

void Receive()
    {
        try
        {
            if (_datacb == null)
                _datacb = new AsyncCallback(OnRecvData);

            byte[] buffer = new byte[FBufferSize];
            FSocket.BeginReceive(buffer, 0, FBufferSize, SocketFlags.None, _datacb, buffer);
        }
        catch (SocketException ex)
        {
            OnSocketError(ex);
        }
    }

    void OnRecvData(IAsyncResult ar)
    {
        try
        {
            SocketError err;
            int bytesRead = FSocket.EndReceive(ar, out err);

            if (bytesRead == 0)
            {
                throw new SocketException();
            }

            byte[] buffer = ar.AsyncState as byte[];
            FReceived.AddBytes(buffer, bytesRead);
            ByteBuffer message = FReceived.GetNextMessage();
            while (message != null)
            {
                Process(message);
                message = FReceived.GetNextMessage();
            }
        }
        catch (SocketException ex)
        {
            OnSocketError(ex);
        }

        Receive();
    }

Я думал, что это может быть сервер, который просто отключает сокет для слишком быстрой отправки, но у меня фактически есть блокирующая версия, которую я сделал в VS2008, и я смог использовать 50 различных таймеров, которые отправляли 1 пакет каждую секунду, и я не был отключен.Я попробовал почти тот же код в VS2010, и bytesRead был 0. Я также пробовал код сокета asyc в VS2008 просто чтобы посмотреть, происходит ли что-то странное, но в VS2008 он все еще дает мне bytesRead == 0. Для кода блокировки, который не 'Отключите меня:

tcpClient = new TcpClient(host, 443);
networkStream = tcpClient.GetStream();
mReader = new BinaryReader(networkStream);
mWriter = new BinaryWriter(networkStream);
receiveMessage = new Thread(new ThreadStart(ReceiveMessages));
receiveMessage.Start();

private void ReceiveMessages()
    {
        while (true)
        {
            if ((tcpClient != null) && (tcpClient.Connected))
            {
                if (tcpClient.Available >= 4)
                {
                    if (!isConnected) isConnected = true;

                    try
                    {
                        ByteBuffer message = new ByteBuffer();
                        message.AddBytes(mReader.ReadBytes(4));
                        int mSize = message.ReadInt();
                        message.AddBytes(mReader.ReadBytes(mSize - 4));
                        processor.Process(message);
                    }
                    catch (Exception ex)
                    {
                        Print("Recv Msg: " + ex.Message);
                    }
                }

                Thread.Sleep(1);
            }
}

Отправка:

mWriter.Write(send);
mWriter.Flush();

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

Ответы [ 3 ]

0 голосов
/ 27 февраля 2012

Очевидно, это не имело никакого отношения к фактической отправке, а скорее к данным, которые отправлялись.Сервер ожидал отправки определенного отпечатка пальца с каждым пакетом, что является типом шифрования.Каждый раз, когда вы получаете отпечаток пальца, вы должны настроить значение.Я выполнял блокировку потока при фактической отправке, но его нужно было заблокировать в той части, где он также вычислял отпечаток пальца.Некоторые пакеты были отправлены с неправильными отпечатками пальцев, потому что они были рассчитаны не по порядку, поэтому сервер отключился таким образом.

Возможно, мне следовало упомянуть об этом в моем вопросе, но я не думал, что это актуально.

0 голосов
/ 27 февраля 2012

Сокет может читать 0 байтов.Это нормально.Вам нужно читать снова, пока не получите полный пакет данных .Это распространенная ошибка, когда люди ожидают, что сокет будет считывать столько же байтов, сколько сервер отправил.Да, это часто тренировки.Но на самом деле данные могут быть фрагментированы, и вы получите только куски данных.Итак, ваш код должен быть готов к сборке фрагментов данных из сокета, потому что фрагментация данных неизбежна.

0 голосов
/ 26 февраля 2012

Розетки не являются поточно-безопасными. Смотри здесь

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

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