C#:我有100个客户端分别进行线程,发送/接收数据的最有效方法是什么?

时间:2017-06-06 19:13:52

标签: c# multithreading object tcp udp

我正在使用一个服务器和一个客户端进行项目以进行模拟。目标是开发一个可以使用TCP和UDP同时与100个客户端通信的服务器。我正在为服务器和客户端使用TCPListener和UDPClient。我正在使用多线程,创建一个对象类。

我的代码有两个问题:

  1. 在运行期间,UDP数据包丢失,冻结了一些线程。

  2. UDP的发送/接收速度非常不同(线程2发送2000与线程4发送900等)

  3. 此外,当我运行10个Threads时,它将完美地运行。但是当线程数量增加时,就会出现这些问题。

    我有以下代码↓

    客户端(接收功能):

    rpacket = new byte[1024];
                //#0: UDP packet timeout
                // is this necessary?? I could use while loop to measure waiting time.
                net.tClient.Client.ReceiveTimeout = 1000;
                net.client.Client.ReceiveTimeout = 1000;
                //net.client.Client.SendTimeout = 2000;
    
                // #1: message
                _logIt(false, localThreadId, "started receivedata for threadid" + localThreadId);
    
                // #2: number of received data recording
                int nReceived = 0;
    
                // #3: loop runs forever...
                while (true)
                {
    
                    // receiving data
                    bool recursion = false;
                    int recursionCount = 0;
                    int bytesReceived = 0;
    
                    // receiving data
                    bytesReceived = messageReceive(out recursion);
    
                    while (recursion == true)
                    {
                        // check if there are data 3 times
                        bytesReceived = messageReceive(out recursion);
                        recursionCount++;
    
                        if (recursionCount <= 3)
                        {
                            if (recursionCount == 1 && rIndex > 0)
                            { rIndex--; }
                            // resend message
                            if (net.q[rIndex][1] == 0x00)
                            {
    
                                // sending data to PICS
                                // changed net.q[rIndex].Length - 1 to changed net.q[rIndex].Length - 2
                                try
                                {
                                    int nBytesSend = net.client.Send(tempData, tempData.Length, net.ipEndPoint);
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine(e.Message);
                                }
    
                            }
    
                            // if 0x01, the protocl is TCP
                            else if (net.q[rIndex][1] == 0x01)
                            {
                                try { 
                                int nBytesSend = net.tClient.Client.Send(tempData, tempData.Length, SocketFlags.None);
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine(e.Message);
                                }
                            }
    
                        }
                        else if (recursionCount == 4)
                        {
    
                            // hoot~ byebye 
                            recursion = false;
                            Usage("hoot");
    
                        }
                    }
                }
    

    客户端(发送功能):

    // if 0x00, the protocol is UDP
    
                        if (net.q[rIndex][1] == 0x00)
                        {
    
                            // sending data to PICS
                            // changed net.q[rIndex].Length - 1 to changed net.q[rIndex].Length - 2
                            int nBytesSend = net.client.Send(data, net.q[rIndex].Length - 2, net.ipEndPoint);
    
                        }
    
                        // if 0x01, the protocl is TCP
    
                        else if (net.q[rIndex][1] == 0x01)
                        {
    
                            int nBytesSend = net.tClient.Client.Send(data, net.q[rIndex].Length - 2, SocketFlags.None);
                        }
    
                        // June 1th, tempData is only used for failed messages
                        if (data.Length != 0)
                        { tempData = data; }
                        Array.Resize(ref tempData, data.Length);
    
    
                        net.nSent++;
                        totalSent[localThreadId]++;
                        if (verbosity)
                            _logIt(false, localThreadId, "# of data sent: " + net.nSent);
    
                        // after sending, set the queue[][0] to 0x00
                        net.q[rIndex][0] = 0x00;
                        // setting queue[][1] is not neccessary...
                        rIndex++;
    

    消息接收(输出递归):

    messageReceive(out bool audi)
            {
                int numBytes = 0;
                audi = false;
                // updated try and catch method, because there can be udp or tcp
                try
                {
    
                    rpacket = net.client.Receive(ref net.ipEndPoint);
                    numBytes = rpacket.Length;
    
                }
    
                catch (SocketException ex)
                {
                    try
                    {
                        numBytes = net.tClient.Client.Receive(rpacket);
                        Array.Resize(ref rpacket, numBytes);
                    }
                    catch (Exception exc)
                    {
                        audi = true;
                        Console.WriteLine(exc.Message);
                    }
    
    
                }
    
                return numBytes;
            }
    

    服务器端与客户端相同。

    仅供参考:有一个名为queue的二维字节数组,用于存储将发送给远程用户的所有数据。 wIndex是一个整数,用于将回复数据写入队列。 rIndex是一个整数,用于从队列中读取回复数据并将其发送给远程用户。

    如何解决我的问题?

1 个答案:

答案 0 :(得分:0)

警告使用多个线程通过套接字发送/接收会锁定程序中的套接字及其I / O操作。

当您使用套接字时,最好使用套接字提供的异步,例如BeginRead和'EndRead',因为它们使程序将在同一个线程上运行并避免套接字 - 客户端被封锁另外,不要在socket-clients中使用sleep,它们会阻塞socket。

检查此答案:C# High CPU usage on Listener thread, sleeping misses disconnect

在这个答案中,我展示了如何使用BeginReadWriteRead