Linux网络编程_常见API

下面我给你逐行详细拆解 这 5 个 socket 核心 API,讲清它们的作用、参数含义、返回值、使用场景和关键细节,帮你彻底搞懂 TCP 网络编程的核心流程。


1. socket() ------ 创建套接字(客户端 + 服务器都要用)

作用

创建一个套接字文件描述符 (相当于网络通信的 "句柄"),是网络编程的第一步 。它定义了通信的地址族、套接字类型、协议类型

函数原型

cpp 复制代码
int socket(int domain, int type, int protocol);

参数详解

参数 含义 常用取值
domain 地址族(通信域),决定用哪种网络协议 AF_INET:IPv4 网络AF_INET6:IPv6 网络AF_UNIX:本地进程间通信(Unix 域)
type 套接字类型,决定通信的特性 SOCK_STREAM:TCP(面向连接、可靠、字节流)SOCK_DGRAM:UDP(无连接、不可靠、数据报)SOCK_RAW:原始套接字(直接操作 IP 层)
protocol 具体协议,一般填 0 让系统自动匹配 0:自动匹配(TCP 对应 IPPROTO_TCP,UDP 对应 IPPROTO_UDPIPPROTO_TCP:显式指定 TCPIPPROTO_UDP:显式指定 UDP

返回值

  • 成功:返回非负整数 (套接字文件描述符 sockfd
  • 失败:返回 -1,可通过 perror() 打印错误信息

关键要点

  • 客户端和服务器都必须先调用 socket() ,拿到 sockfd 才能进行后续操作。
  • SOCK_STREAM 对应 TCP,SOCK_DGRAM 对应 UDP,这是最常用的两种类型。

示例

cpp 复制代码
// 创建 IPv4 的 TCP 套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1) {
    perror("socket failed");
    exit(1);
}

2. bind() ------ 绑定地址(服务器端专用)

作用

套接字 sockfd 与一个具体的 IP 地址 + 端口号 绑定,让客户端能通过这个地址找到服务器。客户端一般不需要 bind()(系统会自动分配临时端口)。

函数原型

复制代码
int bind(int socket, const struct sockaddr *address, socklen_t address_len);

参数详解

参数 含义 关键说明
socket 要绑定的套接字 sockfd socket() 返回的文件描述符
address 指向地址结构体的指针 通用结构体 struct sockaddr,实际用 struct sockaddr_in(IPv4),需强制转换
address_len 地址结构体的长度 sizeof(struct sockaddr_in)

关键细节(必看)

  1. 地址结构体 struct sockaddr_in(IPv4 专用)

    cpp 复制代码
    struct sockaddr_in {
        sa_family_t    sin_family;   // 地址族,必须填 AF_INET
        in_port_t      sin_port;     // 端口号,需用 htons() 转网络字节序
        struct in_addr sin_addr;     // IP 地址
        char           sin_zero[8];  // 填充位,全填 0
    };
    • sin_port:端口号,必须用 htons() 转换(主机字节序 → 网络字节序,大端序)。
    • sin_addr.s_addr:IP 地址,常用 INADDR_ANY(监听本机所有网卡的 IP,如 127.0.0.1、局域网 IP 等)。
  2. 网络字节序转换

    • 端口 / IP 必须转网络字节序:htons()(端口)、htonl()(IP)
    • 接收时转主机字节序:ntohs()(端口)、ntohl()(IP)

返回值

  • 成功:返回 0
  • 失败:返回 -1(常见错误:端口被占用、权限不足)

示例

cpp 复制代码
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));  // 清空结构体
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);          // 绑定 8080 端口,转网络字节序
serv_addr.sin_addr.s_addr = INADDR_ANY;    // 监听所有网卡

// 绑定套接字
if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
    perror("bind failed");
    exit(1);
}

3. listen() ------ 开启监听(TCP 服务器专用)

作用

bind() 后的套接字从 "主动套接字" 转为 "被动监听套接字" ,让服务器开始等待客户端的连接请求。UDP 不需要 listen()(无连接,无需监听)。

函数原型

cpp 复制代码
int listen(int socket, int backlog);

参数详解

参数 含义 说明
socket 监听的套接字 sockfd bind() 后的套接字
backlog 等待连接的队列长度 全连接队列 的最大长度(已完成三次握手、等待 accept() 的连接数),不同系统有上限(如 Linux 一般最大 128)

返回值

  • 成功:返回 0
  • 失败:返回 -1

关键要点

  • 仅 TCP 服务器需要调用,调用后套接字进入监听状态
  • backlog 不是 "最大连接数",而是 "等待被接受的连接数",实际最大连接数受系统资源限制。

示例

cpp 复制代码
// 开启监听,队列长度 5
if (listen(sockfd, 5) == -1) {
    perror("listen failed");
    exit(1);
}

4. accept() ------ 接受连接(TCP 服务器专用)

作用

从监听队列中取出一个客户端连接 ,并创建一个新的套接字 (用于和该客户端单独通信)。原监听套接字继续保持监听状态,可接受其他客户端。UDP 不需要 accept()(无连接,无需建立连接)。

函数原型

cpp 复制代码
int accept(int socket, struct sockaddr* address, socklen_t* address_len);

参数详解

参数 含义 关键说明
socket 监听套接字 sockfd listen() 后的套接字
address 输出参数 用于存储客户端的地址信息 (IP + 端口),不需要可填 NULL
address_len 输入输出参数 传入时填 address 结构体的长度,返回时填实际长度,不需要可填 NULL

返回值

  • 成功:返回新的连接套接字 connfd(专门用于和该客户端通信)
  • 失败:返回 -1

关键要点

  • accept()阻塞函数:若无客户端连接,会一直阻塞,直到有连接到来。
  • 新的 connfd独立的套接字 ,服务器通过它和客户端收发数据,原 sockfd 继续监听。
  • 多客户端场景:服务器需循环调用 accept(),或用多线程 / IO 多路复用(select/poll/epoll)处理。

示例

cpp 复制代码
struct sockaddr_in cli_addr;
socklen_t cli_len = sizeof(cli_addr);

// 接受客户端连接,阻塞等待
int connfd = accept(sockfd, (struct sockaddr*)&cli_addr, &cli_len);
if (connfd == -1) {
    perror("accept failed");
    exit(1);
}

// 打印客户端 IP 和端口
printf("client connected: %s:%d\n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port));

5. connect() ------ 建立连接(TCP 客户端专用)

作用

客户端主动向服务器发起连接请求 ,完成 TCP 三次握手,建立可靠连接。UDP 不需要 connect() (无连接,可直接 sendto()/recvfrom() 通信)。

函数原型

cpp 复制代码
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数详解

参数 含义 说明
sockfd 客户端的套接字 sockfd 即客户端 socket() 创建的套接字
addr 服务器的地址结构体 需填服务器的 IP + 端口,同样用 struct sockaddr_in,强制转换为 struct sockaddr*
addrlen 地址结构体的长度 sizeof(struct sockaddr_in)

返回值

  • 成功:返回 0(连接建立完成)
  • 失败:返回 -1(常见错误:服务器未启动、IP / 端口错误、网络不通)

关键要点

  • 仅 TCP 客户端需要调用,调用后客户端和服务器的连接就建立了。
  • 客户端无需 bind(),系统会自动分配临时端口。
  • connect() 也是阻塞函数,直到连接成功或失败。

示例

cpp 复制代码
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);          // 服务器端口
// 服务器 IP,这里用 127.0.0.1(本地回环)
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
    perror("inet_pton failed");
    exit(1);
}

// 连接服务器
if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
    perror("connect failed");
    exit(1);
}

总结:TCP 客户端 / 服务器的完整流程

服务器端(TCP)

  1. socket() → 创建监听套接字
  2. bind() → 绑定 IP + 端口
  3. listen() → 开启监听
  4. accept() → 接受客户端连接,得到通信套接字
  5. read()/write()(或 recv()/send())→ 和客户端通信
  6. close() → 关闭套接字

客户端(TCP)

  1. socket() → 创建客户端套接字
  2. connect() → 连接服务器
  3. read()/write()(或 recv()/send())→ 和服务器通信
  4. close() → 关闭套接字

UDP 简化流程(无连接)

  • 服务器:socket()bind()recvfrom()/sendto()
  • 客户端:socket()sendto()/recvfrom()(无需 connect()
相关推荐
小北方城市网2 小时前
Spring Cloud Gateway 动态路由进阶:基于 Nacos 配置中心的热更新与版本管理
java·前端·javascript·网络·spring boot·后端·spring
STCNXPARM2 小时前
Linux-ARM-Bootloader概述
linux·运维·arm开发·uboot·bootloader
林深现海2 小时前
宇树 Go2 + NaVILA 全栈导航系统详解 (新手入门版)
linux·vscode·yolo·ubuntu·机器人
橙露2 小时前
Docker 容器化运维:镜像优化、容器编排与持久化存储方案
java·运维·docker
LUCIFER2 小时前
[驱动进阶——MIPI摄像头驱动(三)]rk3588+OV13855摄像头驱动加载过程详细解析第二部分——DPHY驱动+CSI驱动
linux·驱动开发
小尧嵌入式2 小时前
【Linux开发四】Linux中概念|MobaXterm和Filezilla软件使用|线程|互斥锁|读写锁
linux·运维·服务器·开发语言·数据结构
a努力。2 小时前
Spring Boot 4 全面拥抱 Jackson 3
java·运维·开发语言·spring boot·后端·spring·jenkins
峥嵘life2 小时前
Android16 EDLA【GTS】GtsUnofficialApisUsageTestCases存在fail项
android·linux·运维·学习