C # Сеть: сервер зависает после получения более 65535 байт - PullRequest
3 голосов
/ 07 ноября 2010

UPDATE: Из-за проблем с администраторами здесь, на Stackoverflow, я разместил очень урезанную версию той же проблемы на форуме MSDN. Этот текст ниже использовал MyNetworking.dll, но это не проблема. Вот очень узкая клиент-серверная проблема, и проблема точно такая же. Не стесняйтесь попробовать это =) http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/d3d33eb9-7dce-4313-929e-a8a63d0f1e03 / UPDATE

Итак, у меня странная ошибка.

Обычно у нас есть DLL, которая обрабатывает наши сети. Давайте назовем это MyNetworking.dll. Мы используем его везде на наших серверах и клиентах и ​​делаем это в течение 5 лет. До сих пор у меня с этим не было проблем.

У меня есть «XMLPoller», который читает XML из базы данных MySQL, сериализует его в массив byte [] и отправляет его по сети. Эти конкретные XML-сообщения имеют 627 байт в сериализованной форме.

XMLPoller подключается к порту на «удаленном сервере» (который является локальным) и отправляет пакеты по одному за раз. Точно в пакете NBR 105 соединение закрыто. 104 пакета отправляются из XMLPoller и принимаются сервером. 104 х 627 = 65208 байт. Но пакет 105, когда общее количество отправленных байтов было бы 65835, соединение закрывается с этой ошибкой:

System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
       at System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult)
       at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)

Это ошибка на сервере . Однако я прошел через XMLPoller (клиент) и вижу, когда отправляются последние 627 байт (таким образом, отправка до 65835 байт), и я не вижу ошибок на клиенте, он проходит отправку без проблем.

ОБНОВЛЕНИЕ 20:15 ШВЕДСКОЕ ВРЕМЯ

Я также получаю эту ошибку в клиенте, когда немного больше отлаживаю:

Unable to read data from the transport connection: An established connection was aborted by the software in your host machine.

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

/ EndUpdate

Мне кажется, что Сервер никогда не получает его, получая ошибку выше. Сервер закрывает соединение из-за того, что что-то происходит на Клиенте. Однако ошибка на клиенте находится в TCPInput; Поток чтения данных по какой-то причине мертв?

Я ничего не буферизирую в MyNetworking.dll.

Когда я получаю новое соединение на сокете (на сервере), я делаю этот код:

public void setConnected(Socket thisClient)
{
    NetworkStream stream = new NetworkStream(thisClient);
 socket = thisClient;
 output = new TCPOutput(stream, outputHandler,this);
 remoteIP = this.socket.RemoteEndPoint.ToString();
 changeState(State.Connected);
    try
    {
        stream.BeginRead(inputBuffer, 0, 5000, new AsyncCallback(OnDataReceived), null);
    }
    catch (Exception e)
    {
        this.disconnect();
    }
}

и затем метод OnDataReceived (где данные фактически получены):

public void OnDataReceived(IAsyncResult asyn)
        {
            int nbrRead = 0;
            byte[] tmp = null;
            try
            {
                nbrRead = stream.EndRead(asyn);
                tmp = new byte[nbrRead];
            }
            catch (Exception e)
            {
                // *** HERE IS WHERE THE EXCEPTION IS CAUGHT ***
                System.Diagnostics.Debugger.Log(0, "Bla1", e.ToString());
                this.disconnect();
            }
            if (nbrRead > 0)
            {
                try
                {
                    Array.Copy(inputBuffer, 0, tmp, 0, nbrRead);
                }
                catch(Exception e)
                {
                    System.Diagnostics.Debugger.Log(0, "Bla2", e.ToString());
                    this.disconnect();
                }
                preProcessMessage(tmp);
                try
                {
                    stream.BeginRead(inputBuffer, 0, 5000, new AsyncCallback(OnDataReceived), new object());
                }
                catch(Exception e)
                {
                    System.Diagnostics.Debugger.Log(0, "Bla3", e.ToString());
                    this.disconnect();
                }
            }
            else
                this.disconnect();

        }

Прямо сейчас я вроде не в курсе, что происходит ... Есть идеи?

ОБНОВЛЕНИЕ 1:

Код клиента для отправки данных:

public bool sendData(byte[] data)
        {
            if(this.state == State.Connected)
            {
                if (data != null && data.Length > 0)
                {
                    try
                    {
                        //data = Crypto.Encrypt("a1s2d3", data);
                        outputStream.Write(data, 0, data.Length);
                    }
                    catch(Exception e)
                    {
                        System.Diagnostics.Debug.WriteLine("ClientHandler.sendData> " + e.ToString());
                    }
                    //parent.outDataLog(data.Length);
                }
            }
            return true;
        }

Обновление 2

Я попытался выполнить очистку исходящего потока от клиента - без эффекта :

public bool sendData(byte[] data)
{
    if(this.state == State.Connected)
    {
        if (data != null && data.Length > 0)
        {
            try
            {
                //data = Crypto.Encrypt("a1s2d3", data);
                outputStream.Write(data, 0, data.Length);
                outputStream.Flush();
            }
            catch(Exception e)
            {
                System.Diagnostics.Debug.WriteLine("ClientHandler.sendData> " + e.ToString());
            }
            //parent.outDataLog(data.Length);
        }
    }
    return true;
}

ОБНОВЛЕНИЕ 3: размещение большего количества кода согласно запросу

Этот код старый, а не симпатичный в мире, я знаю. Но это работает очень хорошо в течение 5 лет, поэтому =)

ClientHandler.cs (что фактический клиент использует для отправки и т. Д.)

using System;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace tWorks.tNetworking.tNetworkingCF
{
    /// <summary>
    /// Summary description for connectionHandler.
    /// </summary>
    public class ClientHandler
    {
        #region Fields (17) 

        string address;
        Connector connector;
        DataHandler dataHandler;
        int id;
        TCPInput input;
        int interval;
        string localAddress;
        IPEndPoint localPoint;
        int localPort;
        NetworkStream outputStream;
        public TTCPClientInterface parent;
        int port;
        tWorks.tNetworking.Protocol.Protocol protocol;
        bool reconnect;
        string remoteIP;
        Socket socket;
        public State state;

        #endregion Fields 

        #region Enums (1) 

        public enum State {Disconnected,Connecting,Connected}

        #endregion Enums 

        #region Constructors (4) 

        public ClientHandler(int id, TTCPClientInterface parent, Socket socket, tWorks.tNetworking.Protocol.Protocol protocol)
        {
            this.id=id;
            this.parent = parent;
            this.protocol = protocol;
            dataHandler = new DataHandler(protocol, this);
            setConnected(socket);
        }

        public ClientHandler(int id, TTCPClientInterface parent, Protocol.Protocol protocol)
        {
            this.id=id;
            this.parent = parent;
            this.protocol = protocol;
            dataHandler = new DataHandler(protocol, this);
            state = State.Disconnected;
        }

        public ClientHandler(int id, TTCPClientInterface parent, Socket socket)
        {
            this.id=id;
            this.parent = parent;
            setConnected(socket);
        }

        public ClientHandler(int id, TTCPClientInterface parent)
        {
            this.id=id;
            this.parent = parent;
            this.protocol = null;
            changeState(State.Disconnected);
        }

        #endregion Constructors 

        #region Delegates and Events (4) 

        // Delegates (2) 

        public delegate void ConnectionLostDelegate(string message);
        public delegate void exceptionDelegate(Exception ex);
        // Events (2) 

        public event exceptionDelegate ConnectionFailed;

        public event ConnectionLostDelegate ConnectionLostEvent;

        #endregion Delegates and Events 

        #region Methods (17) 

        // Public Methods (16) 

        public void connect(string address, int port, int retryInterval, bool reestablish)
        {
            System.Random rand = new Random();
            localPort = rand.Next(40000, 60000);
            IPAddress localIP = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; // new IPAddress(Dns.GetHostByName(Dns.GetHostName()).AddressList[0].Address);
            connect(address, port, retryInterval, reestablish, localIP.ToString(), localPort);            
        }

        /// <summary>
        /// Will connect to the address and port specified. If connection failed a new attempt will be made according to the Interval parameter. 
        /// If connection is lost attempts to reastablish it will be made if Reestablish is set to true.
        /// </summary>
        /// <param name="address"></param>
        /// <param name="port"></param>
        /// <param name="retryInterval"></param>
        /// <param name="reestablish"></param>
        public void connect(string address, int port, int retryInterval, bool reestablish, string localAddress, int localPort)
        {
            this.reconnect = reestablish;
            this.address = address;
            this.port = port;
            this.interval = retryInterval;
            this.localAddress = localAddress;
            this.localPort = localPort;
            changeState(State.Connecting);
            connector = new Connector(address, port, this, interval, localPoint, reestablish);
            connector.Connect();
        }

        public void disconnect()
        {
            reconnect = false;
            if (connector != null)
            {
                connector.stopConnecting();
            }
            setDisconnected();
        }

        public void dispose()
        {

        }

        public void failedConnect(Exception e)
        {
            if (ConnectionFailed != null)
                ConnectionFailed(e);
        }

        public int getID()
        {
            return this.id;
        }

        public string getIP()
        {
            return remoteIP;
        }

        public bool isConnected()
        {
            return this.state == State.Connected;
        }

        public void outDataLog(int nbrBytes)
        {
            parent.outDataLog(nbrBytes, id);
        }

        public void preProcessMessage(byte[] data)
        {
            //data = Crypto.Decrypt("a1s2d3", data);
            if(protocol != null)
                dataHandler.addData(data);
            else
                processMessage(data);
        }

        public void processMessage(byte[] data)
        {

            parent.processMessage(data,this);
        }

        public bool sendData(byte[] data)
        {
            if(this.state == State.Connected)
            {
                if (data != null && data.Length > 0)
                {
                    try
                    {
                        //data = Crypto.Encrypt("a1s2d3", data);
                        outputStream.Write(data, 0, data.Length);
                        outputStream.Flush();
                    }
                    catch(Exception e)
                    {
                        System.Diagnostics.Debug.WriteLine("ClientHandler.sendData> " + e.ToString());
                    }
                    //parent.outDataLog(data.Length);
                }
            }
            return true;
        }

        public void setConnected(Socket thisClient)
        {
            socket = thisClient;
            outputStream = new NetworkStream(thisClient);
            input = new TCPInput(outputStream, this);
            remoteIP = this.socket.RemoteEndPoint.ToString();
            changeState(State.Connected);
        }

        public void setDisconnected()
        {
            try
            {
                if (this.state == State.Connected)
                {
                    changeState(State.Disconnected);
                    //socket.Shutdown(SocketShutdown.Both);
                    socket.Close();
                }
            }
            catch { }
            if (reconnect)
                this.connect(address, port, interval, true, localAddress, localPort);
        }

        public void stopConnect()
        {
            connector.stopConnecting();
            changeState(State.Disconnected);
        }

        public override string ToString()
        {
            string returnString = "(D)";
            if(this.state == State.Connected)
                returnString = this.getIP();
            return returnString;
        }
        // Private Methods (1) 

        private void changeState(State state)
        {
            if (this.state == State.Connected && state == State.Disconnected)
            {
                if (ConnectionLostEvent != null)
                    ConnectionLostEvent("Uppkoppling bröts.");
            }
            this.state = state;
            parent.connStateChange(this);
        }

        #endregion Methods 
    }
}

Это TCPInput.cs, который прослушивает входящие данные и передает их ClientHandler (см. Выше):

using System;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace tWorks.tNetworking.tNetworkingCF
{
    public class TCPInput
    {
        NetworkStream stream;
        ClientHandler client;

        public TCPInput(NetworkStream nS, ClientHandler client)
        {
            stream = nS;
            this.client = client;
            Thread t = new Thread(new ThreadStart(run));
            t.IsBackground = true;
            t.Name = "TCPInput";
            t.Start();
        }

        public void run()
        {
            bool continueRead = true;
            byte[] readBuffer = new byte[32768];
            byte[] receivedBuffer = null;

            int nbrBytesRead = 0;
            int receivedBufferPos = 0;
            while(continueRead)
            {
                try
                {
                    nbrBytesRead = 0;
                    nbrBytesRead = stream.Read(readBuffer, 0, 10000);
                    receivedBuffer = new byte[nbrBytesRead];
                }
                catch (Exception e)
                {
                    System.Diagnostics.Debug.WriteLine("TCPInput> Exception when stream.Read: " + e.ToString());
                    continueRead = false;
                }
                if(nbrBytesRead > 0)
                {
                    try
                    {
                        Array.Copy(readBuffer, 0, receivedBuffer, receivedBufferPos, nbrBytesRead);
                    }
                    catch (Exception e)
                    {
                        System.Diagnostics.Debug.WriteLine("TCPInput> Exception when Array.Copy: " + e.ToString());
                        continueRead = false; 
                    }
                    client.preProcessMessage(receivedBuffer);
                }
                else
                {
                                // *** I can break here, the nbrOfBytes read is 0 when this whole thing explodes =)
                    System.Diagnostics.Debug.WriteLine("TCPInput> Number of bytes read == 0! Setting continueRead = false");
                    continueRead = false;
                }

            }
            client.setDisconnected();
        }
    }
}

Ответы [ 4 ]

1 голос
/ 07 ноября 2010

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

1 голос
/ 10 ноября 2010

Итак, после долгих испытаний и обсуждений с моим соучастником, мы обнаружили, что вместо использования порта 21 и, например, порта 22, проблема исчезает.

Понятия не имею, почему он так себя ведет, но это так ...

1 голос
/ 07 ноября 2010

Это число («таким образом отправляя до 65835 байт») магически близко к 2^16-1 (65535) - выглядит как один пакет сверх!

(я предполагаю, что это просто больший размерэто привело к тому, что дела пошли kaboom! - это можно надежно протестировать.)

Я подозреваю, что в библиотеке используется 16-разрядная переменная без знака (в библиотеке), где вам нужно что-то с большим диапазоном.Возможно, вы можете периодически «опорожнять» внутреннюю часть библиотеки или выполнять операцию в нескольких соединениях?(Хорошо, просто пытаюсь выбросить некоторые идеи «быстрого взлома»: -)

0 голосов
/ 10 ноября 2010

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

Тогда мне также интересно, почему вы допускаете ошибку Windows, а не одну в MyNetowrking.dll. Конечно, вы использовали это в течение пяти лет. Но у него все еще не было того уровня проверки, который Microsoft дает своему коду.

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