boxmoe_header_banner_img

Hello! 欢迎来到我的博客!

加载中

文章导读

Unity基础网络开发(2)——多客户端与服务器对话


avatar
xiaoifei 2024年5月24日 221

回顾

上一章讲述了如何用Socket套接字建立TCP的基础连接,并实现了简单的收发消息,但是存在以下问题:

在主线程中会经历以下卡顿阶段:

1. 服务端Accept卡顿,这是由于服务端在一直等待客户端连接,直到客户端连上才能继续执行

2.Receive接收消息卡顿,一般由于网络情况所导致

当然还有收发消息不可控,服务端只能连接一个客户端的局限性

 

在这一节我们为了解决问题需要引入多线程的知识

并且对于服务器的项目进行封装,使其自由连接多个客户端,并且能够自由切换客户端对话

知识引入

多线程Thread

适用场景:执行其他比较耗时的逻辑(例如寻路算法,网络请求与连接等)

新开线程:Thread t = new Thread(func())

启动线程:t.Start()

线程休眠:Thread.Sleep(30000);//让线程休眠30000毫秒(30秒)

将线程设置为后台线程(随着主线程结束而销毁):t.IsBackground = true

释放线程:当线程逻辑执行完毕(函数执行完),就可以直接将t = null,GC垃圾回收机制会自动释放资源,但是当线程存在while死循环,则需在外部设置一个isRunning成员方法作为循环条件,由其他线程控制其是否执行

线程池ThreadPool

适用场景:多线程的应用程序开发中,不易频繁地开启和销毁线程,这样会带来巨大性能开销

而ThreadPool由若干数量的线程,我们可以从中取任意个线程执行任意任务。任务执行完后,线程不会被销毁,而是被线程池回收

当所有线程都被分配任务,又有新任务才会新建线程。当线程数量达到最大值,任务会排队等待其他任务执行

获取可用线程:ThreadPool.GetAvailableThreads(out num1,out num2);num1为工作线程数,num2为I/O线程数

设置最大线程:ThreadPool.SetMaxThreads(20, 20);返回一个是否成功设置的bool值

获取最大线程:ThreadPool.GetMaxThreads(out num1, out num2);

设置最小线程:ThreadPool.SetMinThreads(5, 5)

获取最小线程:ThreadPool.GetMinThreads(out num1, out num2);

将方法排入队列以便执行,当线程池中线程变得可用时执行

ThreadPool.QueueUserWorkItem((obj) =>
{
    print(obj);
    print("开启了一个线程");
}, "123452435345");
for (int i = 0; i < 10; i++)
{
    ThreadPool.QueueUserWorkItem((obj) =>
    {
       print("第" + obj + "个任务");
    }, i);
}

解释:QueueUserWorkItem有两个参数,第一个参数是委托函数/lambda表达式,其包含传入参数(会转换为object),第二个参数是要传入的参数值

案例

针对第一个耗时的问题,首先需要将Accept函数用多线程优化,我们知道Accpet可以检测新的客户端连入并返回与此相关的Socket对象,所以可以用死循环,并将里连入的不同Socket对象存储到一个容器中(这里采用List来存储)

private static List<Socket> socketClientsList = new List<Socket>();
//主方法
            Thread threadAcceptConnect = new Thread(AcceptClientConnect);
            threadAcceptConnect.Start();
            threadAcceptConnect.IsBackground = true;
//新开线程的方法
    static void AcceptClientConnect()
    {
        Socket socketClient;
        while (!isClose)//循环检测
        {
            socketClient = socket.Accept();
            socketClientsList.Add(socketClient);
            socketClient.Send(Encoding.UTF8.GetBytes("Welcome to Connect to Server!"));
            Console.WriteLine("与 " +socketClient.RemoteEndPoint.ToString() + " 连接建立成功!");
        }
    }
1

 

using System.Net;
using System.Net.Sockets;
using System.Text;

namespace LearnTcpServer;

class Program
{
    private static bool isClose = false;
    private static Socket socket;
    private static List socketClientsList = new List();
    private static int socketClientNum = 0;
    private static void Main(string[] args)
    {
        Console.WriteLine("输入 command:Quit 即可退出程序");
        Console.WriteLine("输入 command:Switch 可切换对象");
        socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        try
        {
            IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8080);
            socket.Bind(ipEndPoint);
        }
        catch (Exception e)
        {
            Console.WriteLine("绑定失败");
            Console.WriteLine(e.Message);
            throw;
        }

        socket.Listen(1024);
        Console.WriteLine("等待客户端连入");

        Thread threadAcceptConnect = new Thread(AcceptClientConnect);
        threadAcceptConnect.Start();
        threadAcceptConnect.IsBackground = true;

        Thread threadReceiveMsg = new Thread(ReceiveMsg);
        threadReceiveMsg.Start();
        threadReceiveMsg.IsBackground = true;

        while (!isClose)
        {
            string sendMsg = Console.ReadLine();
            if (string.IsNullOrEmpty(sendMsg)) continue;
            if (sendMsg.StartsWith("command:"))
            {
                sendMsg = sendMsg.Substring(8);
                if (sendMsg == "Quit")
                {
                    Console.WriteLine("closing...");
                    isClose = true;
                    break;
                }
                else if (sendMsg == "Switch")
                {
                    Console.WriteLine("选择聊天对象:(输入序号即可)");
                    for (int i = 0; i < socketClientsList.Count; i++) { Console.WriteLine(socketClientsList[i].RemoteEndPoint.ToString() + " 序号:" + i); } string tempstr = Console.ReadLine(); if (!string.IsNullOrEmpty(tempstr) && int.Parse(tempstr)>=0 && int.Parse(tempstr)<= socketClientsList.Count)
                    {
                        socketClientNum = int.Parse(tempstr);
                        Console.WriteLine("已切换至:"+socketClientNum);
                    }
                }
            }
            else
            {
                Console.WriteLine("发送给:"+socketClientNum);
                try
                {
                    socketClientsList[socketClientNum].Send(Encoding.UTF8.GetBytes(sendMsg));
                }
                catch (Exception e)
                {
                    Console.Write("发送失败,原因:");
                    Console.WriteLine(e);
                }
            }
        }
        if (isClose)
        {
            threadReceiveMsg.Join();
            //threadAcceptConnect.Join();
            for (int i = 0; i < socketClientsList.Count; i++)
            {
                socketClientsList[i].Shutdown(SocketShutdown.Both);
                socketClientsList[i].Close();
            } 
            socketClientsList.Clear();
        }
        Console.WriteLine("服务端已关闭");
    }
    static void ReceiveMsg()
    {
        byte[] result = new byte[1024];
        Socket socketClient;
        while (!isClose)
        {
            for (int i = 0; i < socketClientsList.Count; i++) { socketClient = socketClientsList[i]; if (socketClient != null && socketClient.Available > 0)
                {
                    int receiveNum = socketClient.Receive(result);
                    Console.WriteLine("Received Message From {0}:{1}",socketClient.RemoteEndPoint.ToString(),Encoding.UTF8.GetString(result,0,receiveNum));
                }
            }
        }
    }

    static void AcceptClientConnect()
    {
        Socket socketClient;
        while (!isClose)
        {
            socketClient = socket.Accept();
            socketClientsList.Add(socketClient);
            socketClient.Send(Encoding.UTF8.GetBytes("Welcome to Connect to Server!"));
            Console.WriteLine("与 " +socketClient.RemoteEndPoint.ToString() + " 连接建立成功!");
        }
    }
}


评论(0)

查看评论列表

暂无评论


发表评论

表情 颜文字
插入代码