C# Socket 编程完全指南
第 1 章:Socket 编程基础
1.1 什么是 Socket
Socket(套接字)是网络通信的端点,提供了应用程序与网络协议之间的接口。在C#中,System.Net.Sockets命名空间提供了完整的Socket编程支持。
通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据;
套接字就是四元组,即源ip+端口port 目的ip+端口port,表示绝对唯一的连接;
Socket 是在应用层(Http、SSH、FTP)和传输层(TCP、UDP)之间的一个抽象层,它把 TCP/IP 层复杂的操作抽象为几个简单的接口,供应用层调用实现进程在网络中的通信;
实际上socket是对TCP/IP协议的封装,Socket本身并不是协议,而是一个调用接口(API),通过Socket,我们才能使用TCP/IP协议;
1.2 Socket 通信模型
- TCP Socket:面向连接的可靠通信
- UDP Socket:无连接的快速通信
- 原始 Socket:直接访问网络层协议
1.3 核心命名空间
using System.Net;
using System.Net.Sockets;
using System.Text;
1.4 流程
服务器端:
第一步:建立一个用于通信的Socket对象
第二步:使用bind绑定IP地址和端口号
第三步:使用listen监听客户端
第四步:使用accept中断程序直到连接上客户端
第五步:接收来自客户端的请求
第六步:返回客户端需要的数据
第七步:如果接收到客户端已关闭连接信息就关闭服务器端
客户端:
第一步:建立一个用于通信的Socket对象
第二步:根据指定的IP和端口connet服务器
第三步:连接成功后向服务器端发送数据请求
第四步:接收服务器返回的请求数据
第五步:如果还需要请求数据继续发送请求
第 2 章:TCP Socket 编程
2.1 TCP 服务器端实现
2.1.1 基础TCP服务器
public class TcpServer
{private TcpListener listener;private bool isRunning;public TcpServer(string ipAddress, int port){IPAddress localAddr = IPAddress.Parse(ipAddress);listener = new TcpListener(localAddr, port);}public void Start(){isRunning = true;listener.Start();Console.WriteLine("TCP服务器已启动,等待客户端连接...");while (isRunning){try{// 接受客户端连接(阻塞方法)TcpClient client = listener.AcceptTcpClient();Console.WriteLine($"客户端已连接: {client.Client.RemoteEndPoint}");// 为每个客户端创建独立线程处理Thread clientThread = new Thread(HandleClient);clientThread.Start(client);}catch (Exception ex){Console.WriteLine($"接受连接错误: {ex.Message}");}}}private void HandleClient(object obj){TcpClient client = (TcpClient)obj;NetworkStream stream = client.GetStream();byte[] buffer = new byte[1024];int bytesRead;try{while ((bytesRead = stream.Read(buffer, 0, buffer.Length)) != 0){string receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"收到数据: {receivedData}");// 回显数据给客户端byte[] response = Encoding.UTF8.GetBytes($"服务器回复: {receivedData}");stream.Write(response, 0, response.Length);}}catch (Exception ex){Console.WriteLine($"处理客户端错误: {ex.Message}");}finally{client.Close();Console.WriteLine("客户端已断开连接");}}public void Stop(){isRunning = false;listener.Stop();}
}// 使用示例
TcpServer server = new TcpServer("127.0.0.1", 8080);
server.Start();
2.1.2 异步TCP服务器
public class AsyncTcpServer
{private TcpListener listener;private CancellationTokenSource cancellationTokenSource;public AsyncTcpServer(string ipAddress, int port){IPAddress localAddr = IPAddress.Parse(ipAddress);listener = new TcpListener(localAddr, port);cancellationTokenSource = new CancellationTokenSource();}public async Task StartAsync(){listener.Start();Console.WriteLine("异步TCP服务器已启动");try{while (!cancellationTokenSource.Token.IsCancellationRequested){TcpClient client = await listener.AcceptTcpClientAsync();_ = HandleClientAsync(client, cancellationTokenSource.Token);}}catch (Exception ex){Console.WriteLine($"服务器错误: {ex.Message}");}}private async Task HandleClientAsync(TcpClient client, CancellationToken token){using (client)using (NetworkStream stream = client.GetStream()){byte[] buffer = new byte[1024];try{while (!token.IsCancellationRequested){int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token);if (bytesRead == 0) break;string receivedData = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"收到数据: {receivedData}");// 异步发送回复byte[] response = Encoding.UTF8.GetBytes($"异步回复: {receivedData}");await stream.WriteAsync(response, 0, response.Length, token);}}catch (Exception ex){Console.WriteLine($"客户端处理错误: {ex.Message}");}}Console.WriteLine("客户端连接已关闭");}public void Stop(){cancellationTokenSource.Cancel();listener.Stop();}
}
2.2 TCP 客户端实现
2.2.1 基础TCP客户端
public class TcpClientExample
{private TcpClient client;private NetworkStream stream;public async Task ConnectAsync(string server, int port){client = new TcpClient();try{await client.ConnectAsync(server, port);stream = client.GetStream();Console.WriteLine("已连接到服务器");// 启动接收线程_ = Task.Run(ReceiveDataAsync);// 启动发送循环await SendDataLoopAsync();}catch (Exception ex){Console.WriteLine($"连接错误: {ex.Message}");}}private async Task SendDataLoopAsync(){try{while (client.Connected){Console.Write("请输入要发送的消息: ");string message = Console.ReadLine();if (message?.ToLower() == "exit") break;byte[] data = Encoding.UTF8.GetBytes(message);await stream.WriteAsync(data, 0, data.Length);}}catch (Exception ex){Console.WriteLine($"发送错误: {ex.Message}");}}private async Task ReceiveDataAsync(){byte[] buffer = new byte[1024];try{while (client.Connected){int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);if (bytesRead == 0) break;string receivedMessage = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"服务器回复: {receivedMessage}");}}catch (Exception ex){Console.WriteLine($"接收错误: {ex.Message}");}finally{Disconnect();}}public void Disconnect(){stream?.Close();client?.Close();Console.WriteLine("已断开连接");}
}// 使用示例
TcpClientExample client = new TcpClientExample();
await client.ConnectAsync("127.0.0.1", 8080);
第 3 章:UDP Socket 编程
3.1 UDP 服务器实现
public class UdpServer
{private UdpClient udpClient;private IPEndPoint remoteEndPoint;private bool isRunning;public UdpServer(int port){udpClient = new UdpClient(port);remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);}public async Task StartAsync(){isRunning = true;Console.WriteLine("UDP服务器已启动");try{while (isRunning){// 接收数据UdpReceiveResult result = await udpClient.ReceiveAsync();string receivedData = Encoding.UTF8.GetString(result.Buffer);Console.WriteLine($"收到来自 {result.RemoteEndPoint} 的数据: {receivedData}");// 发送回复string response = $"UDP服务器回复: {receivedData}";byte[] responseData = Encoding.UTF8.GetBytes(response);await udpClient.SendAsync(responseData, responseData.Length, result.RemoteEndPoint);}}catch (Exception ex){Console.WriteLine($"UDP服务器错误: {ex.Message}");}}public void Stop(){isRunning = false;udpClient.Close();}
}
3.2 UDP 客户端实现
public class UdpClientExample
{private UdpClient udpClient;private IPEndPoint serverEndPoint;public UdpClientExample(string server, int port){udpClient = new UdpClient();serverEndPoint = new IPEndPoint(IPAddress.Parse(server), port);}public async Task SendMessageAsync(string message){try{byte[] data = Encoding.UTF8.GetBytes(message);await udpClient.SendAsync(data, data.Length, serverEndPoint);// 接收回复UdpReceiveResult result = await udpClient.ReceiveAsync();string response = Encoding.UTF8.GetString(result.Buffer);Console.WriteLine($"服务器回复: {response}");}catch (Exception ex){Console.WriteLine($"UDP发送错误: {ex.Message}");}}public void Close(){udpClient.Close();}
}// 使用示例
UdpClientExample udpClient = new UdpClientExample("127.0.0.1", 8081);
await udpClient.SendMessageAsync("Hello UDP Server");
第 4 章:Socket 高级特性
4.1 Socket 选项配置
public class AdvancedSocketConfig
{public void ConfigureSocket(Socket socket){// 设置发送缓冲区大小socket.SendBufferSize = 8192;// 设置接收缓冲区大小socket.ReceiveBufferSize = 8192;// 设置发送超时(毫秒)socket.SendTimeout = 5000;// 设置接收超时(毫秒)socket.ReceiveTimeout = 5000;// 启用地址重用socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);// 设置KeepAlivesocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);// 禁用Nagle算法(减少延迟)socket.NoDelay = true;// 设置Linger选项LingerOption lingerOption = new LingerOption(true, 10); // 延迟10秒关闭socket.LingerState = lingerOption;}
}
4.2 多路复用 I/O(Select模式)
public class SocketMultiplexer
{private List<Socket> socketList = new List<Socket>();public void AddSocket(Socket socket){socketList.Add(socket);}public void StartMonitoring(){while (true){// 检查可读的SocketSocket.Select(socketList, null, null, 1000000); // 1秒超时foreach (Socket socket in socketList){if (socket.Available > 0){// 处理接收数据byte[] buffer = new byte[1024];int bytesRead = socket.Receive(buffer);string data = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"收到数据: {data}");}}}}
}
第 5 章:协议设计与数据序列化
5.1 自定义协议设计
// 协议头定义
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MessageHeader
{public int MessageId; // 消息IDpublic int BodyLength; // 消息体长度public MessageType Type; // 消息类型
}public enum MessageType
{Text = 1,Binary = 2,Command = 3
}// 消息序列化器
public class MessageSerializer
{public static byte[] SerializeMessage(int messageId, MessageType type, byte[] body){MessageHeader header = new MessageHeader{MessageId = messageId,BodyLength = body?.Length ?? 0,Type = type};// 计算总长度int headerSize = Marshal.SizeOf(typeof(MessageHeader));int totalSize = headerSize + header.BodyLength;byte[] data = new byte[totalSize];// 序列化头部IntPtr ptr = Marshal.AllocHGlobal(headerSize);Marshal.StructureToPtr(header, ptr, false);Marshal.Copy(ptr, data, 0, headerSize);Marshal.FreeHGlobal(ptr);// 复制消息体if (body != null && body.Length > 0){Buffer.BlockCopy(body, 0, data, headerSize, body.Length);}return data;}public static (MessageHeader header, byte[] body) DeserializeMessage(byte[] data){int headerSize = Marshal.SizeOf(typeof(MessageHeader));// 反序列化头部MessageHeader header;IntPtr ptr = Marshal.AllocHGlobal(headerSize);Marshal.Copy(data, 0, ptr, headerSize);header = (MessageHeader)Marshal.PtrToStructure(ptr, typeof(MessageHeader));Marshal.FreeHGlobal(ptr);// 提取消息体byte[] body = new byte[header.BodyLength];if (header.BodyLength > 0){Buffer.BlockCopy(data, headerSize, body, 0, header.BodyLength);}return (header, body);}
}
5.2 JSON协议实现
public class JsonProtocolHandler
{public class Message{public string Command { get; set; }public object Data { get; set; }public DateTime Timestamp { get; set; }}public byte[] SerializeMessage(Message message){string json = JsonSerializer.Serialize(message);return Encoding.UTF8.GetBytes(json);}public Message DeserializeMessage(byte[] data){string json = Encoding.UTF8.GetString(data);return JsonSerializer.Deserialize<Message>(json);}public async Task SendMessageAsync(NetworkStream stream, Message message){byte[] data = SerializeMessage(message);// 先发送数据长度byte[] lengthBytes = BitConverter.GetBytes(data.Length);await stream.WriteAsync(lengthBytes, 0, 4);// 发送实际数据await stream.WriteAsync(data, 0, data.Length);}public async Task<Message> ReceiveMessageAsync(NetworkStream stream){// 读取数据长度byte[] lengthBytes = new byte[4];await stream.ReadAsync(lengthBytes, 0, 4);int dataLength = BitConverter.ToInt32(lengthBytes, 0);// 读取实际数据byte[] data = new byte[dataLength];int bytesRead = 0;while (bytesRead < dataLength){bytesRead += await stream.ReadAsync(data, bytesRead, dataLength - bytesRead);}return DeserializeMessage(data);}
}
第 6 章:网络通信模式
6.1 请求-响应模式
public class RequestResponseClient
{private TcpClient client;private NetworkStream stream;private JsonProtocolHandler protocolHandler;public RequestResponseClient(){protocolHandler = new JsonProtocolHandler();}public async Task<T> SendRequestAsync<T>(string command, object data){try{var request = new JsonProtocolHandler.Message{Command = command,Data = data,Timestamp = DateTime.UtcNow};await protocolHandler.SendMessageAsync(stream, request);var response = await protocolHandler.ReceiveMessageAsync(stream);return JsonSerializer.Deserialize<T>(response.Data.ToString());}catch (Exception ex){Console.WriteLine($"请求错误: {ex.Message}");throw;}}
}
6.2 发布-订阅模式
public class PubSubServer
{private TcpListener listener;private List<TcpClient> subscribers = new List<TcpClient>();private readonly object lockObject = new object();public async Task StartAsync(string ipAddress, int port){IPAddress localAddr = IPAddress.Parse(ipAddress);listener = new TcpListener(localAddr, port);listener.Start();Console.WriteLine("发布-订阅服务器已启动");while (true){TcpClient client = await listener.AcceptTcpClientAsync();_ = HandleSubscriberAsync(client);}}private async Task HandleSubscriberAsync(TcpClient client){lock (lockObject){subscribers.Add(client);}try{NetworkStream stream = client.GetStream();byte[] buffer = new byte[1024];while (client.Connected){// 处理订阅者消息int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);if (bytesRead == 0) break;// 这里可以处理订阅者的订阅请求等}}catch (Exception ex){Console.WriteLine($"处理订阅者错误: {ex.Message}");}finally{lock (lockObject){subscribers.Remove(client);}client.Close();}}public async Task PublishAsync(string topic, object data){var message = new JsonProtocolHandler.Message{Command = "PUBLISH",Data = new { Topic = topic, Content = data },Timestamp = DateTime.UtcNow};byte[] messageData = new JsonProtocolHandler().SerializeMessage(message);List<TcpClient> currentSubscribers;lock (lockObject){currentSubscribers = new List<TcpClient>(subscribers);}var tasks = currentSubscribers.Select(async client =>{try{if (client.Connected){NetworkStream stream = client.GetStream();await stream.WriteAsync(messageData, 0, messageData.Length);}}catch (Exception ex){Console.WriteLine($"发布消息错误: {ex.Message}");}});await Task.WhenAll(tasks);}
}
第 7 章:性能优化与最佳实践
7.1 连接池管理
public class ConnectionPool : IDisposable
{private ConcurrentBag<TcpClient> connections;private string server;private int port;private int maxPoolSize;private SemaphoreSlim semaphore;public ConnectionPool(string server, int port, int maxPoolSize = 10){this.server = server;this.port = port;this.maxPoolSize = maxPoolSize;connections = new ConcurrentBag<TcpClient>();semaphore = new SemaphoreSlim(maxPoolSize, maxPoolSize);}public async Task<TcpClient> GetConnectionAsync(){await semaphore.WaitAsync();if (connections.TryTake(out TcpClient client) && client.Connected){return client;}// 创建新连接client = new TcpClient();await client.ConnectAsync(server, port);return client;}public void ReturnConnection(TcpClient client){if (client.Connected){connections.Add(client);}else{client.Dispose();}semaphore.Release();}public void Dispose(){foreach (var client in connections){client?.Close();client?.Dispose();}connections.Clear();semaphore.Dispose();}
}
7.2 缓冲区管理
public class BufferManager
{private byte[] bufferPool;private int currentIndex;private int bufferSize;private readonly object lockObject = new object();public BufferManager(int totalBytes, int bufferSize){this.bufferSize = bufferSize;bufferPool = new byte[totalBytes];}public bool SetBuffer(SocketAsyncEventArgs args){lock (lockObject){if ((bufferPool.Length - currentIndex) < bufferSize){return false;}args.SetBuffer(bufferPool, currentIndex, bufferSize);currentIndex += bufferSize;return true;}}public void FreeBuffer(SocketAsyncEventArgs args){// 在实际应用中可能需要更复杂的内存管理args.SetBuffer(null, 0, 0);}
}
第 8 章:安全与加密
8.1 SSL/TLS 加密通信
public class SslTcpClient
{public async Task ConnectWithSslAsync(string server, int port){TcpClient client = new TcpClient();await client.ConnectAsync(server, port);using (SslStream sslStream = new SslStream(client.GetStream(), false, ValidateServerCertificate, null)){try{await sslStream.AuthenticateAsClientAsync(server);// 加密通信byte[] message = Encoding.UTF8.GetBytes("Hello Secure Server");await sslStream.WriteAsync(message, 0, message.Length);byte[] buffer = new byte[1024];int bytesRead = await sslStream.ReadAsync(buffer, 0, buffer.Length);string response = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"安全服务器回复: {response}");}catch (Exception ex){Console.WriteLine($"SSL通信错误: {ex.Message}");}}}private bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors){// 在实际应用中应该进行严格的证书验证return sslPolicyErrors == SslPolicyErrors.None;}
}
8.2 数据加密传输
public class SecureDataTransmitter
{private Aes aesAlg;public SecureDataTransmitter(){aesAlg = Aes.Create();aesAlg.KeySize = 256;aesAlg.GenerateKey();aesAlg.GenerateIV();}public byte[] EncryptData(string plainText){using (ICryptoTransform encryptor = aesAlg.CreateEncryptor()){byte[] plainBytes = Encoding.UTF8.GetBytes(plainText);return encryptor.TransformFinalBlock(plainBytes, 0, plainBytes.Length);}}public string DecryptData(byte[] cipherText){using (ICryptoTransform decryptor = aesAlg.CreateDecryptor()){byte[] plainBytes = decryptor.TransformFinalBlock(cipherText, 0, cipherText.Length);return Encoding.UTF8.GetString(plainBytes);}}public void Dispose(){aesAlg?.Dispose();}
}
第 9 章:实战案例
9.1 聊天服务器实现
public class ChatServer
{private TcpListener listener;private List<ChatClient> clients = new List<ChatClient>();private readonly object lockObject = new object();public async Task StartAsync(string ipAddress, int port){IPAddress localAddr = IPAddress.Parse(ipAddress);listener = new TcpListener(localAddr, port);listener.Start();Console.WriteLine("聊天服务器已启动");while (true){TcpClient client = await listener.AcceptTcpClientAsync();ChatClient chatClient = new ChatClient(client, this);lock (lockObject){clients.Add(chatClient);}_ = chatClient.StartHandlingAsync();}}public async Task BroadcastMessageAsync(string message, ChatClient sender){List<ChatClient> currentClients;lock (lockObject){currentClients = new List<ChatClient>(clients);}var tasks = currentClients.Where(c => c != sender).Select(async client =>{await client.SendMessageAsync(message);});await Task.WhenAll(tasks);}public void RemoveClient(ChatClient client){lock (lockObject){clients.Remove(client);}}
}public class ChatClient
{private TcpClient client;private NetworkStream stream;private ChatServer server;private string username;public ChatClient(TcpClient client, ChatServer server){this.client = client;this.server = server;this.stream = client.GetStream();}public async Task StartHandlingAsync(){try{// 获取用户名byte[] buffer = new byte[1024];int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);username = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"{username} 加入了聊天室");await server.BroadcastMessageAsync($"{username} 加入了聊天室", this);while (client.Connected){bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);if (bytesRead == 0) break;string message = Encoding.UTF8.GetString(buffer, 0, bytesRead);Console.WriteLine($"{username}: {message}");await server.BroadcastMessageAsync($"{username}: {message}", this);}}catch (Exception ex){Console.WriteLine($"处理客户端 {username} 错误: {ex.Message}");}finally{server.RemoveClient(this);client.Close();await server.BroadcastMessageAsync($"{username} 离开了聊天室", this);Console.WriteLine($"{username} 离开了聊天室");}}public async Task SendMessageAsync(string message){byte[] data = Encoding.UTF8.GetBytes(message);await stream.WriteAsync(data, 0, data.Length);}
}
第 10 章:常见问题与解决方案
10.1 连接管理问题
问题:连接泄漏
// 解决方案:使用using语句确保资源释放
using (TcpClient client = new TcpClient())
using (NetworkStream stream = client.GetStream())
{// 使用连接
}
10.2 性能优化技巧
// 使用SocketAsyncEventArgs进行高性能I/O
public class HighPerformanceSocket
{public void Start(){Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);SocketAsyncEventArgs args = new SocketAsyncEventArgs();args.Completed += OnSocketOperationCompleted;args.SetBuffer(new byte[8192], 0, 8192);if (!socket.AcceptAsync(args)){OnSocketOperationCompleted(socket, args);}}private void OnSocketOperationCompleted(object sender, SocketAsyncEventArgs e){// 处理完成的操作}
}
10.3 错误处理最佳实践
public class RobustSocketClient
{public async Task<bool> ConnectWithRetryAsync(string server, int port, int maxRetries = 3){for (int attempt = 1; attempt <= maxRetries; attempt++){try{using (TcpClient client = new TcpClient()){await client.ConnectAsync(server, port);return true;}}catch (Exception ex){Console.WriteLine($"连接尝试 {attempt} 失败: {ex.Message}");if (attempt == maxRetries) return false;await Task.Delay(1000 * attempt); // 指数退避}}return false;}
}
本指南涵盖了C# Socket编程的所有核心概念和实战技巧,从基础TCP/UDP通信到高级的网络编程模式,适合从入门到精通的完整学习路径。