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()等)是跨平台/网络通信的基础,必须正确使用。
理解这些参数的细节后,才能灵活应对不同场景(如高并发服务器、实时数据传输)的开发需求。