在一段时间后,套接字会受到限制

时间:2011-12-24 20:04:22

标签: c# .net windows networking tcp

我正在C#中创建一个可以在任何应用程序中使用的网络库,作为这个库的一部分,我有一个TCP客户端/服务器设置。这种设置几乎适用于所有情况;它可以在最小和中等应力负载下连接,发送/接收数据并完美断开连接。但是,当我从客户端向服务器发送大量数据时,客户端套接字可以工作不同的时间(有时很短,有时很长),然后只是拒绝发送数据一段时间。具体来说,我的数据速率从550-750 KBps范围变为0 KBps,并且在那里再次存在不同的时间。然后套接字将在很短的时间内再次开始发送,并再次“受到限制”。在限制期间,我假设套接字已断开,因为我无法发送任何内容,但轮询使用此代码返回套接字IS连接:


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

我刚刚在我的大学上学了一个网络课程,所以我开始考虑TCP中的拥塞控制和流量控制机制,但在我看来,这两者都不会导致这个问题。拥塞控制只会降低数据速率,接收方的完整缓冲区不会持续几乎达到0 KBps数据速率的时间。该症状似乎指向某种类型的重数据限制或大规模丢弃数据包。

我的问题是:有没有人知道可能导致这些数据“限制”的原因,因为缺乏一个更好的术语?另外,我发送的数据包是否可能比我的路由器更进一步,即使它们被发送到同一子网中的主机?

编辑:就是这么清楚,我试图解决这个问题的原因是因为我想以最高的数据速率通过TCP发送文件。我知道UDP也可以使用,我也会使用它来制作解决方案,但我希望TCP先工作。

具体信息:

我正在使用阻塞读/写操作,服务器是多线程的。客户端也在自己的线程上运行。我正在我的本地子网上测试,通过我的路由器弹出所有数据包,其吞吐量应为54 Mbps。数据包的大小各为8 KB,最大值每秒发送1000次(发送线程休眠1 ms),但显然没有达到该速率。减小数据包的大小以使数据速率降低会导致限制消失。 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 个答案:

答案 0 :(得分:2)

这可能是你的代码,但我们很难说它不完整。

在经过多年的TCP / IP经验后,我在.NET TCP/IP FAQ中编写了自己的一套最佳实践。我建议你从那开始。

P.S。我为线上数据包保留了“数据包”一词。 TCP应用程序无法控制数据包。我对应用程序协议级消息使用术语“消息”。我认为这可以减少混乱,尤其是对于新手来说。

答案 1 :(得分:1)

如果您正在尝试创建

  

C#中的网络库,我可以在任何应用程序中使用

你知道那里有任何现有的开源库吗? networkComms.net可能是一个好的开始。如果你能重新创造同样的问题我会非常惊讶。我个人用它来维护1000多个并发连接,每个连接每秒发送大约10个数据包。否则,如果您想继续使用您的代码,或许查看networkComms.net的source可以指出您可能出错的地方。

答案 2 :(得分:1)

没有密切关注你的代码片段,但我看到你在那里有分配 - 你有没有检查过你对垃圾收集器施加的压力?

PS:(sending thread sleeps 1 ms) - 请记住,没有timeBeginPeriod()的Sleep()不会获得1ms的分辨率 - 可能接近10-20ms,具体取决于Windows版本和硬件。

答案 3 :(得分:0)

不知道C#,这段代码不完整。如果我做对了,那么

readPacket.Read(reader); 

将读取任何可用的内容,并且您的接收器端循环将被敲除。你在哪里检查读取的字节数?

无论如何,检查TCP级别和更低级别发生的事情的好方法是wireshark