文章目录
- 0、写在前面的话
- [1、Socket 介绍](#1、Socket 介绍)
-
- [1.1 Socket是什么](#1.1 Socket是什么)
- [1.2 Socket在网络中的位置](#1.2 Socket在网络中的位置)
- [2、C# 中的Socket参数](# 中的Socket参数)
-
- [2.1 超时控制参数](#2.1 超时控制参数)
- [2.2 缓冲区参数](#2.2 缓冲区参数)
- [2.3 UDP专用参数](#2.3 UDP专用参数)
- [3、C# 中的Socket API](# 中的Socket API)
-
- [3.1 Socket(构造函数)](#3.1 Socket(构造函数))
-
- [3.1.1 SocketType](#3.1.1 SocketType)
- [3.1.2 ProtocolType](#3.1.2 ProtocolType)
- [3.1.3 AddressFamily](#3.1.3 AddressFamily)
- [3.2 Bind()](#3.2 Bind())
- [3.3 Connect()](#3.3 Connect())
- [3.4 Listen()
(TCP)
](#3.4 Listen() (TCP)) - [3.5 Accept()
(TCP)
](#3.5 Accept() (TCP)) - [3.6 Receive() 与 Send()](#3.6 Receive() 与 Send())
- [3.7 ReceiveFrom()与SendTo()](#3.7 ReceiveFrom()与SendTo())
- [3.8 Shutdown()和Close()](#3.8 Shutdown()和Close())
- [4、IPAddress 和 IPEndPoint](#4、IPAddress 和 IPEndPoint)
-
- [4.1 IPAddress](#4.1 IPAddress)
- [4.2 IPEndPoint](#4.2 IPEndPoint)
0、写在前面的话
我创建的Unity、C#交流群,有兴趣可加入大家一起学习:952914223
1、Socket 介绍
1.1 Socket是什么
Socket的英文原意是"插座",的意思,通常在计算机编程中称作套接字,是对对于TCP/IP的封装,我们可以将Socket联想成是由两个Socket对象搭建的成的一根通信管道,管道的两端是这两个Socket对象,而这根管道的连接的是两台主机的应用进程(通过IP地址和端口号确定进程)
1.2 Socket在网络中的位置

2、C# 中的Socket参数
2.1 超时控制参数
- ReceiveTimeout:接收超时
csharp
socket.ReceiveTimeout = 5000; // 5秒(单位:毫秒)
// 超时抛出SocketException,ErrorCode为10060 (TimedOut)
- SendTimeout:发送超时
csharp
socket.SendTimeout = 3000; // 3秒
// 影响Send/SendTo操作
- BeginConnect:连接超时
csharp
// 通过Connect方法的异步版本实现
socket.BeginConnect(remoteEP, null, null);
if(!socket.Connected) {
// 自定义超时逻辑
}
2.2 缓冲区参数
- ReceiveBufferSize:接收缓冲区大小
csharp
socket.ReceiveBufferSize = 65536; // 64KB
// 影响内核网络栈的接收缓冲区
- SendBufferSize:发送缓冲区大小
csharp
socket.SendBufferSize = 32768; // 32KB
// 影响内核网络栈的发送缓冲区
2.3 UDP专用参数
- EnableBroadcast:启用广播
csharp
udpSocket.EnableBroadcast = true;
// 允许发送到广播地址(如255.255.255.255)
- 多播设置
csharp
// 加入多播组
udpSocket.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.AddMembership,
new MulticastOption(IPAddress.Parse("224.0.0.1")));
// 设置多播TTL
udpSocket.SetSocketOption(SocketOptionLevel.IP,
SocketOptionName.MulticastTimeToLive,
2); // 最多跨越2个路由器
3、C# 中的Socket API
3.1 Socket(构造函数)
csharp
public Socket(SocketInformation socketInformation);
public Socket(SocketType socketType, ProtocolType protocolType);
public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType);
常见的 Socket 对象创建实例:
csharp
// 监控 ip4 地址,套接字类型为 TCP ,协议类型为 TCP
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
3.1.1 SocketType
指定 Socket 类的实例表示的套接字类型。
SocketType | 对应的ProtocolType | 描述 |
---|---|---|
Unknown | Unknown | 指定未知的 Socket 类型 |
Stream(使用字节流) | Tcp | 支持可靠、双向、基于连接的字节流 |
Dgram(使用数据报) | Udp | 面向无连接 |
Raw | Icmp、lgmp | 支持对基础传输协议的访问 |
Rdm | 支持无连接、面向消息、以可靠方式发送的消息, | |
Seqpacket | 在网络上提供排序字节流的面向连接且可靠的双向传输 |
3.1.2 ProtocolType
表示协议类型,是一个 enum 类型。
其中枚举重复值,常见用途:
- 为旧值提供别名
- 向后兼容
- 提供更清晰的命名选择
枚举值 | 值 | 协议说明 |
---|---|---|
Unknown | -1 | 未知协议类型 |
Unspecified | 0 | 未指定的协议 |
IP | 0 | Internet 协议 (IP) - 原始 IP 数据包 |
IPv6HopByHopOptions | 0 | IPv6 逐跳选项头 |
Icmp | 1 | Internet 控制消息协议 (ICMP) |
Igmp | 2 | Internet 组管理协议 (IGMP) |
Ggp | 3 | 网关到网关协议 (GGP) |
IPv4 | 4 | IPv4 协议 |
Tcp |
6 | 传输控制协议 (TCP) |
Pup | 12 | PARC 通用数据包协议 (PUP) |
Udp |
17 | 用户数据报协议 (UDP) |
Idp | 22 | Internet 数据报协议 (IDP) |
IPv6 | 41 | IPv6 协议 |
IPv6RoutingHeader | 43 | IPv6 路由头 |
IPv6FragmentHeader | 44 | IPv6 分片头 |
IPSecEncapsulatingSecurityPayload | 50 | IPv6 封装安全负载 (ESP) 头 |
IPSecAuthenticationHeader | 51 | IPv6 认证头 (AH) |
IcmpV6 | 58 | ICMP for IPv6 |
IPv6NoNextHeader | 59 | IPv6 无下一个头 |
IPv6DestinationOptions | 60 | IPv6 目的选项头 |
ND | 77 | Net Disk 协议 (非正式) |
Raw | 255 | 原始 IP 数据包 |
Ipx | 1000 | Internet 数据包交换协议 (IPX) |
Spx | 1256 | 顺序数据包交换协议 (SPX) |
SpxII | 1257 | SPX 版本 2 |
3.1.3 AddressFamily
表示使用的网络寻址方案
枚举值 | 值 | 协议说明 | 使用场景 |
---|---|---|---|
Unknown | -1 | 未知地址族 | 保留值 |
Unspecified | 0 | 未指定的地址族 | 通用情况 |
Unix | 1 | Unix本地通信 | Unix域Socket |
InterNetwork |
2 | IPv4地址族 | 最常用的IPv4网络 |
ImpLink | 3 | ARPANET IMP地址 | 历史遗留 |
Pup | 4 | PUP协议地址 | Xerox PUP网络 |
Chaos | 5 | MIT CHAOS协议 | 历史遗留 |
NS | 6 | Xerox NS协议 | Xerox网络系统 |
Ipx | 6 | IPX/SPX地址 | Novell网络 |
Iso | 7 | ISO协议 | OSI协议族 |
Osi 7 | OSI协议 | 同Iso | |
Ecma | 8 | ECMA协议 | 欧洲计算机制造商协会 |
DataKit | 9 | Datakit协议 | AT&T Datakit |
Ccitt 10 | CCITT协议 | TU-T协议 | |
Sna | 11 | IBM SNA地址 | IBM系统网络架构 |
DecNet | 12 DECnet地址 | Digital Equipment Corporation网络 | |
DataLink | 13 | 直接数据链路接口 | 数据链路层访问 |
Lat | 14 | LAT地址 | Local Area Transport |
HyperChannel | 15 | NSC Hyperchannel | Network Systems Corporation |
AppleTalk | 16 | AppleTalk地址 | Apple网络协议 |
NetBios | 17 | NetBios地址 | Windows网络基本输入输出系统 |
VoiceView | 18 | VoiceView地址 | VoiceView音频协议 |
FireFox | 19 | FireFox协议 | 非标准协议 |
Banyan | 21 | Banyan VINES | Banyan虚拟网络系统 |
Atm | 22 | 本机ATM服务 | 异步传输模式 |
InterNetworkV6 |
23 | IPv6地址族 | IPv6网络 |
Cluster | 24 | Microsoft Wolfpack | Microsoft集群服务 |
Ieee12844 | 25 | IEEE 1284.4工作组 | 打印机共享协议 |
Irda | 26 | IrDA地址 | 红外数据协会 |
NetworkDesigners | 28 | Network Designers OSI网关 | 网络设计者协议 |
Max | 29 | 最大地址族 | 保留值 |
3.2 Bind()
绑定本地IP和端口,你将在在本地创建 IPEndPoint 对象,拥有此 ip:post 的访问权限。目的是绑定本地机器的某个端口,所有经过此端口的数据就归你管了。
csharp
public void Bind (System.Net.EndPoint localEP);
使用实例:
csharp
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress iP = IPAddress.Parse("127.0.0.1");
serverSocket.Bind(new IPEndPoint(iP, 2300))
3.3 Connect()
与远程主机建立连接。Connect() 有四个重载方法,不必关注,只需知道,必需提供 IP 和 Post 两个值。
csharp
IPAddress iP = IPAddress.Parse("127.0.0.1");
IPEndPoint iPEndPoint = new IPEndPoint(iP, 2300);
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //创建与远程主机的连接
serverSocket.Connect(iPEndPoint);
3.4 Listen() (TCP)
监控所有发送到此主机的、特定端口的连接请求。服务端使用,客户端不需要。
csharp
public void Listen (int backlog);
使用 Bind() 后,使用 Listen() 方法进行监控,backlog 参数指定可排队等待接受的传入连接的数量,即挂起的连接队列的最大长度。
3.5 Accept() (TCP)
Accept() 以同步方式监听套接字,在连接请求队列中提取第一个挂起的连接请求,然后创建并返回一个新的 Socket 对象。
csharp
//创建终结点(EndPoint)
IPAddress ip = IPAddress.Any;
IPEndPoint ipe = new IPEndPoint(ip, 8000);
//创建 socket 并开始监听
Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(ipe);
serverSocket.Listen(10);//开始监听
//接受到client连接,为此连接建立新的socket,并接受信息
Socket temp = serverSocket.Accept();//为新建连接创建新的socket
注意的是,每次建立连接是一个 Accept() 对象,如果你要进行 服务器-客户端互相通讯,应使用同一个 Accept() 对象。每个 Accept 对象都是 从客户端请求建立开始的,期间只要使用同一个 Accept 对象,都可以进行数据传输。
3.6 Receive() 与 Send()
- Receive() :接收信息
csharp
string recvStr = "";
byte[] recvBytes = new byte[1024];
int bytes;
bytes = socket.Receive(recvBytes, recvBytes.Length, 0);//从客户端接受信息
recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes);
- Send() :发送信息
csharp
string str = "hello";
byte[] a = Encoding.UTF8.GetBytes(str);
send = socket.Send(a, 0);
3.7 ReceiveFrom()与SendTo()
- ReceiveFrom()
特性 | Receive | ReceiveFrom |
---|---|---|
连接要求 | 需要已建立连接 (面向连接) | 无需预先连接 (无连接) |
适用协议 | TCP UDP | |
来源获取 | 无法获取发送方信息 | 可获取发送方的端点信息 |
方法签名 | int Receive(byte[] buffer) | int ReceiveFrom(byte[] buffer, ref EndPoint remoteEP) |
典型用途 | 可靠数据流传输 | 数据报接收,特别是需要回复的场景 |
- SendTo()
特性 | Send | SendTo |
---|---|---|
连接要求 | 需要已建立连接 (面向连接) | 无需预先连接 (无连接) |
适用协议 | TCP | UDP |
目标指定 | 在Connect时指定目标 | 每次发送时指定目标 |
性能 | 略高 (连接已建立) | 略低 (每次需解析地址) |
可靠性 | 高 (TCP保证送达和顺序) | 低 (UDP不保证送达和顺序) |
方法签名 | Send(byte[] buffer) | SendTo(byte[] buffer, EndPoint remoteEP) |
3.8 Shutdown()和Close()
释放资源,有 Accept 释放和 Socket 的释放。
- Shutdown()
- 向对方发送FIN包,启动TCP的正常关闭握手流程
- 非破坏性操作,Socket对象仍然有效
- 允许完成正在进行的数据传输
- 遵循TCP协议的四次挥手过程
- 对方应用会收到EOF(Receive返回0)
csharp
public void Shutdown (System.Net.Sockets.SocketShutdown how);
SocketShutdown:
值 | 描述 |
---|---|
Send | 禁止对此发送Socket |
Receive | 禁用对此接收Socket |
Both | 禁用发送和接收对此Socket |
- Close()
- 破坏性操作,Socket对象不再可用
- 可能丢失发送/接收缓冲区中的数据
- 对方可能收到ConnectionReset错误
- 立即释放系统资源
- 最佳实践
csharp
void SafeClose(Socket socket)
{
try
{
// 1. 先优雅关闭通信通道
if(socket.Connected)
{
socket.Shutdown(SocketShutdown.Both);
}
// 2. 设置linger选项确保数据发送
socket.LingerState = new LingerOption(true, 5); // 等待5秒
// 3. 最终释放资源
socket.Close();
}
catch(Exception ex)
{
// 记录日志
Debug.WriteLine($"关闭Socket异常: {ex.Message}");
// 确保资源释放
socket?.Close();
}
}
4、IPAddress 和 IPEndPoint
4.1 IPAddress
用来处理IP地址、转换IP地址
IPAddress.Parse() 方法可以把以小数点隔分的十进制 IP 表示转化成 IPAddress 类。
csharp
IPAddress ip = IPAddress.Parse("127.0.0.1");//把ip地址字符串转换为IPAddress类型的实例
IPAddress提供4个只读字段:
- Any 用于代表本地系统可用的任何IP地址
- Broadcase用于代表本地网络的IP广播地址
- Loopback用于代表系统的回送地址
- None用于代表系统上没有网络接口
4.2 IPEndPoint
表示IPAddress对象与端口的绑定
csharp
IPAddress ip = IPAddress.Any; //把ip地址字符串转换为IPAddress类型的实例
IPEndPoint ipe = new IPEndPoint(ip, 8000);//用指定的端口和ip初始化IPEndPoint类的新实例