×

关于TCP_Server接入多个客户端的问题

zxjy辉 zxjy辉 发表于2023-03-13 21:29:08 浏览287 评论0

抢沙发发表评论

一个TCP服务端,接入多个客户端,之间通信的问题

如何在一个服务端里,实现其他客户端之间的数据的广播,也就是说,有一个客户端发送消息,其他客户端都能收到这条数据

最便捷的方式,就是将每一个接入的客户端都保存到一个list列表中,然后通过遍历的方式,将数据发送给出自己以外的socker,

值得注意的是,在通过线程,进行单独的数据接收和发送的时候,不要用直接用list[下标]的方式进行数据发送,否则在断开连接之后,清除该对象,那么其他的socker

下标就乱了。所以一定要接收到客户端的请求之后,加入列表,然后在接收发送线程中立马把对用的socker对象取出来,断开连接之后一定要在合适的地方将线程break;否则卡死

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Reflection;

namespace Linux_TCP_Server
{


    class Program
    {

        //const int cycleNum = 10;
        //static void Main(string[] args)
        //{
        //    ThreadPool.SetMinThreads(1, 1);
        //    ThreadPool.SetMaxThreads(5, 5);
        //    for (int i = 1; i <= cycleNum; i++)
        //    {
        //        ThreadPool.QueueUserWorkItem(new WaitCallback(testFun), i.ToString());
        //    }
        //    Console.WriteLine("主线程执行!");
        //    Console.WriteLine("主线程结束!");
        //    Console.ReadKey();
        //}
        //public static void testFun(object obj)
        //{
        //    Console.WriteLine(string.Format("{0}:第{1}个线程,当前线程为{2}",
        //    DateTime.Now.ToString(), obj.ToString(),
        //    Thread.CurrentThread.ManagedThreadId.ToString()));
        //    Thread.Sleep(1000);
        //}

        /*
         * 实现原理:
         * 服务器作为一个服务端,4G模块和PC上位机作为客户端
         * 4G模块是一直连接在服务器上,PC则是不一定,当需要的时候,连接服务器,像4G模块发起更新
         * 服务器将双方的数据,进行转接
         * 不采用服务器直接端口转发的原因在于:
         * 如果采用端口转发,那就意味着,PC这边,是要有一个架设在电脑上的服务端,这个本地服务端,通过端口转发,将本地端口映射到
         * 互联网上,也就是服务器上。
         * 然后,4G模块,连接服务器的端口,就相当于访问本地pc的端口,这样的劣势在于,如果断开pc的服务端,那么4g模块就断开了
         * 
         * 
         * 
         */




        static List<Socket> userList = new List<Socket>();
        static TcpListener tcpListener;                                      //创建服务端对象
        static Socket socket;                                                //创建socket对象
        static TcpClient newClient;                                          //客户端对象
        static Socket mysocket;

        //   static String IP = "10.1.1.69";                                     //主机IP
        static String IP = "192.168.1.4";                                     //主机IP
        static int Port = 4445;                                             //主机端口号

        static char[] Master_4G = { 'M', 'A' };                               //4G模块的标志
        static String Master_4G_IP = null;                                   //4G模块的IP地址
        static int Master_4G_Port = 0;                                       //4G模块IP的端口号
        static int Master_4G_number = 0;                                     //4G模块连接时的在列表中的下标
        static int Master_4G_Stop = 0;                                       //4G模块断开连接标志

        static char[] PC = { 'P', 'C' };                                       //PC上位机的标志
        static String PC_IP = null;                                          //PC上位机的IP地址
        static int PC_Port = 0;                                              //PC上位机IP对应的端口号
        static int PC_number = 0;                                            //PC连接时的在列表中的下标
        static int PC_Stop = 0;                                              //PC断开连接标志

        static int num = 0;
        static int stop_flag = 0;

        static int PC_Flag = 0;                      //表示PC连接成功
        static int Master_4G_Flag = 0;               //表示4G模块连接成功
        static Socket PC_Socket;
        static Socket Master_4G_Socket;

        static byte SOH = 0X01;
        static byte STX = 0X02;
        static byte EOT = 0X04;


        static void Main(string[] args)
        {

            //IPHostEntry ipEntry = Dns.GetHostEntry(Dns.GetHostName());
            //foreach (var ip in ipEntry.AddressList)
            //{
            //    Console.WriteLine("IP Address: " + ip.ToString());
            //}

            //Console.WriteLine("请输入当前服务器的IP,以回车键结束");
            //IP = Console.ReadLine();
            //// Console.WriteLine("当前输入的IP:"+IP);
            //// Console.WriteLine();
            //Console.WriteLine("请输入开放的端口号,以回车键结束");
            //Port = Convert.ToInt32(Console.ReadLine());
            ////Console.WriteLine("当前输入的端口号:" + Port);
            ////Console.WriteLine();

            TCP_Server();    //创建服务端监听
            Console.ReadKey();
        }
        /// <summary>
        /// 创建一个TCP服务器,
        /// </summary>
        static void TCP_Server()
        {
            Console.WriteLine("创建中。。。");
            try
            {
                tcpListener = new TcpListener(IPAddress.Parse(IP), Port);
                //tcpListener = new TcpListener(IPAddress.Parse("185.77.225.13"), 2333);
                tcpListener.Start();
                //创建一个线程,等待客户端连接
                Console.WriteLine("创建成功");
                WaitConnect();
                //Thread WC_Thread = new Thread(WaitConnect);
                //WC_Thread.Start();

            }
            catch (Exception)
            {

                Console.WriteLine("地址或端口错误");
            }


        }
        void test()
        {
            Console.WriteLine("回调");
        }
        /// <summary>
        /// 创建等待客户端连接的线程,只要有客户端连接,就创建一个socke对象,并加入列表
        /// </summary>
        static void WaitConnect()
        {

            while (true)
            {
                try
                {
                    socket = tcpListener.AcceptSocket(); //等待连接,阻塞式
                    userList.Add(socket);                //添加到客户端列                 
                                                         //Console.WriteLine("当前客户端IP:" + userList[num].RemoteEndPoint.ToString().Split(':')[0]);
                                                         //Console.WriteLine("当前客户端Port:" + userList[num].RemoteEndPoint.ToString().Split(':')[1]);


                    //创建一个接收数据的线程
                    //Thread Receive_Thread = new Thread( ReceiveDate );
                    //Receive_Thread.Start();
                    ThreadStart threadStart = new ThreadStart(Change_Data);
                    threadStart.BeginInvoke(null, null);
                    num++;
                    Console.WriteLine("当前连接次数:" + userList.Count);

                }
                catch (Exception)
                {

                    Console.WriteLine("客户端连接错误!!");
                }
            }


        }

        static void Change_Data()
        {
            byte[] receive = new byte[256];
            int Receive_n = 0;
            int Index = userList.Count - 1;
            Socket socket = userList[Index];                  //直接取出对象,否则用列表的下标对象,否则在其他对象删除的时候,下标就乱了
            Console.WriteLine("当前客户端IP:" + userList[Index].RemoteEndPoint.ToString().Split(':')[0]);
            Console.WriteLine("当前客户端Port:" + userList[Index].RemoteEndPoint.ToString().Split(':')[1]);
            while (true)
            {
                Receive_n = socket.Receive(receive);
                //IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("255,255,255,255"), Port);
                if (Receive_n > 0)
                {
                    try
                    {

                        for (int i = 0; i < userList.Count; i++)
                        {
                            if (socket != userList[i])
                            {
                                userList[i].Send(receive, Receive_n, SocketFlags.None);
                            }
                        }
                    }
                    catch (Exception)
                    {
                        break;

                    }
                }
                else
                {
                    Console.WriteLine("客户端下标:{0}断开链接", Index);
                    userList.Remove(socket);                             //移除的时候,用对象,不用下标,否则多次连接会找出连接失败        
                    //if (userList[Index] == null) {
                    //    Console.WriteLine("没有对象");

                    //}
                    break;
                }
            }

        }
    }
}


#好好学习!

群贤毕至

访客