草庐IT

c# - 从所有套接字接收消息而不循环遍历所有套接字

coder 2023-09-19 原文

我正在为我的 a 级项目使用 C# 和 unity 编写消息传递应用程序,它使用的是 tcp 客户端-服务器模型。我已经创建了一个可以接收消息的套接字列表,但我必须指定从中接收消息的套接字,当我只有 1 个客户端时这很适合测试,但我需要能够处理更多连接和循环通过每个套接字尝试从中接收消息似乎非常低效和缓慢。

我的问题是:如何在不使用上述方法的情况下接收来自多个客户端的消息?

我将发布特定部分,然后是完整代码,以便您了解我在做什么。

这是处理从套接字接收数据的线程

    public static void Thread1()
    {
        for (int i = 0; i <= 2; i++)
        {
            byte[] b = new byte[155];
            ///////// I need to specify the socket to receive from////////
            int k = counter.numbers1[counter.i2].Receive(b);
            //////////////////////////////////////////////////////////////
            string k1 = "";

            for (int i3 = 0; i3 < k; i3++)
                k1 = k1 + Convert.ToChar(b[i3]);

            Console.WriteLine(k1);

            string sender_endpoint = k1.Substring(0, 21); ;
            string receiver_endpoint = k1.Substring(22, 21);
            string message = k1.Substring(44, 100);
            string msgtype = k1.Substring(145, 1);
            Console.WriteLine(k1.Length);
            Console.WriteLine(sender_endpoint);
            Console.WriteLine(receiver_endpoint);
            Console.WriteLine(message);
            Console.WriteLine(msgtype);

            if (msgtype == "0") //message
            {
                Console.WriteLine("000");
            }
            else if (msgtype == "1") //command
            {
                Console.WriteLine("111");
            }
        }
    }

完整的服务器代码是:

namespace server
{
    static class counter
    {
        public static int i2 = 0;//main listener
        public static int i4 = 0;//port number
        public static List<TcpListener> numbers = new List<TcpListener>();//listeners
        public static List<Socket> numbers1 = new List<Socket>();//sockets
    }

    class server_code
    {
        public static void Main()
        {
            string xyz123 = "x";
            Thread thread3 = new Thread(new ThreadStart(Thread3));
            Thread test_thread = new Thread(() => Test_Thread (xyz123));
            thread3.Start();
        }

        public static void Thread1()
        {
            //message reactions
            for (int i = 0; i <= 2; i++)
            {
                byte[] b = new byte[155];
                int k = counter.numbers1[counter.i2].Receive(b);
                string k1 = "";

                for (int i3 = 0; i3 < k; i3++)
                    k1 = k1 + Convert.ToChar(b[i3]);

                Console.WriteLine(k1);

                string sender_endpoint = k1.Substring(0, 21); ;
                string receiver_endpoint = k1.Substring(22, 21);
                string message = k1.Substring(44, 100);
                string msgtype = k1.Substring(145, 1);
                Console.WriteLine(k1.Length);
                Console.WriteLine(sender_endpoint);
                Console.WriteLine(receiver_endpoint);
                Console.WriteLine(message);
                Console.WriteLine(msgtype);

                if (msgtype == "0") //message
                {
                    Console.WriteLine("000");
                }
                else if (msgtype == "1") //command
                {
                    Console.WriteLine("111");
                }
            }
        }

        public static void Thread2()
        {
            //client message receiver
            var host = Dns.GetHostEntry(Dns.GetHostName());

            foreach (var ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    IPAddress ipAd = IPAddress.Parse(ip.ToString());

                    counter.numbers.Add(null); 
                    counter.numbers1.Add(null);

                    Console.WriteLine("");

                    Socket xyz1 = counter.numbers[counter.i2].AcceptSocket();

                    counter.numbers1[counter.i2] = xyz1;
                    ASCIIEncoding asen = new ASCIIEncoding();
                    counter.numbers1[counter.i2].Send(asen.GetBytes(counter.numbers1[counter.i2].RemoteEndPoint.ToString()));
                    Console.WriteLine("Connection accepted from " + counter.numbers1[counter.i2].RemoteEndPoint);
                    Console.WriteLine("connecting to " + counter.numbers[counter.i2].LocalEndpoint);
                    //System.Threading.Thread.Sleep(3000);

                    Thread thread1 = new Thread(new ThreadStart(Thread1));
                    thread1.Start();
                }
            }
        }

        public static void Thread3()
        {
            //new clients acception
            var host = Dns.GetHostEntry(Dns.GetHostName());

            foreach (var ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    for (int i = 0; i <= 10; i++)
                    {
                        Console.WriteLine(ip.ToString());
                        IPAddress ipAd = IPAddress.Parse(ip.ToString());
                        TcpListener myList = new TcpListener(ipAd, 8080);
                        myList.Start();
                        Console.WriteLine("The local endpoint is  :" + myList.LocalEndpoint);

                        Socket s = myList.AcceptSocket();//////////////////////////////////////////////
                        Console.WriteLine("Connection accepted from " + s.RemoteEndPoint);
                        Console.WriteLine("connecting to " + myList.LocalEndpoint);
                        ASCIIEncoding asen = new ASCIIEncoding();
                        counter.i2 = counter.i2 + i;
                        int portno = 8000;
                        s.Send(asen.GetBytes(portno.ToString()));

                        Console.WriteLine();
                        myList.Stop();
                        s.Close();
                        Thread thread2 = new Thread(new ThreadStart(Thread2));

                        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                        counter.numbers.Add(null);

                        TcpListener xyz = new TcpListener(ipAd, portno);

                        counter.numbers[counter.i2] = xyz;
                        //Console.WriteLine(counter.i4);//current portno

                        counter.numbers[counter.i2].Start();
                        Console.WriteLine("The local End point is  :" + counter.numbers[counter.i2].LocalEndpoint);
                        ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                        thread2.Start();
                    }
                }
            }
        }
}

完整的客户端代码是:

public class cc : MonoBehaviour 
{
    public InputField NameField;
    private string x;
    private string y = "";
    //private int z = 0;

    static class ipadressnumber{
        public static string ipadressno = "192.168.1.250";
    }

    public void Start(){
            TcpClient tcpclnt = new TcpClient();

            tcpclnt.Connect(ipadressnumber.ipadressno,8080);
            Stream stm = tcpclnt.GetStream();

            byte[] bb=new byte[100];
            int k=stm.Read(bb,0,100);

            for (int i=0;i<k;i++){
                y = y + Convert.ToChar(bb[i]);
            }
            //Debug.Log(y);
            tcpclnt.Close();
    }

    public void OnSubmit() 
    {
        x =  NameField.text;
        //Debug.Log(x);

        try 
        {
            System.Net.IPAddress ipad = IPAddress.Parse(ipadressnumber.ipadressno);
            IPEndPoint ipLocalEndPoint = new IPEndPoint(ipad,8888);
            //TcpClient clientSocket = new TcpClient(ipLocalEndPoint);
            TcpClient tcpclnt1 = new TcpClient();
            //Debug.Log(y);
            tcpclnt1.Connect(ipadressnumber.ipadressno,int.Parse(y));
            Stream stm = tcpclnt1.GetStream();

            byte[] bb=new byte[100];
            int k=stm.Read(bb,0,100);
            string lclep = "";

            for (int i=0;i<k;i++) 
            {
                lclep = lclep + Convert.ToChar(bb[i]);
            }

            String str=x;
            ////////////format of message:(sender endpoint, recipient endpoint, message, message type, check sum/digit)
            string message = "";
            int strlen = 21;
            int msglen = 100;
            string msgtype = "0";// 0 = message, 1 = command
            lclep = Convert.ToString(lclep);
            Debug.Log (lclep);

            while (strlen > lclep.Length)
            {
                Debug.Log('1');
                lclep = lclep + "#";
            }

            string rmtep = "000.00.00.000:8000";

            while (strlen > rmtep.Length)
            {
                Debug.Log ('2');
                rmtep = rmtep + "#";
            }

            while (msglen > x.Length)
            {
                Debug.Log ('3');
                x = x + "#";
            }           

            message = message + lclep + ':' + rmtep + ':' + x + ':' + msgtype + ':';
            Debug.Log(message);

            ASCIIEncoding asen= new ASCIIEncoding();
            byte[] ba = new byte[155];
            ba=asen.GetBytes(message);

            stm.Write(ba,0,ba.Length);

            tcpclnt1.Close();
        }
        catch (Exception e) {
            Console.WriteLine("Error..... " + e.StackTrace);
        }
    }
}

输出是这样的:

192.168.1.250
The local End point is  :192.168.1.250:8080
Connection accepted from 192.168.1.250:54024
connecting to 192.168.1.250:8080

The local End point is  :192.168.1.250:8000
192.168.1.250
The local End point is  :192.168.1.250:8080

Connection accepted from 192.168.1.250:54055
connecting to 192.168.1.250:8000
192.168.1.250:54055##:000.00.00.000:8000###:message test 1 for stackoverflow####################################################################:0:
147
192.168.1.250:54055##
000.00.00.000:8000###
message test 1 for stackoverflow####################################################################
0
000

如您所见,我没有收到任何错误,所以我所需要的只是一种从多个客户端接收消息的方法。

最佳答案

如果有一个最小的、完整的和可验证的例子会很棒 https://stackoverflow.com/help/mcve但我会针对我理解的问题给出一个可能的解决方案:如何监听多个套接字并同时处理它们的消息。

您可以为每个需要的套接字启动一个线程,这个读取器线程将读取整个消息并发送到另一个线程中的线程安全列表,我将其称为处理线程。最后一个线程会将您的业务逻辑应用于套接字读取的数据,生成响应并将其发送到另一个线程:编写器线程。

Socker -> Readers -> Processor -> Writer

不要像在 Thread3 和 Thread2 方法中那样启动您的线程,这需要过多的 CPU,并且会减慢您的应用程序。预先创建线程,然后以适当的顺序启动。

关于c# - 从所有套接字接收消息而不循环遍历所有套接字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55436346/

有关c# - 从所有套接字接收消息而不循环遍历所有套接字的更多相关文章

  1. ruby - 树顶语法无限循环 - 2

    我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

  2. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  3. ruby - 如何以所有可能的方式将字符串拆分为长度最多为 3 的连续子字符串? - 2

    我试图获取一个长度在1到10之间的字符串,并输出将字符串分解为大小为1、2或3的连续子字符串的所有可能方式。例如:输入:123456将整数分割成单个字符,然后继续查找组合。该代码将返回以下所有数组。[1,2,3,4,5,6][12,3,4,5,6][1,23,4,5,6][1,2,34,5,6][1,2,3,45,6][1,2,3,4,56][12,34,5,6][12,3,45,6][12,3,4,56][1,23,45,6][1,2,34,56][1,23,4,56][12,34,56][123,4,5,6][1,234,5,6][1,2,345,6][1,2,3,456][123

  4. ruby-on-rails - 使用 rails 4 设计而不更新用户 - 2

    我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它​​不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数

  5. ruby-on-rails - 跳过状态机方法的所有验证 - 2

    当我的预订模型通过rake任务在状态机上转换时,我试图找出如何跳过对ActiveRecord对象的特定实例的验证。我想在reservation.close时跳过所有验证!叫做。希望调用reservation.close!(:validate=>false)之类的东西。仅供引用,我们正在使用https://github.com/pluginaweek/state_machine用于状态机。这是我的预订模型的示例。classReservation["requested","negotiating","approved"])}state_machine:initial=>'requested

  6. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  7. ruby - Nokogiri 剥离所有属性 - 2

    我有这个html标记:我想得到这个:我如何使用Nokogiri做到这一点? 最佳答案 require'nokogiri'doc=Nokogiri::HTML('')您可以通过xpath删除所有属性:doc.xpath('//@*').remove或者,如果您需要做一些更复杂的事情,有时使用以下方法遍历所有元素会更容易:doc.traversedo|node|node.keys.eachdo|attribute|node.deleteattributeendend 关于ruby-Nokog

  8. ruby-on-rails - 如何在 Rails View 上显示错误消息? - 2

    我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c

  9. ruby-on-rails - RSpec:避免使用允许接收的任何实例 - 2

    我正在处理旧代码的一部分。beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)endRubocop错误如下:Avoidstubbingusing'allow_any_instance_of'我读到了RuboCop::RSpec:AnyInstance我试着像下面那样改变它。由此beforedoallow_any_instance_of(SportRateManager).toreceive(:create).and_return(true)end对此:let(:sport_

  10. ruby - 获取模块中定义的所有常量的值 - 2

    我想获取模块中定义的所有常量的值:moduleLettersA='apple'.freezeB='boy'.freezeendconstants给了我常量的名字:Letters.constants(false)#=>[:A,:B]如何获取它们的值的数组,即["apple","boy"]? 最佳答案 为了做到这一点,请使用mapLetters.constants(false).map&Letters.method(:const_get)这将返回["a","b"]第二种方式:Letters.constants(false).map{|c

随机推荐