Day 9. TCP并发模型、select、poll、epoll

TCP并发模型

1.TCP多线程模型:

缺点:

1)创建线程会带来资源开销,能够实现

2.IO模型:

1)阻塞IO:没有数据到来时,可以让任务故挂起,节省CPU资源开销,提高系统效率

2)非阻塞IO:程序未接受到数据时程序一直执行,效率很低

3)异步IO:只能绑定一个文件描述符用来读取数据,但是效率很高

4)多路复用IO:

select:

1.select监听的集合中的文件描述符有上限限制 fd_set

2.select有内核层向用户层数据空间拷贝的过程,占用系统资源开销

3.select必须轮询检测产生事件的文件描述符

4.select只能工作在水平触发模式(低速模式),无法工作在边缘触发(高速模式)

poll:

1.poll监听集合中的文件描述符没有上限

2.poll有内核层向用户层数据空间拷贝的过程,占用系统资源开销

3.poll必须轮询检测产生事件的文件描述符

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

epoll:

1.epoll没有文件描述符的上限限制

2.epoll创建的是内核监听事件表,所以只需要在内核空间完成数据拷贝即可

3.eopll会将产生事件的文件描述符直接返回

4. eopll可以工作在水平触发模式(低速模式),还可以工作在边缘触发模式(高速模式)

3.函数接口:

1)select

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

fd_set *exceptfds, struct timeval *timeout);

功能:select建听描述符集合中是否有文件描述编程ready状态

参数:nfds:最大文件买哦舒服的值+1;

readfds:读文件描述符集合

writefds:写文件描述符集合

exceptfds:其余文件描述符集合

timeout:等待的时长

NULL 一直等待

返回值:成功返回文件描述符集合中的文件描述个数;失败返回-1;

void FD_CLR(int fd, fd_set *set);

功能:将文件描述符fd从集合中清除

int FD_ISSET(int fd, fd_set *set);

功能:判断文件描述符fd是否仍在集合中

void FD_SET(int fd, fd_set *set);

功能:将文件描述符fd加入到集合中

void FD_ZERO(fd_set *set);

功能:将文件描述符集合清0

写端

复制代码
#include "head.h"
 
int main(void)
{
    int fd = 0;
    char tmpbuff[4096] = {0};
 
    mkfifo("/tmp/myfifo", 0777);
 
    fd = open("/tmp/myfifo", O_WRONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }
 
    while (1)
    {
        gets(tmpbuff);
        write(fd, tmpbuff, strlen(tmpbuff));
    }
    close(fd);
 
    return 0;
}

读端

复制代码
#include "head.h"
 
int main(void)
{
    int fd = 0;
    int flags = 0;
    char *pret = NULL;
    ssize_t nsize = 0;
    char tmpbuff[4096] = {0};
    fd_set rdfds;
    fd_set tmpfds;
    int ret = 0;
 
    mkfifo("/tmp/myfifo", 0664);
    
    fd = open("/tmp/myfifo", O_RDONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }
 
    FD_ZERO(&rdfds);//将文件描述符集合清0
    FD_SET(fd, &rdfds);//将文件描述符fd加入到文件描述符集合中
    FD_SET(0, &rdfds);//将文件描述符0加入到文件描述符集合中
 
    while (1)
    {
        tmpfds = rdfds;
        ret = select(fd+1, &tmpfds, NULL, NULL, NULL);
        if (-1 == ret)
        {
            perror("fail to select");
            return -1;
        }
 
        if (FD_ISSET(fd, &tmpfds))//判断文件描述符fd是否还在文件描述符集合中
        {
            memset(tmpbuff, 0, sizeof(tmpbuff));
            read(fd, tmpbuff, sizeof(tmpbuff));
            printf("FIFO:%s\n", tmpbuff);
        }
 
        if (FD_ISSET(0, &tmpfds))
        {
            memset(tmpbuff, 0, sizeof(tmpbuff));
            gets(tmpbuff);
            printf("STDIN:%s\n", tmpbuff);
        }
 
    }
 
    close(fd);
 
    return 0;
}

2)poll

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

功能:监听文件描述符集合是否有事件发生

参数:

fds:监听文件描述符集合数组空间的首地址

nfds:监听文件描述符集合元素个数

timeout:等待的时间(-1 一直等待)

返回值:成功返回失败返回产生文件描述符的个数;失败返回-1;

struct pollfd {

int fd; /* file descriptor */

short events; /* requested events */

short revents; /* returned events */

};

fd:监听的文件描述符

events:要监听的事件 POLLIN:是否可读 POLLOUT:是否可写

revents:实际产生的事件

读端

复制代码
#include "head.h"
 
int main(void)
{
	int fd = 0;
	int flags = 0;
	char *pret = NULL;
	ssize_t nsize = 0;
	char tmpbuff[4096] = {0};
	struct pollfd fds[2];
	int nready = 0;
 
	mkfifo("/tmp/myfifo", 0664);
 
	fd = open("/tmp/myfifo", O_RDONLY);
	if (-1 == fd)
	{
		perror("fail to open");
		return -1;
	}
	
	fds[0].fd = fd;
	fds[0].events = POLLIN;
	fds[1].fd = 0;
	fds[1].events = POLLIN;
 
	while (1)
	{
		nready = poll(fds, 2, -1);
		if (-1 == nready)
		{
			perror("fail to poll");
			return -1;
		}
 
		if (fds[0].revents & POLLIN)
		{
			memset(tmpbuff, 0, sizeof(tmpbuff));
			read(fd, tmpbuff, sizeof(tmpbuff));
			printf("FIFO:%s\n", tmpbuff);
		}
	
		if (fds[1].revents & POLLIN)
		{
			memset(tmpbuff, 0, sizeof(tmpbuff));
			gets(tmpbuff);
			printf("STDIN:%s\n", tmpbuff);
		}
	}
 
	close(fd);
}

写端

复制代码
#include "head.h"
 
int main(void)
{
	int fd = 0;
	char tmpbuff[4096] = {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));
	}
 
	close(fd);
 
	return 0;
}

3)epoll

int epoll_create(int size);

功能:创建一张内核事件表

参数:size:事件个数

返回值:成功返回文件描述符;失败返回-1;

epoll_ctl

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

功能:维护epoll事件表

参数:

epfd:事件表的个数

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 */

};

返回值:成功返回0;失败返回-1;

epoll_wait

int epoll_wait(int epfd, struct epoll_event *events,

int maxevents, int timeout);

功能:监听事件表的事件

参数:

epfd:文件描述符

events:存放实际产生事件的数组空间的首地址

maxevents:最多存放事件的个数

timeout:设定监听的时间(超过该时间则不再监听)

-1 一直监听直到有事件发生

返回值:

成功返回产生事件的文件描述符个数

失败返回-1

如果时间达到仍没有事件发生返回0

读端

复制代码
#include "head.h"
 
int main(void)
{
    int fd = 0;
    int epfd = 0;
    struct epoll_event env;//epoll_ctl需要的事件的结构体
    int nready = 0;
    struct epoll_event retenv[2];
    int i = 0;
    ssize_t nsize = 0;
    char *pret = NULL;
    char tmpbuff[4096] = {0};
 
 
    mkfifo("/tmp/myfifo", 0664);
    fd = open("/tmp/myfifo", O_RDONLY);
    if (-1 == fd)
    {
        perror("fail to open");
        return -1;
    }
 
    /*创建一张2个事件的内核事件表*/
    epfd = epoll_create(2);
    if (epfd == -1)
    {
        perror("fail to create");
        return -1;
    }
 
    /*设置事件结构体的属性*/
    env.events = EPOLLIN;
    env.data.fd = fd;
    /*操作事件*/
    epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &env);
 
    env.events = EPOLLIN;
    env.data.fd = 0;
    epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &env);
 
    while (1)
    {
        /*监听事件表中的事件*/
        nready = epoll_wait(epfd, retenv, 2, -1);//第二个参数是存放实际产生事件的结构体, 最多存放的个数, 设置监听时间,-1一直监听直到有事件发生
        if (-1 == nready)
        {
            perror("fail to nready");
            return -1;
        }
 
        for (i = 0; i < nready; ++i)
        {
            if (retenv[i].data.fd == 0)//判断要操作的流是否为从终端输入
            {
                memset(tmpbuff, 0, sizeof(tmpbuff));
                gets(tmpbuff);
                printf("STDIN: %s\n", tmpbuff);
            }
 
            if (retenv[i].data.fd == fd)
            {
                memset(tmpbuff, 0, sizeof(tmpbuff));
                read(fd, tmpbuff, sizeof(tmpbuff));
                printf("FIFO: %s\n", tmpbuff);
            }
        }
    }
 
    close(fd);
 
    return 0;
}

写端

复制代码
#include "head.h"
 
int main(void)
{
    int fd = 0;
    char tmpbuff[4096] = {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));
    }
    close(fd);
 
    return 0;
}
相关推荐
無限進步D1 小时前
Java 运行原理
java·开发语言·入门
是苏浙1 小时前
JDK17新增特性
java·开发语言
SPC的存折3 小时前
1、Redis数据库基础
linux·运维·服务器·数据库·redis·缓存
爱学习的小囧3 小时前
VMware ESXi 6.7U3v 新版特性、驱动集成教程和资源包、部署教程及高频问答详情
运维·服务器·虚拟化·esxi6.7·esxi蟹卡驱动
小疙瘩4 小时前
只是记录自己发布若依分离系统到linux过程中遇到的问题
linux·运维·服务器
dldw7774 小时前
IE无法正常登录windows2000server的FTP服务器
运维·服务器·网络
阿里加多4 小时前
第 4 章:Go 线程模型——GMP 深度解析
java·开发语言·后端·golang
likerhood5 小时前
java中`==`和`.equals()`区别
java·开发语言·python
运维有小邓@5 小时前
什么是重放攻击?如何避免成为受害者?
运维·网络·安全
光路科技5 小时前
工业数字化三大核心概念拆解:IIoT、工业互联网与工业4.0
网络