目录
[阻塞IO:scanf(); getchar(); fgets();](#阻塞IO:scanf(); getchar(); fgets();)
[非阻塞IO :](#非阻塞IO :)
[int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);](#int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);)
[① int epoll_create(int size);](#① int epoll_create(int size);)
显卡
声卡
网卡:能让你上网的
URL的一般形式
<协议>://<主机 ip> : <端口>/
strstr --> char *strcasestr(const char *haystack, const char *needle);
功能:从中找字符串
参数:
返回值:返回找到的子字符串的起始位置
客户端:并发服务器------支持多个用户同时访问
循环服务器(迭代服务器)
while(1)
{
//连接
accept(); //问题1:阻塞操作------在等待队列中没有连接请求时会阻塞
while(1) //问题2:通信过程可能是一个耗时操作
{
//通信recv();send();}}
实现简单。可以处理多个客户端,但是只能是串行
并发服务器
while(1)
{connfd = accept();
fork() 或 pthread_create()
}
多进程:资源开销大,进程的退出要考虑僵尸态资源的回收
多线程:线程的创建和调度开销小,线程共享资源方便,线程之间竞争和同步问题
服务器模型:IO多路复用
核心目的 ------ 提高并发的程度
多路IO ------ 当前进程有多处输入输出的操作
复用 ------ 服复用的是当前进程或线程
应用:在一个进程中有多个线程要执行
系统开销小,也不用创建线程和进程,不用维护,减少系统的开销
常见的IO操作模型
阻塞IO:scanf(); getchar(); fgets();
- 常见的主要是读取、写入、管道(64kb大小空间)
- 当读取数据时,内核没有数据,读取操作会一直阻塞,直到内核获得数据
- 特点:实现简单,效率不高
非阻塞IO :
特点:当去内核读取数据时,如果内核没有数据,他不阻塞等待,而是立即返回,所以非阻塞方式想要获得数据,需要配合轮询操作
while(1)
{
recvfrom(); //非阻塞一直要轮询到等读到数据
}//很耗CPU
信号驱动IO
- 1 设置好对SIGIO的信号处理函数,等有数据来了,内核会给进程发信号,进程收到信号后,再去做读取操作,不需要一直等也不需要轮询
- 使用流程:
- 为内核发送的通知信号安装一个信号处理例程,默认情况下,这个通知信号为SIGIO
- //signal 或 sigaction
- signal(SIGIO,handler);
- 2 设定文件描述符的属主,也就是当文件描述符上可执行IO时会接收到通知信的进程或进程组,通常我们让调用进程成为属主,设定属主可通过fcntl();的F_SETOWN操作来完成
- //owner
- fcntl();
- 异步的效率高,但是只能处理一路IO
- 3 设置标志:通过设定O_NONBLOCK标志使能非阻塞IO;通过打开O_ASYNC标志使能信号驱动IO;这可以一个操作,因为它们都需要用到fcntl()的F_SETFL操作

IO多路复用
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set);

select使用流程
- 准备监控文件描述符表:fd_set readfds;FD_ZERO(&readfds);
- 添加要监控的文件描述符:FD_SET(fd,&readfds);FD_SET(0,&readfds);
- 准备nfsd:nfds = n+1;
- 调用select函数:select(nfsd,&readfds,NULL,NULL,NULL);
cpp
select 实现并发:
1.listenfd=socket
2.bind
3.listen
//1.准备表
fd_set readfds;
//清空
FD_ZERO(&readfds);
//2.添加要监控的文件描述符到表中
//listenfd
FD_SET(listenfd,readfds);
//3.调用select函数进行监控
//1.nfds --> 最大的文件描述符+1
int nfds = listenfd + 1; //注意: 是三个集合中最大的文件描述符加1
int ret = 0;
int i = 0;
fd_set bakfds = readfds;
while (1)
{
bakfds = readfds;
ret = select(nfds,&bakfds,NULL,NULL,NULL);
if (ret > 0)
{ //表示有就绪的文件描述符
//寻找具体是哪个文件描述符就绪
for (i = 0; i < nfds; i++)
{
if (FD_ISSET(i,&bakfds))
{ //只能判断出 i 是否在bakfds中
if (i == listenfd) //意味着有客户端发起连接请求
{
connfd = accpet();
//实现并发,添加对新连接的套接字的监控
//1. 添加到要监控的表中,进行监控
FD_SET(connfd,&readfds);
//2.更新maxfd
if (connfd + 1> nfds)
nfds = connfd+1;
}
else //创建 --- 子进程 或 线程
{ //此时肯定是某个客户端对应的connfd的值
read(i,buf,sizeof(buf)); //从i中读值,
//因为i表示的是当前就绪的文件描述符
printf("buf == %s\n",buf);
}
}
}
}
}

epoll
1.创建监控的epoll对象
epoll_create
① int epoll_create(int size);
功能:创建epoll对象,返回文件描述符
参数:@size 并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。
返回值:是Epoll描述符
epoll_ctl


- 所谓边沿触发,指的是对应fd从无数据到有数据这一瞬间的变化,而水平触发是指对应的fd中只要有数据就认为是就绪状态,直到数据处理完
- epoll如果监听ET事件,fd必须是非阻塞套接口(高性能场合)比如:监听可读事件,当ET上报可读后,需要一直读Fd直到遇到EAGATN错误为止,以免遗留数据在缓冲区中,如果FD是阻塞的,则会读到阻塞了(不建议做太多的耗时操作)
- 现在很多的高性能并发服务器:epoll+et+线程池
epoll_wait

