C # TcpClient закрывает соединение на сервере при отправке мировой информации с исключением - PullRequest
3 голосов
/ 26 января 2012

Я пытаюсь отправить все плитки (которые называются Частицами, не путайте их с эффектами частиц клиента) в моем мире (массив 2D размером 500x350 в моем классе World) через NetworkStream, подключенный к моему Клиенту ина сервере и стороне клиента.

Код выглядит следующим образом:

public int ServerSendWorldData(NetworkStream networkStream, Point from, Point size, bool reportBytesSent, Networking.Client toClient)
    {
        int bytesOfAcknowledgementSent = 0;
        int totalBytesSent = 0;
        int tilesSentThisAcknowledgement = 0;
        int tilesToSendForThisAcknowledgement = 20;

        for (int x = from.X; x < size.X; x++)
        {
            for (int y = from.Y; y < size.Y; y++)
            {
                Thread.Sleep(0);

                //Handle acknowledgement if needed.
                if (tilesSentThisAcknowledgement >= tilesToSendForThisAcknowledgement)
                {
                    //Wait for Client acknowledgement.
                    bytesOfAcknowledgementSent += Networking.NetworkingHelp.SendMessageFromByte((byte)Networking.MessageType.ServerSendWorldMapDataWaitAcknowledgement, networkStream);

                    //Handle here.
                    if (networkStream.ReadByte() == (byte)Networking.MessageType.ClientAcknowledgeWorldMapData)
                        tilesSentThisAcknowledgement = 0;
                    else
                        throw new Exception("Client did not acknowledge data!");
                }

                if (world.worldParticles[x, y] != null)
                {
                    //Send Particle Data
                    totalBytesSent += Networking.NetworkingHelp.SendMessageFromByte((byte)Networking.MessageType.ServerSendWorldMapDataParticle, networkStream);
                    totalBytesSent += Networking.NetworkingHelp.SendMessageFromInt(x, networkStream);
                    totalBytesSent += Networking.NetworkingHelp.SendMessageFromInt(y, networkStream);
                    totalBytesSent += Networking.NetworkingHelp.SendMessageFromInt(world.worldParticles[x, y].ID, networkStream);
                    //totalBytesSent += Networking.NetworkingHelp.SendMessageFromInt(world.worldParticles[x, y].spriteIndex, networkStream);
                    totalBytesSent += Networking.NetworkingHelp.SendMessageFromBool(world.worldParticles[x, y].draw, networkStream);
                    totalBytesSent += Networking.NetworkingHelp.SendMessageFromBool(world.worldParticles[x, y].collisionWithEntities, networkStream);

                    tilesSentThisAcknowledgement++;
                }
            }
        }

        if (reportBytesSent)
        {
            Statistics.Console.WriteLine("Sent " + totalBytesSent + " bytes of World data to Client " + toClient.name + " and " + bytesOfAcknowledgementSent + " bytes of acknowledgement was exchanged!");
        }

        return totalBytesSent;
    }

Класс NetworkingHelp в основном просто networkStream.Write с помощью BitConverter, преобразующего данные, такие как целые числа в байты, или кодировкупреобразование данных, таких как строки, в байты в UTF8.

Код отправляет клиенту в общей сложности 0,76 МБ картографических данных.

При подключении к серверу у меня нет проблемы.используя localhost / IPAddress.Loopback, но проблема возникает при отправке данных Клиентам через Интернет - данные отправляются крайне медленно (вероятно, из-за размера файла, хотя я не уверен) и без бита кода «Обработка подтверждения»...

            //Handle acknowledgement if needed.
            if (tilesSentThisAcknowledgement >= tilesToSendForThisAcknowledgement)
            {
                //Wait for Client acknowledgement.
                bytesOfAcknowledgementSent += Networking.NetworkingHelp.SendMessageFromByte((byte)Networking.MessageType.ServerSendWorldMapDataWaitAcknowledgement, networkStream);

                //Handle here.
                if (networkStream.ReadByte() == (byte)Networking.MessageType.ClientAcknowledgeWorldMapData)
                    tilesSentThisAcknowledgement = 0;
                else
                    throw new Exception("Client did not acknowledge data!");
            }

... Клиент получает около 20 тайлов мира и выбрасывает«Невозможно записать данные в транспортное соединение: существующее соединение было принудительно закрыто удаленным хостом», это происходит потому, что Клиент получает 1-байтовое сообщение с байтом 0 в качестве содержимого.

Кажется,что пауза, создаваемая кодом «Подтверждение дескриптора», решает эту проблему, но делает передачу данных в мире намного медленнее.

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

        //WorldParticlesMapDataParticle
        else if (recievedMessage[0] == (byte)Networking.MessageType.ServerSendWorldMapDataParticle)
        {
            //Read the position of the new data.
            recievedMessageBytes = networkStream.Read(recievedMessage, 0, 4);
            int newParticleX = BitConverter.ToInt32(recievedMessage, 0);
            recievedMessageBytes = networkStream.Read(recievedMessage, 0, 4);
            int newParticleY = BitConverter.ToInt32(recievedMessage, 0);

            //Read the particle ParticleID.
            recievedMessageBytes = networkStream.Read(recievedMessage, 0, 4);
            int newParticleID = BitConverter.ToInt32(recievedMessage, 0);

            //Read the particle SpriteID.
            //recievedMessageBytes = networkStream.Read(recievedMessage, 0, 4);
            //int newSpriteID = BitConverter.ToInt32(recievedMessage, 0);

            //Read the particle draw.
            recievedMessageBytes = networkStream.Read(recievedMessage, 0, 1);
            bool newParticleDraw = BitConverter.ToBoolean(recievedMessage, 0);

            //Read the particle collision.
            recievedMessageBytes = networkStream.Read(recievedMessage, 0, 1);
            bool newParticleCollision = BitConverter.ToBoolean(recievedMessage, 0);

            //Set particle.
            try
            {
                world.worldParticles[newParticleX, newParticleY] = World.Particle.ParticleManager.particleArray[newParticleID];
                //world.worldParticles[newParticleX, newParticleY].spriteIndex = newSpriteID;
                world.worldParticles[newParticleX, newParticleY].draw = newParticleDraw;
                world.worldParticles[newParticleX, newParticleY].collisionWithEntities = newParticleCollision;
            }
            catch (Exception ex)
            {
                Statistics.Console.WriteLine("Server requested new Particle at " + newParticleX + "," + newParticleY + ", but Client failed to place it due to an exception : " + ex.Message);

                try
                {
                    world.worldParticles[newParticleX, newParticleY] = null;
                }
                catch
                { }
            }
        }

Ответы [ 3 ]

3 голосов
/ 26 января 2012

Вы игнорируете возвращаемое значение из NetworkStream.Read.Я думаю, вы предполагаете, что он всегда будет считывать количество байтов, которые вы просили его прочитать, если нет ошибки.На самом деле, указанное вами число байтов составляет всего максимум , которое он будет читать, и он не будет читать больше байтов, чем доступно в данный момент, когда вы его вызываете.

"Этот метод считывает данные впараметр буфера и возвращает количество успешно прочитанных байтов. Если данные для чтения недоступны, метод Read возвращает 0. Операция Read считывает столько данных, сколько доступно, вплоть до количества байтов, указанного размеромпараметр. Если удаленный хост завершает соединение и все доступные данные получены, метод Read завершается немедленно и возвращает ноль байтов. "- NetworkStream.Read

Вы сделали классическую ошибку, предполагая, что TCP каким-то образом «склеит» данные только потому, что вы отправили их за один вызов.На самом деле в TCP нет такого механизма.

1 голос
/ 31 января 2012

В дополнение к ответу Дэвида Шварца, было бы целесообразно прочитать FAQ по TCP / IP Стивена Клири *, особенно раздел создания фрейма сообщения

1 голос
/ 26 января 2012

вы отправляете одну частицу за раз, попробуйте создать большой массив того, что вы хотите отправить, и отправить его одним фрагментом

...