C#网络编程之TCP(四)
一、并发的访问
什么是并发的访问呢?并发访问是指多个客户端同时连接到服务器。前几节的服务器代码,支持并发访问吗?
while (true) { // 监听客户端发起连接 Socket conn = socket.Accept(); // 处理此连接的请求(可能要较长的时间!) // 关闭此连接 conn.Close(); }
显然,第一个客户端被处理的过程中,第二个客户端是连接不上来的。
那怎么解决这个问题呢?我们可以采用多线程的模式来实现并发的访问。
while (true) { // 监听客户端发起连接 Socket conn = socket.Accept(); // 创建一个线程,在线程里来处理这个连接 // 关闭此连接 }
二、多线程模式
当有客户端连接上来时,创建一个线程来处理这个连接的所有请求。
static void Main(string[] args) { const string ip = "127.0.0.1"; const int port = 10002; // 建立服务器 Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); EndPoint point = new IPEndPoint(IPAddress.Parse(ip), port); socket.Bind(point); socket.Listen(100); while (true) { // 监听客户端发起连接 Socket conn = socket.Accept(); ServiceHandler serviceHandler = new ServiceHandler(conn); // 创建一个线程处理 Task.Run(serviceHandler.Run); } }
public class ServiceHandler { Socket conn; public ServiceHandler(Socket conn) { this.conn = conn; } public void Run() { try { // 接收数据 byte[] bytes = new byte[4]; // 先获取内容长度 conn.Receive(bytes); int inputLength1 = BitConverter.ToInt32(bytes, 0); byte[] msgBytes1 = new byte[inputLength1]; // 再获取内容 conn.Receive(msgBytes1); string inputMsg1 = Encoding.UTF8.GetString(msgBytes1, 0, inputLength1); Console.WriteLine("<<: " + inputMsg1); // 发送数据 string outputMsg1 = "Hello World!"; byte[] buf = new byte[4]; // 先发送内容的长度 ConvertIntToByteArray(Encoding.UTF8.GetBytes(outputMsg1).Length, ref buf); conn.Send(buf); // 再发送内容 conn.Send(Encoding.UTF8.GetBytes(outputMsg1)); } catch (Exception ex) { throw ex; } conn.Close(); } private bool ConvertIntToByteArray(int m, ref byte[] arry) { if (arry == null) return false; if (arry.Length < 4) return false; arry[0] = (byte)(m & 0xFF); arry[1] = (byte)((m & 0xFF00) >> 8); arry[2] = (byte)((m & 0xFF0000) >> 16); arry[3] = (byte)((m >> 24) & 0xFF); return true; } }
三、非托管资源(网络连接句柄)的关闭
每一个客户端连接到服务器时都会创建一个socket连接。如果一个客户端连上服务器,什么消息也不发,那么服务器就需要将这些连接给关闭掉。所以必须要添加连接设置,在超时之后自动关闭连接
public class ServiceHandler { Socket conn; public ServiceHandler(Socket conn) { this.conn = conn; } public void Run() { try { // 设置超时时间 conn.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 10 * 1000); conn.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 10 * 1000); // 接收数据 byte[] bytes = new byte[4]; // 先获取内容长度 conn.Receive(bytes); int inputLength1 = BitConverter.ToInt32(bytes, 0); byte[] msgBytes1 = new byte[inputLength1]; // 再获取内容 conn.Receive(msgBytes1); string inputMsg1 = Encoding.UTF8.GetString(msgBytes1, 0, inputLength1); Console.WriteLine("<<: " + inputMsg1); // 发送数据 string outputMsg1 = "Hello World!"; byte[] buf = new byte[4]; // 先发送内容的长度 ConvertIntToByteArray(Encoding.UTF8.GetBytes(outputMsg1).Length, ref buf); conn.Send(buf); // 再发送内容 conn.Send(Encoding.UTF8.GetBytes(outputMsg1)); } catch (Exception ex) { throw ex; } conn.Close(); } private bool ConvertIntToByteArray(int m, ref byte[] arry) { if (arry == null) return false; if (arry.Length < 4) return false; arry[0] = (byte)(m & 0xFF); arry[1] = (byte)((m & 0xFF00) >> 8); arry[2] = (byte)((m & 0xFF0000) >> 16); arry[3] = (byte)((m >> 24) & 0xFF); return true; } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现