Linux网络编程基础-简易TCP服务器框架

1. 引入头文件

cpp 复制代码
#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. 全局变量与信号处理

cpp 复制代码
static bool stop = false; // 全局标志变量,控制主循环是否退出

handle_term(int sig):信号处理函数

cpp 复制代码
static void handle_term(int sig)
{
    stop = true;
}
  • 参数 sig 是信号编号(比如 SIGTERM 对应 15)。
  • 这个函数的作用是:当接收到某个信号(如 kill -15)时,把 stop 设置为 true,让主循环停止。

3. 主函数部分

函数声明和参数

cpp 复制代码
int main(int argc, char* argv[])
  • argc:命令行参数个数(包括程序本身)
  • argv[]:命令行参数数组(argv[0] 是程序名)

例如运行:

bash 复制代码
./myserver 127.0.0.1 8888 5
  • argc = 4
  • argv[1] = "127.0.0.1"
  • argv[2] = "8888"
  • argv[3] = "5"

注册信号处理函数

cpp 复制代码
signal(SIGTERM, handle_term);
  • 作用:当收到 SIGTERM 信号(编号 15)时,调用 handle_term()
  • 实现优雅关闭服务器(如用户 kill 程序时能正常释放资源)

检查参数是否合法

cpp 复制代码
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

解析命令行参数

cpp 复制代码
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 套接字

cpp 复制代码
int sock = socket(PF_INET, SOCK_STREAM, 0);
assert(sock >= 0);
socket() 参数解释:
cpp 复制代码
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() 确保创建成功,否则程序直接崩溃(用于调试)

创建并初始化地址结构

cpp 复制代码
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)

    • 将主机字节序的端口号转为网络字节序(统一使用大端)

绑定地址

cpp 复制代码
int ret = bind(sock, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
bind() 参数:
cpp 复制代码
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);
  • sockfd:socket 描述符
  • addr:地址结构(需要强转为 sockaddr*
  • addrlen:地址结构大小

作用:把 socket 绑定到你指定的本地地址(IP + 端口号)


开始监听连接

cpp 复制代码
ret = listen(sock, backlog);
assert(ret != -1);
listen() 参数:
cpp 复制代码
int listen(int sockfd, int backlog);
  • sockfd:socket 描述符
  • backlog:等待连接的队列长度(最大连接排队数)

作用:把 socket 变为监听状态,准备接受客户端连接


主循环逻辑(等待 SIGTERM)

cpp 复制代码
while(!stop)
{
    sleep(1);
}
  • 死循环中每秒 sleep 一下(CPU 负载低)
  • 一旦收到 SIGTERM(stop == true),跳出循环
  • 注意这里没有真正处理任何连接,只是为了演示信号控制退出流程

程序清理和退出

cpp 复制代码
close(sock);
return 0;
  • close(sock):关闭 socket,释放资源
  • return 0:正常退出程序

总结:完整程序做了什么?

步骤 作用
创建 socket 建立 TCP 套接字
设置地址结构 设置 IP、端口
bind 把 socket 绑定到地址
listen 开始监听
signal 注册 收到信号时修改 stop 标志
while 循环 不断 sleep,等待信号中断
close 收到信号后关闭 socket,退出程序

测试