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:
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);//将文件描述符集合清0FD_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;
}