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

相关推荐
程序员夏洛1 小时前
HTTP 请求包含哪些内容,请求头和请求体有哪些类型?
网络·网络协议·http
啊阿狸不会拉杆1 小时前
《计算机网络-自顶向下方法》3.9 小结 · 课后习题 · Wireshark实验
网络·测试工具·wireshark
Chengbei111 小时前
SRC报告成稿 Skill(SRC/0day 提交稿、分层验证门、Step 式 PoC、截图铁律)
网络·人工智能·安全·web安全·自动化·系统安全
小雪崩1 小时前
嵌入式学习 day39:TCP并发服务器模型
linux·服务器·c语言·网络·学习·tcp/ip
Doraemomo1 小时前
网络编程-复习
网络
智购无人售货机厂家1 小时前
2026自动售货机软硬件版本管理策略:从版本号规范到兼容性矩阵的工程实践~YH
运维·服务器·人工智能·单片机·嵌入式硬件·线性代数·矩阵
2601_966377131 小时前
云上系统零改造过密评|数达安全发布通关四件套
网络·安全
bksczm2 小时前
Linux 五种 I/O 模型与多路复用详解
运维·服务器·网络
蒸蒸yyyyzwd3 小时前
cpp选手秋招学习笔记day24-cs144项目总结
运维·服务器·网络