31-TCP并发

TCP并发服务器模型:

TCP并发服务器模型之多线程

1.解决TCP并发问题

2.因为线程占据独立栈空间,所以同时创建线程个数是有限的,如果并发客户端太多,系统有崩溃风险

3.一般搭配线程池来使用

四种常见的IO模型,

1.阻塞IO

2.非阻塞IO

3.信号驱动IO

4.多路复用IO

==================================================================================================

2.非阻塞IO

1.设置非阻塞模式

#include <fcntl.h>

// 获取当前文件描述符标志

int flags = fcntl(sockfd, F_GETFL, 0);

// 添加 O_NONBLOCK 标志

fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

或

// 使用 SOCK_NONBLOCK 标志

int sockfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);

或

// 使用 accept4() 函数(Linux 特有)

int client_fd = accept4(listen_fd, NULL, NULL, SOCK_NONBLOCK);

===================================================================================================

3.信号驱动IO

1.注册信号处理函数

2.设置文件描述符为异步模式

3.设置接收信号的进程ID

1.signal(SIGIO, sigio_handler);

void handler(int signo)

{

char tmpbuff4096 = {0};

memset(tmpbuff, 0, sizeof(tmpbuff));

read(fd, tmpbuff, sizeof(tmpbuff));

printf("FIFO:%s\n", tmpbuff);

return;

}

2.设置文件描述符为异步模式

//在文件描述符原来的标志位基础上新增O_ASYNC,异步IO标志

int flags = fcntl(fd, F_GETFL);

flags |= O_ASYNC;

fcntl(fd, F_SETFL, flags); //fcntl(sockfd, F_SETFL, flags | O_ASYNC);

fd为fifo的管道文件的文件描述符

3.设置接收信号的进程ID

// 告诉内核当 I/O 就绪时,向哪个进程发送 SIGIO

fcntl(sockfd, F_SETOWN, getpid());

例子:

read.c


#include "head.h"

int fd = 0;

void handler(int signo)

{

char tmpbuff4096 = {0};

memset(tmpbuff, 0, sizeof(tmpbuff));

read(fd, tmpbuff, sizeof(tmpbuff));

printf("FIFO:%s\n", tmpbuff);

return;

}

int main(void)

{

int flags = 0;

char tmpbuff4096 = {0};

signal(SIGIO, handler);

mkfifo("/tmp/myfifo", 0664);

fd = open("/tmp/myfifo", O_RDONLY);

if (-1 == fd)

{

perror("fail to open");

return -1;

}

//在文件描述符原来的标志位基础上新增O_ASYNC,异步IO标志

flags = fcntl(fd, F_GETFL);

flags |= O_ASYNC;

fcntl(fd, F_SETFL, flags);

//指定异步IO给当前进程发送信号

fcntl(fd, F_SETOWN, getpid());

while (1)

{

memset(tmpbuff, 0, sizeof(tmpbuff));

gets(tmpbuff);

printf("STDIN:%s\n", tmpbuff);

}

close(fd);

return 0;

}

write.c


#include "head.h"

int main(void)

{

int fd = 0;

char tmpbuff4096 = {0};

mkfifo("/tmp/myfifo", 0664);

fd = open("/tmp/myfifo", O_WRONLY);

if (-1 == fd)

{

perror("fail to open");

return -1;

}

while (1)

{

gets(tmpbuff);

write(fd, tmpbuff, strlen(tmpbuff));

if (0 == strcmp(tmpbuff, ".quit"))

{

break;

}

}

close(fd);

return 0;

}

=================================================================================================

4.多路复用

select/poll/epoll

select:

#include <sys/select.h> *

#include <sys/time.h>

#include <sys/types.h>

#include <unistd.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,

fd_set *exceptfds, struct timeval *timeout);

功能:同时监视多个文件描述符,阻塞直到至少一个描述符就绪(可读/可写/异常),或超时。它会将产生事件的文件描述符留在集合中,将没有发生事件的文件描述符清出集合

参数:

nfds 需要监视的最大文件描述符值 + 1(max_fd + 1)

readfds 监视可读事件的描述符集合(传入传出参数)

writefds 监视可写事件的描述符集合(传入传出参数)一般为NULL

exceptfds 监视异常事件的描述符集合(传入传出参数)一般为NULL

timeout 超时时间(NULL 表示无限等待)(阻塞)

返回值:

> 0 就绪的文件描述符总数

0 超时(没有文件描述符就绪)

-1 失败,设置 errno

void FD_ZERO(fd_set *set) 清空集合

void FD_SET(int fd, fd_set *set) 将 fd 加入集合

void FD_CLR(int fd, fd_set *set) 将 fd 从集合中移除

void FD_ISSET(int fd, fd_set *set) 检查 fd 是否在集合中就绪

==================================================================================================

epoll:

#include <sys/epoll.h>

1.epoll_create

int epoll_create(int size);

功能: 创建一个 epoll 实例,返回一个文件描述符,用于后续的 epoll_ctl() 和 epoll_wait() 操作。一个(内核)监听事件表, 内核,更加高级

参数:size 建议内核初始分配的事件表大小(从 Linux 2.6.8 起被忽略,但必须 > 0)

返回值:

成功 返回 epoll 文件描述符(非负整数)

失败 返回 -1,设置 errno

================================================================================================

epoll_ctl

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

功能:用于向 epoll 实例中添加、修改或删除需要监控的文件描述符。控制 epoll 实例中的事件表:添加、修改或删除要监控的文件描述符及其事件类型。

参数:

epfd epoll 实例的文件描述符(由 epoll_create() 返回)

op 操作类型:EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL

EPOLL_CTL_ADD 添加 将 fd 加入 epoll 实例的监控列表

EPOLL_CTL_MOD 修改 修改 fd 已注册的事件类型

EPOLL_CTL_DEL 删除 从 epoll 实例中移除 fd(不再监控)

fd 要操作的目标文件描述符(如 sockfd、confd)

event 指向 struct epoll_event 的指针,描述关注的事件和用户数据

返回值:

0 成功

-1 失败,设置 errno

struct epoll_event {

uint32_t events; // 事件掩码(关注的事件类型)

epoll_data_t data; // 用户数据(通常存文件描述符)

};

typedef union epoll_data {

void *ptr;

int fd; // 最常用:存文件描述符

uint32_t u32;

uint64_t u64;

} epoll_data_t;

events:

EPOLLIN 可读事件(数据到达)

EPOLLOUT 可写事件(发送缓冲区有空间)

EPOLLERR 发生错误(自动监测,无需设置)

EPOLLHUP 对端关闭连接(自动监测,无需设置)

EPOLLET 边缘触发模式(默认是水平触发)

EPOLLONESHOT 一次性触发(事件发生后自动移除)

==================================================================================================

epoll_wait

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

功能:

等待 epoll 实例中的文件描述符产生事件,将就绪的事件填充到 events 数组中返回。

参数:

epfd epoll 实例的文件描述符(由 epoll_create() 返回)

events 指向 struct epoll_event 数组的指针,用于接收就绪事件

maxevents events 数组的大小(最多返回多少个事件)

timeout 超时时间(毫秒),-1 表示无限等待,0 表示非阻塞轮询

返回值:

> 0 就绪的文件描述符数量(即 events 数组中填充的数量)

0 超时(没有任何事件就绪)

-1 失败,设置 errno

相关推荐
虎头金猫2 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
wuyk5553 天前
《WiFi 嵌入式物联网开发全套实战》| 第 16 章 ESP32 AP+STA 双模共存原理与工程坑点
网络·stm32·物联网
-梅3 天前
linux(8) 软硬链接
linux·运维·服务器
Wang's Blog3 天前
Java 项目实战: 外卖平台-文件下载与ServletOutputStream回写浏览器
服务器·项目开发
琥珀色糖3 天前
SimlpeHttp
linux·服务器
第十人i3 天前
Linux 日志管理系统:journald 配置与优化
linux·服务器
QYRdata3 天前
年均增速24.2%!机器人数据湖未来六年增长动能强劲
网络·机器人·服务发现
CHENKONG_CK3 天前
破解制鞋打磨痛点:RFID赋能去毛刺工序自动化升级
网络·单片机·嵌入式硬件·网络协议·tcp/ip
chshang19923 天前
工业路由器是什么?浅谈5G工业网络中的IR602
网络·物联网·5g·智能路由器
萧瑟余晖3 天前
Netty 核心组件与 Reactor 模型详解
网络·架构