Socket 套接字常用方法

发布于:2025-08-14 ⋅ 阅读:(16) ⋅ 点赞:(0)

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,表示根据 domaintype 自动选择默认协议(如 SOCK_STREAM 对应 IPPROTO_TCP)。
      • 特殊场景需显式指定(如 IPPROTO_ICMP 用于 ping 程序)。
  • 返回值:成功返回非负套接字描述符,失败返回 -1(需用 perror() 查看错误原因)。
2. 绑定地址:bind()
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
  • 参数详解
    • sockfdsocket() 返回的套接字描述符。
    • addr:指向通用地址结构 sockaddr 的指针(需强制转换具体地址类型):
      • IPv4 用 struct sockaddr_in(需转换为 sockaddr*)。
      • IPv6 用 struct sockaddr_in6
      • 本地通信用 struct sockaddr_un
    • addrlenaddr 指向的地址结构的字节长度(如 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);
  • 参数详解
    • sockfdaccept() 返回的客户端连接套接字(服务器)或 connect() 后的客户端套接字。
    • buf
      • send():待发送数据的缓冲区(如 char[]std::string 的数据指针)。
      • recv():接收数据的缓冲区(需预先分配足够空间)。
    • len
      • send():要发送的字节数(如 strlen(msg)buf.size())。
      • recv():缓冲区的最大容量(避免溢出)。
    • flags:传输控制标志(通常为 0,表示默认行为):
      • MSG_OOB:发送/接收带外数据(紧急数据,用于优先级高的信息)。
      • MSG_PEEKrecv() 专用,查看数据但不从缓冲区移除(可用于预览)。
      • MSG_WAITALLrecv() 专用,阻塞直到接收完 len 字节(但仍可能被信号中断)。
  • 返回值
    • 成功:实际发送/接收的字节数(send() 可能小于 len,需循环发送;recv() 可能小于 len,表示数据不足)。
    • 0recv() 收到 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_addrsendto()):目标主机的地址结构(包含 IP 和端口)。
    • src_addrrecvfrom()):输出参数,存储发送方的地址信息(用于回复)。
    • addrlensendto()):目标地址结构的长度;(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);
  • 参数
    • afAF_INETAF_INET6
    • src/dst:源/目标缓冲区。
    • sizeinet_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() 等)是跨平台/网络通信的基础,必须正确使用。

理解这些参数的细节后,才能灵活应对不同场景(如高并发服务器、实时数据传输)的开发需求。


网站公告

今日签到

点亮在社区的每一天
去签到