TCP编程核心API

socket()函数

  • 创建通信端点

函数原型

c 复制代码
#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

参数

参数 类型 说明 常用取值
domain int 协议族/地址族 AF_INET(IPv4), AF_INET6(IPv6), AF_UNIX(本地)
type int 套接字类型 SOCK_STREAM(TCP), SOCK_DGRAM(UDP)
protocol int 具体协议 通常填0,系统自动选择

返回值与错误处理

c 复制代码
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) {
    perror("socket创建失败");
    // 常见错误:
    // EACCES - 权限不足(如创建原始套接字需要root权限)
    // EAFNOSUPPORT - 不支持的地址族
    // ENFILE/EMFILE - 文件描述符耗尽(嵌入式系统常见)
    exit(EXIT_FAILURE);
}

bind()函数

  • 绑定地址与端口

函数原型

c 复制代码
#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数

参数 说明 注意事项
sockfd socket()返回的文件描述符 必须是未绑定的socket
addr 地址结构体指针 需要转换为struct sockaddr *
addrlen 地址结构体长度 sizeof(struct sockaddr_in)

地址结构快速初始化

c 复制代码
#include <netinet/in.h>
#include <arpa/inet.h>

struct sockaddr_in server_addr;

// 清空结构体(重要!避免脏数据)
memset(&server_addr, 0, sizeof(server_addr));

// 设置地址族
server_addr.sin_family = AF_INET;  // IPv4

// 设置端口(必须转换为网络字节序)
server_addr.sin_port = htons(8080);

// 设置IP地址(三种方式)
// 方式1:绑定到所有网络接口(服务器常用)
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

// 方式2:绑定到指定IP(字符串转网络字节序)
inet_pton(AF_INET, "192.168.1.100", &server_addr.sin_addr);

// 方式3:绑定到指定IP(数值转网络字节序)
server_addr.sin_addr.s_addr = inet_addr("192.168.1.100");

// 执行绑定
if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
    perror("bind失败");
    // 常见错误:
    // EADDRINUSE - 地址已被使用(端口被占用)
    // EACCES - 权限不足(绑定1024以下端口需要root)
    // EINVAL - socket已绑定
    close(sockfd);
    exit(EXIT_FAILURE);
}

地址重用优化

  • 解决"Address already in use"问题
c 复制代码
// 开发调试时常用,允许快速重启服务器
int reuse = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
    perror("setsockopt SO_REUSEADDR");
}

// 嵌入式设备中还可设置地址重用超时
struct timeval timeout = {1, 0}; // 1秒
setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &timeout, sizeof(timeout));

listen()函数

  • 监听连接请求

函数原型

c 复制代码
#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);

参数详解

参数 说明 取值范围 建议值
sockfd 已绑定的socket文件描述符 - -
backlog 连接请求队列的最大长度 1~SOMAXCONN 嵌入式:5-20 服务器:128+
backlog
latex 复制代码
已完成三次握手队列(ESTABLISHED)
      │
      ▼     等待accept()处理的连接
┌─────────┐
│ 连接1   │ ← 客户端已完成握手,等待accept()
├─────────┤
│ 连接2   │
├─────────┤
│ 连接3   │
├─────────┤
│   ...   │
├─────────┤
│ 连接N   │  N = backlog
└─────────┘
      ▲
      │
半连接队列(SYN_RCVD)

示例

c 复制代码
// 设置监听队列大小
#define BACKLOG 10  // 嵌入式设备可设为较小值

if (listen(sockfd, BACKLOG) < 0) {
    perror("listen失败");
    // 常见错误:
    // EBADF - 无效的文件描述符
    // ENOTSOCK - 不是socket文件描述符
    // EOPNOTSUPP - socket不支持listen操作
    close(sockfd);
    exit(EXIT_FAILURE);
}

printf("服务器开始监听端口 %d...\n", port);

accept()函数

  • 接受客户端连接

函数原型

c 复制代码
#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数

参数 说明 输入/输出
sockfd 监听socket的文件描述符 输入
addr 客户端地址信息结构体 输出(可设为NULL)
addrlen 地址结构体长度 输入输出(需初始化)

示例

c 复制代码
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
int client_fd;

// 阻塞等待客户端连接(常用模式)
client_fd = accept(sockfd, (struct sockaddr*)&client_addr, &addr_len);
if (client_fd < 0) {
    perror("accept失败");
    // 常见错误:
    // EBADF/EINVAL - 无效参数
    // ECONNABORTED - 连接已中止
    // EWOULDBLOCK - 非阻塞模式无连接
    return -1;
}

// 获取客户端信息
char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip));
int client_port = ntohs(client_addr.sin_port);
printf("接受来自 %s:%d 的连接\n", client_ip, client_port);
  • 非阻塞模式下的accept()
c 复制代码
// 设置监听socket为非阻塞
fcntl(sockfd, F_SETFL, O_NONBLOCK);

// 非阻塞accept循环
while (1) {
    client_fd = accept(sockfd, NULL, NULL);
    if (client_fd < 0) {
        if (errno == EWOULDBLOCK || errno == EAGAIN) {
            // 没有新连接,继续等待
            usleep(100000); // 休眠100ms
            continue;
        } else {
            // 真正的错误
            perror("accept错误");
            break;
        }
    }
    
    // 处理新连接
    handle_client(client_fd);
}

connect()函数

函数原型

c 复制代码
#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

示例

c 复制代码
// 客户端连接示例
struct sockaddr_in server_addr;

// 清空并初始化地址结构
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);  // 服务器端口

// 设置服务器地址(从命令行参数获取)
if (argc > 1) {
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
} else {
    inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
}

// 发起连接
if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
    perror("连接服务器失败");
    // 常见错误:
    // ECONNREFUSED - 连接被拒绝(服务器未启动)
    // ETIMEDOUT - 连接超时
    // ENETUNREACH - 网络不可达
    close(sockfd);
    return -1;
}

printf("成功连接到服务器\n");

连接超时设置

c 复制代码
#include <sys/time.h>

// 设置socket发送超时
struct timeval timeout = {5, 0};  // 5秒超时
setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout));

// 设置连接超时(非标准,但常用技巧)
// 方法1:使用非阻塞connect + select
fcntl(sockfd, F_SETFL, O_NONBLOCK);
connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

fd_set writefds;
FD_ZERO(&writefds);
FD_SET(sockfd, &writefds);
timeout.tv_sec = 5;
timeout.tv_usec = 0;

int ret = select(sockfd + 1, NULL, &writefds, NULL, &timeout);
if (ret <= 0) {
    // 超时或错误
    close(sockfd);
    return -1;
}

数据传输函数

c 复制代码
#include <unistd.h>
#include <sys/socket.h>

// 通用文件I/O(可用于socket)
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

// 专用socket I/O(提供更多控制)
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
函数类型 函数原型 特点 适用场景
通用I/O ssize_t read(int fd, void *buf, size_t count) 通用文件操作,可用于socket 简单数据传输
通用I/O ssize_t write(int fd, const void *buf, size_t count) 通用文件操作,可用于socket 简单数据传输
Socket专用 ssize_t recv(int sockfd, void *buf, size_t len, int flags) 提供更多控制选项 需要特殊处理的socket通信
Socket专用 ssize_t send(int sockfd, const void *buf, size_t len, int flags) 提供更多控制选项 需要特殊处理的socket通信
UDP专用 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) 获取发送方地址 UDP通信
UDP专用 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) 指定接收方地址 UDP通信

send()/recv()函数

函数原型

c 复制代码
#include <sys/socket.h>

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 int socket文件描述符
buf void* 数据缓冲区指针
len size_t 缓冲区长度
flags int 控制标志位(按位或组合)

flags常用选项

c 复制代码
// 常用flags标志位定义
#define MSG_PEEK      0x01    // 窥视数据但不从缓冲区移除
#define MSG_OOB       0x02    // 处理带外数据(紧急数据)
#define MSG_WAITALL   0x04    // 等待接收所有请求的数据
#define MSG_DONTWAIT  0x08    // 非阻塞操作
#define MSG_NOSIGNAL  0x10    // 发送失败时不产生SIGPIPE信号
#define MSG_MORE      0x20    // 有更多数据要发送(TCP_CORK相关)
相关推荐
czy87874752 小时前
LwIP 提供了三种主要的 API 接口,分别针对不同的应用场景(如实时性、易用性、资源占用等),开发者可根据需求选择。
网络协议
梁辰兴2 小时前
计算机网络基础:用户数据报协议 UDP
网络·网络协议·计算机网络·udp·用户数据报协议·计算机网络基础·梁辰兴
SmartRadio2 小时前
基于RK3568实现多电脑KVM共享方案(HDMI采集+虚拟USB键鼠+无缝切换+剪贴板/文件共享)
运维·服务器·网络·电脑·kvm·rk3568
147API2 小时前
Prompt Injection 怎么防:攻击面与工程防线(含安全 Checklist)
网络·安全·prompt
七夜zippoe3 小时前
HTTP协议深度解析与实现:从请求响应到HTTP/3的完整指南
python·网络协议·http·quic·帧结构
乾元3 小时前
社交工程 2.0:生成式 AI 驱动的高拟真钓鱼与认知对抗
网络·人工智能·安全·机器学习·架构
摆摊的豆丁3 小时前
FreeRTOS-Plus-TCP 协议支持与网络编程指南
网络·网络协议·tcp/ip·freertos
鱼跃鹰飞3 小时前
面试题:知道WebSocket协议吗?
网络·websocket·网络协议
啊阿狸不会拉杆3 小时前
《计算机操作系统》第十二章 - 保护和安全
开发语言·网络·c++·算法·安全·计算机组成原理·计算机操作系统