回顾
上一章讲述了如何用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)
暂无评论