C#Networking:服务器在收到超过65535个字节后挂起

时间:2010-11-07 17:22:20

标签: c# networking

更新 由于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”,它从MySQL数据库读取XML,将其序列化为byte []数组并通过网络发送。这些特定的XML消息是序列化形式的627字节。

XMLPoller连接到“远程服务器”上的端口(恰好是localhost)并一次发送一个数据包。在数据包nbr 105处,连接已关闭。从XMLPoller发送104个数据包并由服务器接收。 104 x 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中缓冲任何内容。

当我在Socket(服务器上)上获得新连接时,我会执行以下代码:

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 个答案:

答案 0 :(得分:1)

这个数字(“因此发送直到65835字节”)神奇地接近2^16-1(65535) - 看起来只有一个数据包!

(我假设它只是更大的尺寸,使得事情变成了kaboom! - 这可以可靠地测试。)

我怀疑有一个无符号的16位变量(在库中)你需要更多范围的东西。也许你可以定期“清空”库的内部或者在多个连接中执行操作? (好吧,只是试图抛出一些'快速黑客'的想法: - )

答案 1 :(得分:1)

问题在于您的其他代码,即“客户端”。它在发送所有'数据包'后关闭连接。您必须等到服务器收到所有这些服务器。除了明确协商之外,一种简单的方法是等待服务器关闭连接。

答案 2 :(得分:1)

因此,在经过大量测试并与我的犯罪伙伴讨论后,我们发现不是使用端口21而是使用端口22 - 问题就消失了。

我不知道为什么它会像这样,但确实......

答案 3 :(得分:0)

你为我提出了问题。就像你为什么选择这个服务的知名端口?我不相信巧合,并怀疑你使用“犯罪伙伴”一词可能有更多的真相,我会关心与之相关。

然后我也想知道你为什么假设一个Windows bug而不是MyNetowrking.dll中的一个。当然,你已经用了五年了。但它仍然没有微软提供代码的审查程度。