C#网络编程:服务器在接收超过65535字节后挂起

3

更新:由于在Stackoverflow的管理员出现问题,我已经在MSDN论坛上发布了一个非常简化版本的相同问题。下面的文本使用了MyNetworking.dll,但这不是问题所在。这是一个非常简化的客户端 - 服务器示例,问题完全相同。随意尝试一下 =) http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/d3d33eb9-7dce-4313-929e-a8a63d0f1e03 /更新

我有一个奇怪的错误。

通常,我们有一个处理网络的DLL。让我们称之为MyNetworking.dll。我们在服务器和客户端的各个地方都使用它,并且已经使用了五年。我没有遇到过问题,直到现在。

我有一个“XML轮询程序”,从MySQL数据库中读取XML,将其序列化为byte []数组并通过网络发送。这些特定的XML消息以序列化形式为627字节。

XML轮询程序连接到“远程服务器”(碰巧是localhost)上的一个端口,并逐个发送数据包。在第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

我尝试对来自客户端的输出流进行Flush操作-没有任何效果

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,它监听传入的数据并将其转发给客户端处理程序(如上所示):
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();
        }
    }
}

你能展示一下客户端代码吗? - jgauffin
我更新了上面的文本 =) - Ted
其实,现在还不确定。虽然我考虑过这个问题,但感觉有些奇怪,因为我们在其他客户端中使用相同的代码可以轻松传输数MB的数据而不会关闭连接... 但是无论如何,我现在会尝试一下。 - Ted
请展示完整的客户端代码。整个类。错误提示客户端已关闭连接。 - jgauffin
@crassy 不,刷新并没有帮助(请参见更新的帖子) - Ted
显示剩余3条评论
4个回答

1

那个数字(“因此发送了65835字节”)神奇地接近于2^16-1(65535)——看起来只是多了一个数据包!

(我假设只是更大的尺寸让事情爆炸了!——这可以可靠地测试。)

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


是的,没错 =)这肯定与65536有关。这就是为什么我在文本中强调了这一点。但现在所有使用的代码都已经呈现出来了(包括客户端),并且没有找到Int16或类似的65535/65536。不过,我会尝试只发送一个包含65535的数据包,看看是否有效... 嗯。 - Ted

1
问题出在你的其他代码中,即“客户端”。它在发送所有“数据包”后关闭连接。你必须等到服务器接收完所有数据包再关闭连接。一个简单的方法是,在明确协商之外,等待服务器关闭连接。

嗯,我不太确定。我已经更新了上面的文本。你可以看到发送代码很简单,非常简单。它将字节数组写入输出流。我已经逐步执行了该代码,没有异常。它最后返回"true"... - Ted
有很多代码片段,但它们并没有展示连接是如何被关闭的。你得到的异常抱怨连接关闭得太快了。 - Hans Passant
上面已更新的文本。我现在看到客户端和服务器都出现了相同的错误:“无法从传输连接中读取数据:已建立的连接被主机计算机中的软件中止。” - Ted
所以,更新了文本。我相信很确定的是客户端确实在干扰我。请参见上面的“20:15”更新。 - Ted

1

经过多次测试和与我的搭档讨论后,我们发现使用端口21而不是例如端口22,问题就会消失。

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


由于毫无理由地关闭了同一主题的另一个问题,我可能会在这里提出:这是Win/.NET中的一个错误吗?有人听说过这个问题吗? - Ted
有关“剥离”客户端服务器(没有MyNetworking.dll)的参考,请参见此处:http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/d3d33eb9-7dce-4313-929e-a8a63d0f1e03 - Ted

0

你的帖子让我产生了疑问。比如为什么你要选择这个服务的众所周知的端口?我不相信巧合,怀疑你使用“同谋”这个词可能比我愿意承认的更真实。

此外,我也想知道为什么你假设是Windows的漏洞而不是MyNetowrking.dll中的漏洞。当然,你已经使用了五年了。但它仍然没有得到微软给他们的代码的那种审查水平。


他们删除了我的帖子并对我的“重复发布”在stackoverflow上感到沮丧。我实际上将MyNetworking.dll彻底删除并将代码剥离到非常基础的程度,但情况仍然没有改变。请在此处查看:http://social.msdn.microsoft.com/Forums/en-US/netfxnetcom/thread/d3d33eb9-7dce-4313-929e-a8a63d0f1e03 - Ted
正如您在那篇帖子中所看到的,以及我在那里发布的代码,这是.NET网络编程的基础 - 没有MyNetworking.dll或类似的东西...但我仍然遇到了那个错误。请随意测试=)而我选择知名端口并不是问题所在。我有我的理由=) - Ted

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接