C# - Socket 基础指南
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服务器
第三步:连接成功后向服务器端发送数据请求
第四步:接收服务器返回的请求数据
第五步:如果还需要请求数据继续发送请求
[例子] 服务端例子A:(Socket 版本)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SocketDemo1
{
public partial class Form1 : Form
{
Socket socketWatch = null;
List<Socket> connectedClients = new List<Socket>();
public Form1()
{
InitializeComponent();
}
//初始化Socket
private void InitService()
{
try
{
//第一步.创建监听套接字 使用 ip4协议,流式传输,TCP连接
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定端口
//第2.1步 获取网络节点对象
IPAddress address = IPAddress.Any;// IPAddress.Parse(this.txt_IP.Text);
IPEndPoint endPoint = new IPEndPoint(address, int.Parse(txt_Port.Text));
//第2.2步 绑定端口(其实内部就像系统端口表中注册了一个端口,并指定了当前句柄)
socketWatch.Bind(endPoint);
//第三步 设置监听队列
socketWatch.Listen(10);
ShowMsg($"服务器[{socketWatch.LocalEndPoint}]:启动成功!");
}
catch (Exception ex)
{
ShowMsg($"启动失败:{ex.Message}");
}
}
//开始监听
private async void btn_startListen_Click(object sender, EventArgs e)
{
try
{
this.btn_startListen.Text = "监听中";
// 如果监听器已经启动,先停止它
if (socketWatch != null)
{
socketWatch.Close();
socketWatch = null;
this.btn_startListen.Text = "开始监听";
ShowMsg($"服务器:已关闭!");
return;
}
InitService();
_ = Task.Run(async () =>
{
ShowMsg("监听开始");
while (true)
{
//第四步.开始监听 此方法会阻断当前线程,直到有其他程序连接
Socket clientSocket = await socketWatch.AcceptAsync();
ShowMsg($"[客户端:{clientSocket.RemoteEndPoint}]连接!");
// 将客户端添加到连接列表
lock (connectedClients)
{
connectedClients.Add(clientSocket);
}
_ = Task.Run(async () =>
{
await ReceiveMsg(clientSocket);
});
}
});
}
catch (Exception ex)
{
ShowMsg($"监听错误:{ex.Message}");
}
}
async Task ReceiveMsg(Socket clientSocket)
{
while (true)
{
byte[] buffer = new byte[1024 * 4]; //4kb 普通文本够用了
//var count = clientSocket.Receive(buffer);
ArraySegment<byte> bufferSegment = new ArraySegment<byte>(buffer);
//第五步 接收
int count = await clientSocket.ReceiveAsync(bufferSegment, SocketFlags.None);
#region 判断客户端连接是否关闭
if (count == 0) //判断客户端连接是否关闭
{
ShowMsg($"[客户端:{clientSocket.RemoteEndPoint}]:关闭连接!");
// 从连接列表中移除客户端
lock (connectedClients)
{
connectedClients.Remove(clientSocket);
}
clientSocket.Close();
break;
}
#endregion
string str = Encoding.UTF8.GetString(buffer, 0, count);
ShowMsg($"[客户端:{clientSocket.RemoteEndPoint}]:{str}");
}
}
//显示监听消息方法
void ShowMsg(string strmsg)
{
string dateNow = DateTime.Now.ToString("yyyy-mm-dd HH:mm:ss");
if (this.rtb_receive.InvokeRequired)
{
this.Invoke(new Action(() =>
{
this.rtb_receive.AppendText($"[{dateNow}]{strmsg} \r\n");
this.rtb_receive.SelectionStart = this.rtb_receive.Text.Length;
this.rtb_receive.ScrollToCaret();
}));
}
else
{
this.rtb_receive.AppendText($"[{dateNow}]{strmsg} \r\n");
this.rtb_receive.SelectionStart = this.rtb_receive.Text.Length;
this.rtb_receive.ScrollToCaret();
}
}
// 发送消息给所有客户端
private async void btn_send_Click(object sender, EventArgs e)
{
try
{
string message = rtb_send.Text?.Trim();
if (string.IsNullOrEmpty(message))
{
ShowMsg("发送消息不能为空!");
return;
}
// 获取当前连接的客户端列表
Socket[] clients;
lock (connectedClients)
{
clients = connectedClients.ToArray();
}
if (clients.Length == 0)
{
ShowMsg("没有连接的客户端!");
return;
}
byte[] data = Encoding.UTF8.GetBytes(message);
int successCount = 0;
foreach (var client in clients)
{
try
{
if (client.Connected)
{
await client.SendAsync(new ArraySegment<byte>(data), SocketFlags.None);
successCount++;
}
}
catch (Exception ex)
{
ShowMsg($"发送消息到客户端失败:{ex.Message}");
// 从连接列表中移除失效的客户端
lock (connectedClients)
{
connectedClients.Remove(client);
}
}
}
ShowMsg($"消息发送成功:{message} (发送给 {successCount}/{clients.Length} 个客户端)");
rtb_send.Text = ""; // 清空发送框
}
catch (Exception ex)
{
ShowMsg($"发送消息错误:{ex.Message}");
}
}
// 窗体关闭时清理资源
protected override void OnFormClosed(FormClosedEventArgs e)
{
// 关闭监听Socket
socketWatch?.Close();
// 关闭所有客户端连接
lock (connectedClients)
{
foreach (var client in connectedClients)
{
client?.Close();
}
connectedClients.Clear();
}
base.OnFormClosed(e);
}
}
}
[例子] 客户端例子A:(Socket 版本)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SocketClientDemo1
{
public partial class Form1 : Form
{
private Socket clientSocket;
private bool isConnected = false;
public Form1()
{
InitializeComponent();
}
private async void InitClient()
{
await ConnectAsync("127.0.0.1", 60000);
}
public async Task ConnectAsync(string server, int port)
{
if (clientSocket != null)
{
try
{
// 检查socket是否已被释放
var test = clientSocket.Connected;
}
catch (ObjectDisposedException)
{
clientSocket = null;
}
}
if (clientSocket == null)
{
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
try
{
if (!clientSocket.Connected)
{
// 使用异步连接
await Task.Run(() =>
{
clientSocket.Connect(new IPEndPoint(IPAddress.Parse(server), port));
});
ShowMsg("已连接到服务器");
// 启动接收线程
_ = Task.Run(ReceiveDataAsync);
isConnected = true;
this.btn_open.Enabled = false;
this.btn_close.Enabled = true;
}
}
catch (SocketException ex)
{
ShowMsg($"连接错误: {ex.Message}");
//clientSocket = null;
isConnected = false;
Disconnect();
}
catch (ObjectDisposedException)
{
ShowMsg("连接错误: Socket对象已被释放,请重试");
//clientSocket = null;
isConnected = false;
}
catch (Exception ex)
{
ShowMsg($"连接错误: {ex.Message}");
//clientSocket = null;
isConnected = false;
}
}
private async Task ReceiveDataAsync()
{
byte[] buffer = new byte[1024];
try
{
while (clientSocket != null && clientSocket.Connected && isConnected)
{
if (clientSocket.Available > 0)
{
int bytesRead = await Task.Run(() => clientSocket.Receive(buffer));
if (bytesRead == 0) break;
string receivedMessage = Encoding.UTF8.GetString(buffer, 0, bytesRead);
ShowMsg($"服务器回复: {receivedMessage}");
}
}
}
catch (SocketException ex)
{
ShowMsg($"接收错误: {ex.Message}");
}
catch (ObjectDisposedException)
{
ShowMsg("连接已断开");
}
catch (Exception ex)
{
ShowMsg($"接收错误: {ex.Message}");
}
//finally
//{
// Disconnect();
//}
}
private async void btn_send_Click(object sender, EventArgs e)
{
if (clientSocket == null || !clientSocket.Connected || !isConnected)
{
MessageBox.Show("请先连接到服务器");
return;
}
if (string.IsNullOrWhiteSpace(rtb_send.Text))
{
MessageBox.Show("请输入要发送的消息");
return;
}
try
{
string message = rtb_send.Text;
byte[] data = Encoding.UTF8.GetBytes(message);
// 使用Socket的异步发送方法
int bytesSent = await Task.Run(() => clientSocket.Send(data));
if (bytesSent > 0)
{
rtb_send.Clear();
ShowMsg($"已发送: {message}");
}
}
catch (SocketException ex)
{
MessageBox.Show($"发送错误: {ex.Message}");
}
catch (ObjectDisposedException)
{
MessageBox.Show("连接已断开,请重新连接");
isConnected = false;
}
catch (Exception ex)
{
MessageBox.Show($"发送错误: {ex.Message}");
}
}
void ShowMsg(string strmsg)
{
string dateNow = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
if (this.rtb_receive.InvokeRequired)
{
this.Invoke(new Action(() =>
{
this.rtb_receive.AppendText($"[{dateNow}]{strmsg} \r\n");
this.rtb_receive.SelectionStart = this.rtb_receive.Text.Length;
this.rtb_receive.ScrollToCaret();
}));
}
else
{
this.rtb_receive.AppendText($"[{dateNow}]{strmsg} \r\n");
this.rtb_receive.SelectionStart = this.rtb_receive.Text.Length;
this.rtb_receive.ScrollToCaret();
}
}
#region 连接/断开
private void btn_open_Click(object sender, EventArgs e)
{
InitClient();
}
private void btn_close_Click(object sender, EventArgs e)
{
Disconnect();
}
#endregion
public void Disconnect()
{
try
{
if (clientSocket != null)
{
if (clientSocket.Connected)
{
clientSocket.Shutdown(SocketShutdown.Both);
}
clientSocket.Close();
clientSocket.Dispose();
}
isConnected = false;
this.btn_open.Enabled = true;
this.btn_close.Enabled = false;
}
catch (Exception ex)
{
ShowMsg($"断开连接错误: {ex.Message}");
}
finally
{
clientSocket = null;
isConnected = false;
ShowMsg("已断开连接");
}
}
}
}
1.5 TcpListener 服务器端步骤:
TcpListener
是.NET 中专门用于 TCP 服务器端的高级封装类,它简化了基于 TCP 协议的服务器端 Socket 编程流程,底层同样依赖 Socket,但隐藏了许多底层细节。对比你列出的 Socket 服务器端步骤,TcpListener
的工作流程如下:
-
创建 TcpListener 对象
直接指定要监听的 IP 地址和端口号(无需手动创建底层 Socket),例如:
TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 8888);
(内部已封装 Socket 的创建,默认使用 TCP 协议的 Socket)
-
启动监听(Start)
调用
Start()
方法启动服务器,替代了 Socket 的Bind()
和Listen()
两步操作:server.Start(); // 内部自动完成绑定IP+端口、设置监听队列等
-
等待客户端连接(AcceptTcpClient)
调用
AcceptTcpClient()
方法(阻塞当前线程),直到有客户端连接,返回一个TcpClient
对象(封装了与客户端的连接),替代 Socket 的Accept()
:TcpClient client = server.AcceptTcpClient(); // 类似Socket的Accept(),但返回TcpClient
-
获取通信流(NetworkStream)
通过
TcpClient.GetStream()
获取网络流,用于收发数据,替代 Socket 的Receive()
和Send()
:NetworkStream stream = client.GetStream();
-
接收客户端请求
使用
stream.Read()
从流中读取客户端发送的数据:byte[] buffer = new byte[1024]; int bytesRead = stream.Read(buffer, 0, buffer.Length); string request = Encoding.UTF8.GetString(buffer, 0, bytesRead);
-
向客户端返回数据
使用
stream.Write()
向流中写入响应数据:byte[] response = Encoding.UTF8.GetBytes("已收到请求"); stream.Write(response, 0, response.Length);
-
关闭连接与服务器
当客户端断开连接(如流读取到 0 字节),关闭
TcpClient
和流,最终停止服务器:stream.Close(); client.Close(); server.Stop(); // 停止监听并释放资源
[例子] 服务端例子A:(TcpListener 版本)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SocketDemo1
{
public partial class Form1 : Form
{
TcpListener tcpListener = null;
List<TcpClient> connectedClients = new List<TcpClient>();
private bool isRunning = false;
public Form1()
{
InitializeComponent();
}
private void InitService()
{
try
{
// 使用 TcpListener 替代 Socket
int port = int.Parse(txt_Port.Text);
tcpListener = new TcpListener(IPAddress.Any, port);
tcpListener.Start();
ShowMsg($"服务器[{tcpListener.LocalEndpoint}]:启动成功!");
}
catch (Exception ex)
{
ShowMsg($"启动失败:{ex.Message}");
}
}
//启动监听
private async void btn_startListen_Click(object sender, EventArgs e)
{
try
{
this.btn_startListen.Text = "监听中";
isRunning = true;
// 如果监听器已经启动,先停止它
if (tcpListener != null)
{
tcpListener.Stop();
tcpListener = null;
this.btn_startListen.Text = "开始监听";
ShowMsg($"服务器:已关闭!");
return;
}
// 初始化服务
InitService();
// 启动后台任务持续监听客户端连接
_ = Task.Run(async () =>
{
ShowMsg("监听开始,等待客户端连接...");
while (isRunning)
{
try
{
// 使用 AcceptTcpClientAsync 替代 Socket.AcceptAsync
TcpClient tcpClient = await tcpListener.AcceptTcpClientAsync();
ShowMsg($"[客户端:{tcpClient.Client.RemoteEndPoint}]连接!");
// 将客户端添加到连接列表
lock (connectedClients)
{
connectedClients.Add(tcpClient);
}
// 为每个客户端启动独立的任务处理消息
_ = Task.Run(async () =>
{
await ReceiveMsg(tcpClient);
});
}
catch (ObjectDisposedException)
{
// 监听器被正常关闭,退出循环
isRunning = false;
break;
}
catch (Exception ex)
{
ShowMsg($"接受客户端连接错误:{ex.Message}");
isRunning = false;
}
}
});
}
catch (Exception ex)
{
ShowMsg($"监听错误:{ex.Message}");
this.btn_startListen.Text = "开始监听";
isRunning = false;
}
}
async Task ReceiveMsg(TcpClient tcpClient)
{
try
{
NetworkStream stream = tcpClient.GetStream();
byte[] buffer = new byte[1024 * 4]; // 4KB 缓冲区
while (isRunning)
{
// 使用 NetworkStream.ReadAsync 接收数据
int count = await stream.ReadAsync(buffer, 0, buffer.Length);
// 判断连接是否关闭
if (count == 0)
{
ShowMsg($"[客户端:{tcpClient.Client.RemoteEndPoint}]:关闭连接!");
// 从连接列表中移除客户端
lock (connectedClients)
{
connectedClients.Remove(tcpClient);
}
tcpClient.Close();
break;
}
// 处理接收到的数据
string str = Encoding.UTF8.GetString(buffer, 0, count);
ShowMsg($"[客户端:{tcpClient.Client.RemoteEndPoint}]:{str}");
}
}
catch (Exception ex)
{
ShowMsg($"与客户端[{tcpClient.Client.RemoteEndPoint}]通信错误:{ex.Message}");
// 从连接列表中移除客户端
lock (connectedClients)
{
connectedClients.Remove(tcpClient);
}
tcpClient.Close();
this.btn_startListen.Text = "开始监听";
isRunning = false;
}
}
//显示监听消息方法
void ShowMsg(string strmsg)
{
string dateNow = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
if (this.rtb_receive.InvokeRequired)
{
this.Invoke(new Action(() =>
{
this.rtb_receive.AppendText($"[{dateNow}]{strmsg} \r\n");
this.rtb_receive.SelectionStart = this.rtb_receive.Text.Length;
this.rtb_receive.ScrollToCaret();
}));
}
else
{
this.rtb_receive.AppendText($"[{dateNow}]{strmsg} \r\n");
this.rtb_receive.SelectionStart = this.rtb_receive.Text.Length;
this.rtb_receive.ScrollToCaret();
}
}
// 发送消息给所有客户端
private async void btn_send_Click(object sender, EventArgs e)
{
try
{
string message = rtb_send.Text?.Trim();
if (string.IsNullOrEmpty(message))
{
ShowMsg("发送消息不能为空!");
return;
}
// 获取当前连接的客户端列表
TcpClient[] clients;
lock (connectedClients)
{
clients = connectedClients.ToArray();
}
if (clients.Length == 0)
{
ShowMsg("没有连接的客户端!");
return;
}
byte[] data = Encoding.UTF8.GetBytes(message);
int successCount = 0;
foreach (var client in clients)
{
try
{
if (client.Connected)
{
NetworkStream stream = client.GetStream();
await stream.WriteAsync(data, 0, data.Length);
successCount++;
}
}
catch (Exception ex)
{
ShowMsg($"发送消息到客户端失败:{ex.Message}");
// 从连接列表中移除失效的客户端
lock (connectedClients)
{
connectedClients.Remove(client);
}
}
}
ShowMsg($"消息发送成功:{message} (发送给 {successCount}/{clients.Length} 个客户端)");
rtb_send.Text = ""; // 清空发送框
}
catch (Exception ex)
{
ShowMsg($"发送消息错误:{ex.Message}");
}
}
// 窗体关闭时清理资源
protected override void OnFormClosed(FormClosedEventArgs e)
{
tcpListener?.Stop();
// 关闭所有客户端连接
lock (connectedClients)
{
foreach (var client in connectedClients)
{
client?.Close();
}
connectedClients.Clear();
}
base.OnFormClosed(e);
}
}
}
1.6 TcpClient 客户端步骤:
-
创建 TcpClient 对象
初始化客户端对象(可无参创建,后续连接时指定地址和端口;也可在构造函数中直接指定)。
TcpClient client = new TcpClient(); // 无参构造,后续调用Connect连接 // 或直接在构造函数中指定服务器地址和端口(内部自动连接) // TcpClient client = new TcpClient("127.0.0.1", 8888);
-
连接服务器(Connect)
调用
Connect
方法,指定服务器的 IP 地址(或主机名)和端口号,建立 TCP 连接。string serverIp = "127.0.0.1"; // 服务器IP int port = 8888; // 服务器监听的端口 client.Connect(serverIp, port); // 阻塞直到连接成功或失败(失败会抛异常)
-
获取网络流(NetworkStream)
通过
GetStream()
方法获取与服务器通信的网络流(NetworkStream
),后续通过流进行数据的读写。NetworkStream stream = client.GetStream();
-
向服务器发送数据
将需要发送的数据(字符串或字节数组)通过
stream.Write()
写入流,发送给服务器。string message = "Hello, Server!"; byte[] data = Encoding.UTF8.GetBytes(message); // 转换为字节数组 stream.Write(data, 0, data.Length); // 发送数据
-
接收服务器的响应
使用
stream.Read()
从流中读取服务器返回的数据(需提前定义缓冲区)。byte[] buffer = new byte[1024]; // 缓冲区,用于存放接收的数据 int bytesRead = stream.Read(buffer, 0, buffer.Length); // 读取数据(返回实际读取的字节数) string response = Encoding.UTF8.GetString(buffer, 0, bytesRead); // 转换为字符串 Console.WriteLine("收到服务器响应:" + response);
-
关闭连接与释放资源
通信完成后,依次关闭流和
TcpClient
,释放资源(建议使用using
语句自动释放)。stream.Close(); // 关闭流 client.Close(); // 关闭客户端连接
[例子] 客户端例子B:(TcpClient 版本)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SocketClientDemo1
{
public partial class Form1 : Form
{
private TcpClient client;
private NetworkStream stream;
public Form1()
{
InitializeComponent();
}
private async void InitClient()
{
await ConnectAsync("127.0.0.1", 60000);
}
public async Task ConnectAsync(string server, int port)
{
if (client == null)
{
client = new TcpClient();
}
try
{
if (!client.Connected)
{
await client.ConnectAsync(server, port);
stream = client.GetStream();
ShowMsg("已连接到服务器");
// 启动接收线程
_ = Task.Run(ReceiveDataAsync);
}
}
catch(SocketException ex)
{
//连接错误: 由于目标计算机积极拒绝,无法连接。 127.0.0.1:60000
ShowMsg($"连接错误: {ex.Message}");
client = null; // 重置client以便下次重新创建
}
catch (ObjectDisposedException)
{
ShowMsg("连接错误: 连接对象已被释放,请重试");
client = null; // 重置client以便下次重新创建
}
catch (NullReferenceException)
{
ShowMsg("连接错误: 未将对象引用设置到对象的实例,请重试");
client = null; // 重置client以便下次重新创建
}
catch (Exception ex)
{
Console.WriteLine($"连接错误: {ex.Message}");
ShowMsg($"连接错误: {ex.Message}");
}
}
private async Task ReceiveDataAsync()
{
byte[] buffer = new byte[1024];
try
{
while (client != null && client.Connected && stream != null)
{
int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
string receivedMessage = Encoding.UTF8.GetString(buffer, 0, bytesRead);
Console.WriteLine($"服务器回复: {receivedMessage}");
ShowMsg(receivedMessage);
}
}
catch (ObjectDisposedException)
{
// 流已被释放,正常断开连接
ShowMsg("连接已断开");
client = null;
}
catch (Exception ex)
{
Console.WriteLine($"接收错误: {ex.Message}");
ShowMsg($"接收错误: {ex.Message}");
}
}
private async void btn_send_Click(object sender, EventArgs e)
{
if (client == null || !client.Connected || stream == null)
{
MessageBox.Show("请先连接到服务器");
return;
}
if (string.IsNullOrWhiteSpace(rtb_send.Text))
{
MessageBox.Show("请输入要发送的消息");
return;
}
try
{
string message = rtb_send.Text;
byte[] data = Encoding.UTF8.GetBytes(message);
await stream.WriteAsync(data, 0, data.Length);
rtb_send.Clear();
}
catch (ObjectDisposedException)
{
MessageBox.Show("连接已断开,请重新连接");
}
catch (Exception ex)
{
MessageBox.Show($"发送错误: {ex.Message}");
}
}
void ShowMsg(string strmsg)
{
string dateNow = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
if (this.rtb_receive.InvokeRequired)
{
this.Invoke(new Action(() =>
{
this.rtb_receive.AppendText($"[{dateNow}]{strmsg} \r\n");
this.rtb_receive.SelectionStart = this.rtb_receive.Text.Length;
this.rtb_receive.ScrollToCaret();
}));
}
else
{
this.rtb_receive.AppendText($"[{dateNow}]{strmsg} \r\n");
this.rtb_receive.SelectionStart = this.rtb_receive.Text.Length;
this.rtb_receive.ScrollToCaret();
}
}
#region 连接/断开
private void btn_open_Click(object sender, EventArgs e)
{
InitClient();
this.btn_open.Enabled = false;
this.btn_close.Enabled = true;
}
private void btn_close_Click(object sender, EventArgs e)
{
Disconnect();
this.btn_open.Enabled = true;
this.btn_close.Enabled = false;
}
#endregion
public void Disconnect()
{
stream?.Close();
client?.Close();
ShowMsg("已断开连接");
}
}
}
第 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);
// 设置KeepAlive
socket.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)
{
// 检查可读的Socket
Socket.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; // 消息ID
public 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通信到高级的网络编程模式,适合从入门到精通的完整学习路径。