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