TCP并发服务器构建

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

TCP并发服务器

         单循环服务器:服务端同一时刻只能处理一个客户端的任务

        并发服务器:服务端同一时刻可以处理多个客户端的任务

1 TCP服务端并发模型

1.1 多进程(fork)

        父进程监听端口,每接一个客服端就fork子进程处理该连接

        优点:个进程独立,稳定性和安全性高(子进程崩溃不影响父进程)

        缺点:

1.2 多线程(pthread)

        主线程监听端口,每接一个客户端就创建子线程处理该连接

        优点:线程开销资源比进程小,相同资源环境下,并发量比进程大。

                   内存共享方便

1.3 线程池

        为了解决多线程或者多进程模型,在服务器运行过程,频繁创建和销毁线程(进程)带来的时间消耗问题。

        基于生产者和消费者编程模型,以及任务队列等,实现的一套多线程框架。     

1.4 I/O多路复用

2.多进程模型

操作步骤:

        1.主进程从初始化:创建监听socket()、绑定端口bind()、设置监听队列listen()。

        2.循环等待连接:主进程accept()阻塞等待客户端连接,成功后获得通信套接字。

        3.创建子进程处理连接:主进程调用fork()创建子进程,子进程继承通信套接字,负责与客户端收发数据(recv()/send())。

        主进程关闭监听套接字,回到accept()继续等待新连接。

        4。子进程资源回收:子进程处理完客户端请求后退出,主进程需通过wait()/waitpid()回收子进程资源,避免产生“僵尸进程”

优点:个进程独立,稳定性和安全性高(子进程崩溃不影响父进程)

缺点:

 3.多线程 

        主线程创建监听套接字,循环等待客户端连接

        每接收到一个客户端连接,创建新线程处理该客户端的通信

        子线程负责与客户端的读写交互,主线程继续等待新连接

        优点:

        缺点:

4.  IO多路复用

I-->O:fd
      对多个文件描述符的读写可以复用一个进程。

      在不创建新的进程和线程的前提下,使用一个进程实现对多个文件读写的同时监测。

      fgets(stdin);
      recv(connfd);
      阻塞IO模式:
               1. 多个任务之间是同步的效果

(1)select

(2)poll

(3)epoll
      

4.1 select实现IO多路复用

        select通过监控多个文件描述符的读写/异常事件,实现单进程/线程同时处理多个I/O操作,核心是阻塞等待多个FD就绪,避免为每个FD单独创建进程线程。

4.1.1 操作流程

        1. 创建文件描述符集合                  fd_set

        2. 添加关注的文件描述符到集合   FD_SET();

        3. 使用select传递集合表给内核,内核开始监测事件  select()

        4. 当内核监测到事件时,应用层select将解除阻塞,并获得相关的事件结果

        5. 根据select返回的结果做不同的任务处理

关键函数与参数

#include <sys/select.h>

核心函数:监控 FD 集合,返回就绪 FD 数量(超时返回0,错误返回-1)

int select(int nfds, fd_set *readfds, fd_set *writedfds, 
fd_set *exceptfds, struct timeval *timeout);

参数:
        nfds : 关注的最大文件描述符+1
        readfds:读事件的文件描述符集合
        writefds:写事件的文件描述符集合
        exceptfds:其他事件的文件描述符集合
        timeout:设置select监测时的超时时间
        NULL : 不设置超时时间(select一直阻塞等待)

辅助宏:操作 fd_set 集合

void FD_ZERO(fd_set *set);           清空集合(初始化必做)
void FD_SET(int fd, fd_set *set);    将 FD 加入集合
void FD_CLR(int fd, fd_set *set);    将 FD 从集合中移除
int FD_ISSET(int fd, fd_Set *set);   检查 FD 是否在“就绪”集合中(返回0表示就绪)

select特点:

        1.使用位图实现对文件描述符集合的保存,最多允许同时监测1024个文件描述符。

        2.需要应用层和内核层的反复数据(文件描述符集合表)拷贝。

        3.返回的集合表需要需要遍历寻找到达的事件。

        4.只能工作在水平触发模式(低速模式),不能工作在边沿触发模式(高速模式)。

epoll特点:

        1.使用红黑树(二叉树)实现文件描述符集合的存储,没有文件描述符上限限制,提高查找小琉球。

        2.

epoll:

1. 创建文件描述符集合 : int epoll_create(int size);

2. 添加关注的文件描述符:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

3. epoll通知内核开始进行事件监测 :int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

4. epoll返回时,获取到达事件的结果

5. 根据到达事件做任务处理

函数原型:

#include <sys/epoll.h>

int eopll_create(int size);

功能:

        通知内核创建文件描述符集合
参数: 
         size:监测的文件描述符个数
返回值:
      成功:文件描述符(代表内核创建的集合)
      失败:-1,errno

函数原型:

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

功能:

        对epoll的文件描述符集合进行操作
参数:
        epfd:由epoll_create()创建的epoll文件描述符
        op:对文件描述符集合进行的操作
                EPOLL_CTL_ADD  : 添加文件描述符到集合
                EPOLL_CTL_MOD : 修改集合中的文件描述符
                EPOLL_CTL_DEL   :删除集合中的文件描述符
        fd:要操作的文件描述符
        event:文件描述符对应的事件
         typedef union epoll_data {
               void        *ptr;
               int          fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;      /* Epoll events */              
               epoll_data_t data;        /* User data variable */
           };
           events:文件描述符的事件:
                        EPOLLIN: 读事件
                        EOPLLOUT:写事件
           data.fd : 关注的文件描述符

返回值:
         成功:0
         失败:-1
int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);
功能:通知内核开始监测文件描述符的事件
参数:
         epfd:监测的文件描述符集合
         events:保存返回的到达事件的结果(数组)
                      struct epoll_event evs[MAX_FD_CNT];
                      evs;
        maxevents:最大的事件个数
        timeout:监测的超时时间
                          -1 :不设置超时(一直阻塞)

返回值:
      成功:到达的事件的个数
      失败:-1

                        
 

epoll

1.创建文件描述符集合:int epoll_create(int size);

int epoll_create(int size);

功能:通知内核创建文件描述符集合

参数:

        size:检测的文件描述符个数

返回值:

        成功:文件描述符(代表内核创建的集合)

        失败:-1,errno

int epoll_ctl(int epfd, int op, struct epoll_event *evtt 

ent)