Сокет удушается через определенное время - PullRequest
1 голос
/ 25 декабря 2011

Я создаю сетевую библиотеку на C #, которую я могу использовать в любом приложении, и как часть этой библиотеки у меня есть настройка клиент / сервер TCP.Эта настройка отлично работает практически в любой ситуации;он подключается, отправляет / получает данные и безошибочно отключается при минимальных и средних нагрузках.Однако, когда я отправляю большие объемы данных с клиента на сервер, клиентский сокет работает в течение различного времени (иногда короткого, иногда длительного), а затем просто отказывается отправлять данные некоторое время.В частности, моя скорость передачи данных варьируется от 550-750 Кбит / с до 0 Кбит / с, и снова используется в течение различного периода времени.Затем сокет начнет отправку снова в течение очень короткого времени и снова будет «задушен».Во время регулирования я предполагал, что сокет был отключен, потому что я ничего не мог отправить, но Опрос возвращает, что сокет подключен с использованием следующего кода:


public bool IsConnected(Socket socket)
{
     try
     {
         return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
     }
     catch (SocketException) { return false; }
}

Я только что взял урок по сети в моем колледжепоэтому я начал думать о механизмах контроля перегрузки и управления потоком в TCP, но мне кажется, что ни один из них не вызовет этой проблемы;управление перегрузкой только замедляет скорость передачи данных, и полный буфер на стороне получателя не будет работать почти столько же времени, сколько я получаю со скоростью передачи данных 0 кбит / с.Симптом, по-видимому, указывает либо на некоторый тип сильного удушения данных, либо на массовое отбрасывание пакетов.

Мой вопрос таков: Кто-нибудь имеет представление о том, что может вызвать «удушение» этих данных, из-за отсутствия лучшего термина?Кроме того, возможно ли, что отправляемые мной пакеты идут дальше, чем просто мой маршрутизатор, даже если они адресованы узлу в той же подсети?

Редактировать: Просто так понятно, причина, по которой яЯ пытаюсь решить эту проблему, потому что я хочу отправлять файлы по TCP с максимально возможной скоростью передачи данных.Я понимаю, что UDP также можно использовать, и я также буду принимать решение, используя его, но сначала я хочу, чтобы TCP работал.

Специальная информация:

Я использую блокировку чтения /операции записи, а сервер многопоточный.Клиент также работает в своем собственном потоке.Я тестирую в своей локальной подсети, пропуская все пакеты через мой маршрутизатор, который должен иметь пропускную способность 54 Мбит / с.Размер каждого пакета составляет 8 КБ, и максимум будет отправляться 1000 раз в секунду (поток отправки спит 1 мс), но, очевидно, не достигает этой скорости.Уменьшение размера пакетов для снижения скорости передачи данных приводит к исчезновению регулирования.Windows 7 машин, 1 сервер, 1 клиент.Операция отправки всегда завершается, это ошибка при получении.

Операция отправки приведена ниже:


//get a copy of all the packets currently in the queue
                    IPacket[] toSend;
                    lock (packetQueues[c])
                    {
                        if (packetQueues[c].Count > SEND_MAX)
                        {
                            toSend = packetQueues[c].GetRange(0, SEND_MAX).ToArray();
                            packetQueues[c].RemoveRange(0, SEND_MAX);
                        }
                        else
                        {
                            toSend = packetQueues[c].ToArray();
                            packetQueues[c].RemoveRange(0, toSend.Length);
                        }
                    }
                    if (toSend != null && toSend.Length > 0)
                    { //write the packets to the network stream
                        try
                        {
                            writer.Write(toSend.Length);
                        }
                        catch (Exception e)
                        {
                            Logger.Log(e);
                            if (showErrorMessages)
                                MessageBox.Show("Client " + (int)c + ": " + e, "Error", MessageBoxButtons.OK);
                        }
                        for (int i = 0; i < toSend.Length; i++)
                        {
                            try
                            {
                                toSend[i].Write(writer);
                                if (onSend != null)
                                {
                                    object[] args = new object[2];
                                    args[0] = c;
                                    args[1] = toSend[i];
                                    onSend(args);
                                }
                            }
                            catch (Exception e)
                            {
                                Logger.Log(e);
                                if (showErrorMessages)
                                    MessageBox.Show("Client " + (int)c + ": " + e, "Error", MessageBoxButtons.OK);
                            }
                        }
                    }

И вот код получения:


try
                    { 
                        //default buffer size of a TcpClient is 8192 bytes, or 2048 characters
                        if (client.Available > 0)
                        {
                            int numPackets = reader.ReadInt32();
                            for (int i = 0; i < numPackets; i++)
                            {
                                readPacket.Clear();
                                readPacket.Read(reader);
                                if (owner != null)
                                {
                                    owner.AcceptPacket(readPacket, c); //application handles null packets itself.
                                    if (onReceive != null)
                                    {
                                        object[] args = new object[2];
                                        args[0] = c;
                                        args[1] = readPacket;
                                        onReceive(args);
                                    }
                                }
                            }
                            timestamps[c] = TimeManager.GetCurrentMilliseconds();
                        }
                        else
                        {
                            double now = TimeManager.GetCurrentMilliseconds();
                            if (now - timestamps[c] >= timeToDisconnect)
                            { //if timestamp is old enough, check for connection.
                                connected[c] = IsConnected(client.Client);
                                if (!connected[c])
                                {
                                    netStream.Close();
                                    clients[c].Close();
                                    numConnections--;
                                    if (onTimeout != null) onTimeout(c);
                                }
                                else
                                {
                                    timestamps[c] = now;
                                }
                            }
                        }

                    }
                    catch (Exception s)
                    {
                        Logger.Log(s);
                        if (showErrorMessages)
                            MessageBox.Show("Client " + (int)c + ": " + s, "Error", MessageBoxButtons.OK);
                    }

Пакет отправки / получения:


public void Write(BinaryWriter w)
        {
            w.Write(command); //byte
            w.Write(data.Type); //short
            w.Write(data.Data.Length); //int
            w.Write(data.Data); //byte array
            w.Flush();
        }

        /// <summary>
        /// Reads a command packet from data off a network stream.
        /// </summary>
        /// <param name="r">The stream reader.</param>
        public void Read(BinaryReader r)
        {
            command = r.ReadByte();
            short dataType = r.ReadInt16();
            int dataSize = r.ReadInt32();
            byte[] bytes = r.ReadBytes(dataSize);
            data = new PortableObject(dataType, bytes);
        } 

Полный цикл связи с сервером:


public void Communicate(object cl)
        {
            int c = (int)cl;
            timestamps[c] = TimeManager.GetCurrentMilliseconds();
            try
            {
                //Console.Out.WriteLine("Thread " + Thread.CurrentThread.ManagedThreadId + " has started up. c = " + (int)c);

                TcpClient client = clients[c];
                client.ReceiveTimeout = 100;

                NetworkStream netStream = client.GetStream();
                BinaryReader reader = new BinaryReader(netStream);
                BinaryWriter writer = new BinaryWriter(netStream);

                while (client != null && connected[c])
                {
                    #region Receive
                    try
                    { 
                        //default buffer size of a TcpClient is 8192 bytes, or 2048 characters
                        if (client.Available > 0)
                        {
                            int numPackets = reader.ReadInt32();
                            for (int i = 0; i < numPackets; i++)
                            {
                                readPacket.Clear();
                                readPacket.Read(reader);
                                if (owner != null)
                                {
                                    owner.AcceptPacket(readPacket, c); //application handles null packets itself.
                                    if (onReceive != null)
                                    {
                                        object[] args = new object[2];
                                        args[0] = c;
                                        args[1] = readPacket;
                                        onReceive(args);
                                    }
                                }
                            }
                            timestamps[c] = TimeManager.GetCurrentMilliseconds();
                        }
                        else
                        {
                            double now = TimeManager.GetCurrentMilliseconds();
                            if (now - timestamps[c] >= timeToDisconnect)
                            { //if timestamp is old enough, check for connection.
                                connected[c] = IsConnected(client.Client);
                                if (!connected[c])
                                {
                                    netStream.Close();
                                    clients[c].Close();
                                    numConnections--;
                                    if (onTimeout != null) onTimeout(c);
                                }
                                else
                                {
                                    timestamps[c] = now;
                                }
                            }
                        }

                    }
                    catch (Exception s)
                    {
                        Logger.Log(s);
                        if (showErrorMessages)
                            MessageBox.Show("Client " + (int)c + ": " + s, "Error", MessageBoxButtons.OK);
                    }
                    #endregion

                    Thread.Sleep(threadLatency);

                    #region Send
                    //get a copy of all the packets currently in the queue
                    IPacket[] toSend;
                    lock (packetQueues[c])
                    {
                        if (packetQueues[c].Count > SEND_MAX)
                        {
                            toSend = packetQueues[c].GetRange(0, SEND_MAX).ToArray();
                            packetQueues[c].RemoveRange(0, SEND_MAX);
                        }
                        else
                        {
                            toSend = packetQueues[c].ToArray();
                            packetQueues[c].RemoveRange(0, toSend.Length);
                        }
                    }
                    if (toSend != null && toSend.Length > 0)
                    { //write the packets to the network stream
                        try
                        {
                            writer.Write(toSend.Length);
                        }
                        catch (Exception e)
                        {
                            Logger.Log(e);
                            if (showErrorMessages)
                                MessageBox.Show("Client " + (int)c + ": " + e, "Error", MessageBoxButtons.OK);
                        }
                        for (int i = 0; i < toSend.Length; i++)
                        {
                            try
                            {
                                toSend[i].Write(writer);
                                if (onSend != null)
                                {
                                    object[] args = new object[2];
                                    args[0] = c;
                                    args[1] = toSend[i];
                                    onSend(args);
                                }
                            }
                            catch (Exception e)
                            {
                                Logger.Log(e);
                                if (showErrorMessages)
                                    MessageBox.Show("Client " + (int)c + ": " + e, "Error", MessageBoxButtons.OK);
                            }
                        }
                    }
                    #endregion
                }
            }
            catch (ThreadAbortException tae) 
            { 
                Logger.Log(tae); 
                MessageBox.Show("Thread " + (int)cl + " was aborted.", "Error", MessageBoxButtons.OK); 
            }
        }   

Ответы [ 4 ]

2 голосов
/ 25 декабря 2011

Это, вероятно, ваш код, но нам трудно сказать, что он неполный.

Я написал свой собственный набор рекомендаций в .NET TCP / IP FAQ -после многолетнего опыта работы с TCP / IP.Я рекомендую вам начать с этого.

PS Я резервирую термин "пакет" для пакетов на проводе.Приложение TCP не контролирует пакеты.Я использую термин «сообщение» для сообщений уровня протокола приложения.Я думаю, что это уменьшает путаницу, особенно для новичков.

1 голос
/ 25 декабря 2011

Не посмотрел внимательно на ваши фрагменты кода, но я вижу, что у вас там есть выделение - вы проверяли, какое давление вы оказываете на сборщик мусора?

PS: (sending thread sleeps 1 ms) - имейте в виду, что Sleep () без timeBeginPeriod () не получит разрешение 1 мс - возможно, ближе к 10-20 мс в зависимости от версии Windows и аппаратного обеспечения.

1 голос
/ 25 декабря 2011

Если вы пытаетесь создать

сетевая библиотека на C #, которую я могу использовать в любом приложении

Вы знали о каких-либо существующих библиотеках с открытым исходным кодом? networkComms.net , возможно, хорошее начало. Если бы вы могли воссоздать ту же проблему с этим, я был бы очень удивлен. Я лично использовал его для поддержки более 1000 одновременных подключений, каждое из которых отправляло около 10 пакетов в секунду. В противном случае, если вы хотите продолжать использовать свой код, возможно, взглянув на source networkComms.net, вы сможете указать, где вы можете ошибаться.

0 голосов
/ 25 декабря 2011

Не совсем знаю о C #, и этот код неполон. Если я правильно понял, тогда

readPacket.Read(reader); 

прочитает все, что доступно, и ваш конец приемника для цикла будет опрокинут. Где вы проверяете прочитанное количество байтов?

В любом случае, хороший способ проверить, что происходит на уровне TCP и ниже, это wireshark

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