Socket 套接字常用方法
文章目录
- Socket 套接字常用方法
- 一、核心数据类型与常量
- 二、TCP 套接字方法及参数详解
- 1. 创建套接字:`socket()`
- 2. 绑定地址:`bind()`
- 3. 监听连接:`listen()`
- 4. 接受连接:`accept()`
- 5. 发起连接:`connect()`
- 6. 数据传输:`send()` 与 `recv()`
- 三、UDP 套接字方法及参数详解
- 1. 数据传输:`sendto()` 与 `recvfrom()`
- 四、辅助函数:地址转换与选项设置
- 1. IP 地址转换(`inet_pton()` 与 `inet_ntop()`)
- 2. 套接字选项:`setsockopt()` 与 `getsockopt()`
- 五、总结
Socket 套接字常用方法
在 Linux C++ 套接字(Socket)编程中,理解函数参数的含义和类型是正确实现网络通信的关键。以下是对常用方法的参数细节、类型说明及使用场景的详细补充:
一、核心数据类型与常量
在深入函数参数前,先明确几个基础类型和重要的数据结构与头文件:
类型/常量 | 含义说明 |
---|---|
int (套接字描述符) | 套接字的唯一标识,类似文件描述符(fd ),用于后续所有操作的句柄。 |
sa_family_t | 协议族类型(如 AF_INET 表示 IPv4,AF_INET6 表示 IPv6,AF_UNIX 表示本地套接字)。 |
in_addr_t | 32位无符号整数,用于存储 IPv4 地址的网络字节序表示。 |
socklen_t | 用于表示地址结构长度的整数类型(通常是 uint32_t 的别名)。 |
htons() /htonl() | 字节序转换函数:h (主机)→ n (网络),s (16位,端口)/l (32位,IP)。 |
INADDR_ANY | 特殊常量(0.0.0.0 ),表示绑定到本机所有可用网络接口(服务器常用)。 |
- 使用套接字需包含以下头文件:
#include <sys/socket.h> // 核心套接字函数
#include <netinet/in.h> // 网络地址结构(如sockaddr_in)
#include <arpa/inet.h> // 地址转换函数(如inet_pton)
#include <unistd.h> // 关闭套接字(close)
-
关键数据结构
sockaddr_in
:用于存储 IPv4 地址信息(IP + 端口 + 协议族)struct sockaddr_in {sa_family_t sin_family; // 协议族(AF_INET 表示IPv4)uint16_t sin_port; // 端口号(需用htons()转换为网络字节序)struct in_addr sin_addr; // IP地址(需用inet_pton()转换)char sin_zero[8]; // 填充字段,通常设为0 };
二、TCP 套接字方法及参数详解
1. 创建套接字:socket()
int socket(int domain, int type, int protocol);
- 参数详解:
domain
(协议族):AF_INET
:IPv4 协议(最常用)。AF_INET6
:IPv6 协议(支持更大地址空间)。AF_UNIX
/AF_LOCAL
:本地进程间通信(不通过网络,用于同一台机器的进程)。
type
(套接字类型):SOCK_STREAM
:流式套接字,对应 TCP 协议(可靠、面向连接、字节流)。SOCK_DGRAM
:数据报套接字,对应 UDP 协议(不可靠、无连接、数据报)。SOCK_RAW
:原始套接字,可直接操作底层协议(如 ICMP,需 root 权限)。
protocol
(协议):- 通常设为
0
,表示根据domain
和type
自动选择默认协议(如SOCK_STREAM
对应IPPROTO_TCP
)。 - 特殊场景需显式指定(如
IPPROTO_ICMP
用于 ping 程序)。
- 通常设为
- 返回值:成功返回非负套接字描述符,失败返回
-1
(需用perror()
查看错误原因)。
2. 绑定地址:bind()
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 参数详解:
sockfd
:socket()
返回的套接字描述符。addr
:指向通用地址结构sockaddr
的指针(需强制转换具体地址类型):- IPv4 用
struct sockaddr_in
(需转换为sockaddr*
)。 - IPv6 用
struct sockaddr_in6
。 - 本地通信用
struct sockaddr_un
。
- IPv4 用
addrlen
:addr
指向的地址结构的字节长度(如sizeof(struct sockaddr_in)
)。
- 常见错误:
- 端口被占用:
bind
失败,错误码EADDRINUSE
(可通过setsockopt()
加SO_REUSEADDR
选项解决)。 - 权限不足:绑定端口 < 1024 需 root 权限,错误码
EACCES
。
- 端口被占用:
3. 监听连接:listen()
int listen(int sockfd, int backlog);
- 参数详解:
sockfd
:已绑定的服务器套接字描述符(仅 TCP 有效)。backlog
:未完成连接队列(处于SYN_RCVD
状态)的最大长度。
注意:实际最大等待连接数可能受系统限制(如/proc/sys/net/core/somaxconn
,默认 128),超过后新连接会被拒绝。
- 作用:将套接字从“主动”状态转为“被动”状态,使其能接收客户端连接请求。
4. 接受连接:accept()
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- 参数详解:
sockfd
:处于监听状态的服务器套接字描述符。addr
:输出参数,用于存储客户端的地址信息(需预先分配内存,如struct sockaddr_in client_addr
)。
若不需要客户端地址,可设为NULL
。addrlen
:输入输出参数:- 输入:
addr
指向的地址结构长度(如sizeof(client_addr)
)。 - 输出:实际存储的客户端地址长度(可能小于输入值)。
- 输入:
- 特性:
- 阻塞模式下,
accept()
会一直等待,直到有新连接到达。 - 返回值是新的套接字描述符,用于与该客户端单独通信(原
sockfd
仍用于监听新连接)。
- 阻塞模式下,
5. 发起连接:connect()
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 参数详解:
sockfd
:客户端套接字描述符(socket()
创建,无需bind()
,系统会自动分配端口)。addr
:服务器的地址结构(包含 IP 和端口,与bind()
的addr
格式一致)。addrlen
:服务器地址结构的长度。
- 错误场景:
- 服务器未启动或端口错误:
ECONNREFUSED
。 - 网络不可达:
ENETUNREACH
。 - 超时(默认约 75 秒):
ETIMEDOUT
。
- 服务器未启动或端口错误:
6. 数据传输:send()
与 recv()
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
- 参数详解:
sockfd
:accept()
返回的客户端连接套接字(服务器)或connect()
后的客户端套接字。buf
:send()
:待发送数据的缓冲区(如char[]
或std::string
的数据指针)。recv()
:接收数据的缓冲区(需预先分配足够空间)。
len
:send()
:要发送的字节数(如strlen(msg)
或buf.size()
)。recv()
:缓冲区的最大容量(避免溢出)。
flags
:传输控制标志(通常为0
,表示默认行为):MSG_OOB
:发送/接收带外数据(紧急数据,用于优先级高的信息)。MSG_PEEK
:recv()
专用,查看数据但不从缓冲区移除(可用于预览)。MSG_WAITALL
:recv()
专用,阻塞直到接收完len
字节(但仍可能被信号中断)。
- 返回值:
- 成功:实际发送/接收的字节数(
send()
可能小于len
,需循环发送;recv()
可能小于len
,表示数据不足)。 0
:recv()
收到0
表示对方正常关闭连接(send()
返回0
通常无意义)。-1
:错误(如连接被重置ECONNRESET
)。
- 成功:实际发送/接收的字节数(
三、UDP 套接字方法及参数详解
UDP 无需连接,核心是 sendto()
和 recvfrom()
,其他方法(socket()
、bind()
)参数与 TCP 类似,但 type
需为 SOCK_DGRAM
。
1. 数据传输:sendto()
与 recvfrom()
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
- 新增参数详解:
dest_addr
(sendto()
):目标主机的地址结构(包含 IP 和端口)。src_addr
(recvfrom()
):输出参数,存储发送方的地址信息(用于回复)。addrlen
(sendto()
):目标地址结构的长度;(recvfrom()
)输入输出参数,同accept()
。
- 特性:
- 每次发送都需指定目标地址(无需提前连接)。
- 无确认机制,可能丢包、乱序,返回值仅表示数据已交给内核,不保证对方收到。
四、辅助函数:地址转换与选项设置
1. IP 地址转换(inet_pton()
与 inet_ntop()
)
解决字符串 IP(如 "192.168.1.1"
)与网络字节序二进制 IP 的转换:
// 字符串 → 二进制(网络字节序)
int inet_pton(int af, const char *src, void *dst);
// 二进制 → 字符串
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
- 参数:
af
:AF_INET
或AF_INET6
。src
/dst
:源/目标缓冲区。size
(inet_ntop()
):dst
的最大长度(如INET_ADDRSTRLEN
为 IPv4 字符串最大长度 16)。
示例:
struct sockaddr_in addr;
// 将 "127.0.0.1" 转换为网络字节序的二进制 IP
inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);char ip_str[INET_ADDRSTRLEN];
// 将二进制 IP 转换为字符串
inet_ntop(AF_INET, &addr.sin_addr, ip_str, INET_ADDRSTRLEN);
2. 套接字选项:setsockopt()
与 getsockopt()
用于设置/获取套接字的属性(如端口复用、超时时间):
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
- 常用参数:
level
:选项级别(SOL_SOCKET
表示通用套接字选项,IPPROTO_TCP
表示 TCP 特有选项)。optname
(常用):SO_REUSEADDR
:允许端口被立即复用(解决EADDRINUSE
错误)。SO_RCVTIMEO
/SO_SNDTIMEO
:设置接收/发送超时时间(需传入struct timeval
)。TCP_NODELAY
:禁用 Nagle 算法(减少小数据传输延迟)。
示例(允许端口复用):
int opt = 1;
setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
五、总结
套接字函数的参数设计围绕“标识(描述符)、地址(IP+端口)、数据(缓冲区)、控制选项(标志)”四大核心要素:
-
TCP 流程:
socket()
→bind()
→listen()
→accept()
(服务器);socket()
→connect()
(客户端);之后用send()
/recv()
通信。 -
UDP 流程:
socket()
→bind()
(可选);用sendto()
/recvfrom()
直接通信。 -
字节序转换(
htons()
等)和地址转换(inet_pton()
等)是跨平台/网络通信的基础,必须正确使用。
→
accept()(服务器);
socket()→
connect()(客户端);之后用
send()/
recv()` 通信。
-
UDP 流程:
socket()
→bind()
(可选);用sendto()
/recvfrom()
直接通信。 -
字节序转换(
htons()
等)和地址转换(inet_pton()
等)是跨平台/网络通信的基础,必须正确使用。
理解这些参数的细节后,才能灵活应对不同场景(如高并发服务器、实时数据传输)的开发需求。