Linux网络编程基础-简易TCP服务器框架
1. 引入头文件
#include<iostream>
#include<sys/types.h> // 系统数据类型(如 pid_t、size_t)
#include<sys/socket.h> // socket 函数及数据结构(socket(), bind(), listen() 等)
#include<netinet/in.h> // sockaddr_in 结构体定义,IPV4 地址
#include<arpa/inet.h> // IP 地址转换函数(inet_pton)
#include<signal.h> // 信号处理(signal())
#include<unistd.h> // POSIX 系统调用,如 close(), sleep()
#include<stdlib.h> // 标准库函数,如 atoi()
#include<assert.h> // assert() 断言函数,用于调试
#include<string.h> // 字符串函数,如 bzero()
2. 全局变量与信号处理
static bool stop = false; // 全局标志变量,控制主循环是否退出
handle_term(int sig)
:信号处理函数
static void handle_term(int sig)
{stop = true;
}
- 参数
sig
是信号编号(比如SIGTERM
对应 15)。 - 这个函数的作用是:当接收到某个信号(如 kill -15)时,把
stop
设置为true
,让主循环停止。
3. 主函数部分
函数声明和参数
int main(int argc, char* argv[])
argc
:命令行参数个数(包括程序本身)argv[]
:命令行参数数组(argv[0] 是程序名)
例如运行:
./myserver 127.0.0.1 8888 5
argc = 4
argv[1] = "127.0.0.1"
argv[2] = "8888"
argv[3] = "5"
注册信号处理函数
signal(SIGTERM, handle_term);
- 作用:当收到
SIGTERM
信号(编号 15)时,调用handle_term()
。 - 实现优雅关闭服务器(如用户
kill
程序时能正常释放资源)
检查参数是否合法
if(argc <= 3)
{std::cout << "usage: " << basename(argv[0]) << " ip_address port_number backlog" << std::endl;return 1;
}
-
如果参数不足,输出用法提示并退出。
-
basename(argv[0])
返回程序名(不含路径) -
示例输出:
usage: myserver 127.0.0.1 8888 5
解析命令行参数
const char* ip = argv[1]; // 获取 IP 字符串
int port = atoi(argv[2]); // 将字符串转为 int 类型的端口号
int backlog = atoi(argv[3]); // 将字符串转为 int 类型的连接队列大小
atoi()
(ASCII to int)将字符串转换为整数backlog
控制的是listen()
函数中最多能有多少个“未accept的连接”排队
创建 socket 套接字
int sock = socket(PF_INET, SOCK_STREAM, 0);
assert(sock >= 0);
socket()
参数解释:
int socket(int domain, int type, int protocol);
参数 | 值 | 含义 |
---|---|---|
domain | PF_INET | 使用 IPv4 协议族(也可写 AF_INET ,等价) |
type | SOCK_STREAM | 表示使用 TCP(面向连接) |
protocol | 0 | 默认协议(TCP 默认就是 0) |
- 成功返回 socket 文件描述符(int 型)
assert()
确保创建成功,否则程序直接崩溃(用于调试)
创建并初始化地址结构
struct sockaddr_in address;
bzero(&address, sizeof(address)); // 将地址结构全部清零
address.sin_family = AF_INET; // 协议族:IPv4
inet_pton(AF_INET, ip, &address.sin_addr); // 将 IP 字符串转为网络字节序整数
address.sin_port = htons(port); // 将主机字节序转为网络字节序
说明:
-
sockaddr_in
是用于 IPv4 的 socket 地址结构。 -
bzero()
是memset(&address, 0, sizeof(address))
的简化版,作用是清空结构体。 -
inet_pton(AF_INET, ip, &address.sin_addr)
:- 将 IP 地址字符串(如 “127.0.0.1”)转为网络字节序(用于通信)
-
htons(port)
:- 将主机字节序的端口号转为网络字节序(统一使用大端)
绑定地址
int ret = bind(sock, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
bind()
参数:
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
sockfd
:socket 描述符addr
:地址结构(需要强转为sockaddr*
)addrlen
:地址结构大小
作用:把 socket 绑定到你指定的本地地址(IP + 端口号)
开始监听连接
ret = listen(sock, backlog);
assert(ret != -1);
listen()
参数:
int listen(int sockfd, int backlog);
sockfd
:socket 描述符backlog
:等待连接的队列长度(最大连接排队数)
作用:把 socket 变为监听状态,准备接受客户端连接
主循环逻辑(等待 SIGTERM)
while(!stop)
{sleep(1);
}
- 死循环中每秒 sleep 一下(CPU 负载低)
- 一旦收到 SIGTERM(
stop == true
),跳出循环 - 注意这里没有真正处理任何连接,只是为了演示信号控制退出流程
程序清理和退出
close(sock);
return 0;
close(sock)
:关闭 socket,释放资源return 0
:正常退出程序
总结:完整程序做了什么?
步骤 | 作用 |
---|---|
创建 socket | 建立 TCP 套接字 |
设置地址结构 | 设置 IP、端口 |
bind | 把 socket 绑定到地址 |
listen | 开始监听 |
signal 注册 | 收到信号时修改 stop 标志 |
while 循环 | 不断 sleep,等待信号中断 |
close | 收到信号后关闭 socket,退出程序 |
测试