1.阻塞IO
CPU占用率低,等待资源时将任务挂起,不占用CPU资源,等到拿到资源后继续向下执行
向管道中写入数据
write.c
#include "../head.h"
int main()
{
int fd;
mkfifo("/tmp/myfifo",0777);
char tmpbuff[100]={0};
fd=open("/tmp/myfifo",O_WRONLY);
if(fd==-1)
{
return -1;
}
while(1)
{
memset(tmpbuff,0,sizeof(tmpbuff));
fgets(tmpbuff,sizeof(tmpbuff),stdin);
tmpbuff[strlen(tmpbuff)-1]='\0';
write(fd,tmpbuff,strlen(tmpbuff));
}
close(fd);
return 0;
}
read.c: 先从终端写入数据,再从管道读入数据,管道必须等终端写入才可以读,第二次循环,终端也必须等第一次循环管道读入数据后才能继续从终端写,互相阻塞
#include "head.h"
int main(void)
{
int fd = 0;
char tmpbuff[4096] = {0};
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_RDONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
while (1)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
read(fd, tmpbuff, sizeof(tmpbuff));
printf("FIFO:%s\n", tmpbuff);
memset(tmpbuff, 0, sizeof(tmpbuff));
gets(tmpbuff);
printf("STDIN:%s\n", tmpbuff);
}
close(fd);
return 0;
}
2.非阻塞IO
能够让任务不阻塞,效率低,因为没有数据时,CPU一直空转
write.c和第一个相同
read.c:将管道和stdin流都设置为非阻塞属性
#include "../head.h"
int main()
{
int fd;
ssize_t nsize=0;
int flags=0;
char tmpbuff[200]={0};
char *pret=NULL;
mkfifo("/tmp/myfifo",0777);
fd=open("/tmp/myfifo",O_RDONLY);
if(fd==-1)
{
return -1;
}
/*获得fd文件描述符的属性*/
flags=fcntl(fd,F_GETFL);
/*在现有属性中加入非阻塞属性*/
flags|=O_NONBLOCK;
/*将新属性设置回文件描述符中*/
fcntl(fd,F_SETFL,flags);
/*设置stdin的属性为非阻塞属性*/
flags=fcntl(0,F_GETFL);
flags|=O_NONBLOCK;
fcntl(0,F_SETFL,flags);
while(1)
{
/*从管道中读数据*/
memset(tmpbuff,0,sizeof(tmpbuff));
nsize=read(fd,tmpbuff,sizeof(tmpbuff));
if(nsize>0)
{
printf("FIFO:%s\n",tmpbuff);
}
/*从终端读数据*/
memset(tmpbuff,0,sizeof(tmpbuff));
pret=gets(tmpbuff);
if(pret!=NULL)
{
printf("STDIN:%s\n",tmpbuff);
}
}
close(fd);
return 0;
}
3.异步IO
将一个文件描述符设定为异步IO,当IO有事件发生时,内核会向用户层发送SIGIO信号提醒用户层处理事件
write.c和第一个相同
read.c:将管道设定为异步IO,当管道中有数据写入时发送信号,执行处理函数
#include "../head.h"
int fd;
void handler(int signo)
{
char tmpbuff[256]={0};
memset(tmpbuff,0,sizeof(tmpbuff));
read(fd,tmpbuff,sizeof(tmpbuff));
printf("FIFO:%s\n",tmpbuff);
}
int main()
{
ssize_t nsize=0;
int flags=0;
char tmpbuff[200]={0};
char *pret=NULL;
/*当文件描述符有信号发生时(管道写入数据),发送信号通知,执行处理函数*/
signal(SIGIO,handler);
mkfifo("/tmp/myfifo",0777);
fd=open("/tmp/myfifo",O_RDONLY);
if(fd==-1)
{
return -1;
}
/*获得fd文件描述符的属性*/
flags=fcntl(fd,F_GETFL);
/*将fd设置为异步IO(文件描述符发生可以读的事件,会发送信号通知)*/
flags|=O_ASYNC;
/*将新属性设置回文件描述符中*/
fcntl(fd,F_SETFL,flags);
//通知给当前进程
fcntl(fd,F_SETOWN,getpid());
while(1)
{
memset(tmpbuff,0,sizeof(tmpbuff));
pret=gets(tmpbuff);
if(pret!=NULL)
{
printf("STDIN:%s\n",tmpbuff);
}
}
close(fd);
return 0;
}
4.多路复用IO
1.select
监听文件描述符集合,将所有要监听的事件加入集合中,使用select监听所有事件,当集合中有事件发生, select不再阻塞,同时select会将产生事件的文件描述符留在集合中,而把没有产生事件的文件描述符从集合中踢出,所以留在集合中的文件描述即为产生事件的文件描述符,对其处理即可
函数接口:
1.int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
功能:
监听文件描述符是否有事件发生
参数:
nfds:最大文件描述符的值 + 1
readfds:读文件描述符集合
writefds:写文件描述符集合
exceptfds:异常文件描述符集合
timeout:超时时间
返回值:
成功返回产生事件的文件描述符个数
失败返回-1
timeout时间到达仍然没有产生的事件返回0
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
管道和终端写入数据
write:与第一个相同
read.c:当管道或者终端某一个有时间发生时,留下发生事件的那个文件描述符,其余删除,检测哪个文件描述符还在文件描述符集合中,就处理哪个
#include "../head.h"
int fd;
int main()
{
ssize_t nsize=0;
int flags=0;
char tmpbuff[200]={0};
char *pret=NULL;
fd_set rdfds;
fd_set tmpfds;
int maxfd;
int nready=0;
mkfifo("/tmp/myfifo",0777);
fd=open("/tmp/myfifo",O_RDONLY);
if(fd==-1)
{
return -1;
}
/* 将文件描述符集合清0 */
FD_ZERO(&rdfds);
FD_SET(fd,&rdfds);
/*将stdin加入文件描述符集合中*/
FD_SET(0,&rdfds);
maxfd=fd;
while(1)
{
tmpfds=rdfds;
//监听发生事件的文件描述符,将其余文件描述符从文件描述符集合中清除
nready=select(maxfd+1,&tmpfds,NULL,NULL,NULL);
if(-1==nready)
{
perror("failed to select");
return -1;
}
/*判断fd是否仍在文件描述符集合中*/
if(FD_ISSET(fd,&tmpfds))
{
memset(tmpbuff,0,sizeof(tmpbuff));
read(fd,tmpbuff,sizeof(tmpbuff));
printf("FIFO:%s\n",tmpbuff);
}
/*判断stdin是否仍在文件描述符集合中*/
if(FD_ISSET(0,&tmpfds))
{
memset(tmpbuff,0,sizeof(tmpbuff));
pret=gets(tmpbuff);
if(pret!=NULL)
{
printf("STDIN:%s\n",tmpbuff);
}
}
}
close(fd);
return 0;
}
TCP多路复用应用
client向server发送数据,再接收server的回复
client.c
#include "../head.h"
int CreateTcpConnection()
{
int sockfd;
int ret=0;
struct sockaddr_in sendaddr;
//创建套接字
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
return -1;
}
sendaddr.sin_family=AF_INET;
sendaddr.sin_port=htons(50000);
sendaddr.sin_addr.s_addr=inet_addr("192.168.0.187");
//发送连接
ret=connect(sockfd,(struct sockaddr *)&sendaddr,sizeof(sendaddr));
if(ret==-1)
{
return -1;
}
return sockfd;
}
int HandleConnection(int sockfd)
{
char tmpbuff[1024]={0};
static int cnt=0;
ssize_t nsize=0;
//发送数据
sprintf(tmpbuff,"hello world --%d",cnt);
nsize=send(sockfd,tmpbuff,strlen(tmpbuff),0);
if(nsize==-1)
{
return -1;
}
cnt++;
//接收数据
memset(tmpbuff,0,sizeof(tmpbuff));
nsize=recv(sockfd,tmpbuff,sizeof(tmpbuff),0);
if(nsize==-1)
{
return -1;
}
else if(nsize==0)
{
return 0;
}
printf("RECV:%s\n",tmpbuff);
return nsize;
}
int main()
{
int sockfd;
int ret=0;
sockfd=CreateTcpConnection();
if(sockfd==-1)
{
printf("连接服务器出错\n");
return -1;
}
while(1)
{
ret=HandleConnection(sockfd);
if(ret==-1)
{
printf("连接出错\n");
break;
}
else if(ret==0)
{
printf("关闭连接\n");
break;
}
sleep(1);
}
//关闭套接字
close(sockfd);
return 0;
}
server.c:监听是否有新的连接请求,如果sockfd产生事件,处理新的请求,并将新的文件描述符加入集合,下一次一起监听,遍历所有已连接客户端,要是某个客户端有事件发生,就执行它的处理函数:接收来自客户端的数据,并回复客户端新拼接的数据
#include "../head.h"
int CreateTcpConnection()
{
int sockfd;
int ret=0;
struct sockaddr_in recvaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
return -1;
}
recvaddr.sin_family=AF_INET;
recvaddr.sin_port=htons(50000);
recvaddr.sin_addr.s_addr=inet_addr("192.168.0.187");
ret=bind(sockfd,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(ret==-1)
{
return -1;
}
ret=listen(sockfd,20);
if(ret==-1)
{
return -1;
}
return sockfd;
}
int HandleConnection(int confd)
{
char tmpbuff[1024]={0};
ssize_t nsize=0;
//接收数据并打印
nsize=recv(confd,tmpbuff,sizeof(tmpbuff),0);
if(nsize==-1)
{
return -1;
}
else if(nsize==0)
{
return 0;
}
printf("RECV:%s\n",tmpbuff);
//返回数据
sprintf(tmpbuff,"%s--echo",tmpbuff);
nsize=send(confd,tmpbuff,strlen(tmpbuff),0);
if(nsize==-1)
{
return -1;
}
return nsize;
}
int main()
{
int sockfd;
int i=0;
int maxfd=0;
fd_set rdfds;
fd_set tmpfds;
int nready=0;
int ret=0;
int confd;
//创建套接字
sockfd=CreateTcpConnection();
if (-1 == sockfd)
{
printf("创建监听套接字失败\n");
return -1;
}
//将sockfd加入监听集合中
FD_ZERO(&rdfds);
FD_SET(sockfd,&rdfds);
maxfd=sockfd;
while(1)
{
//开始监听
tmpfds=rdfds;
nready=select(maxfd+1,&tmpfds,NULL,NULL,NULL);
if(nready==-1)
{
perror("failed to select");
return -1;
}
// 如果sockfd产生事件,处理新的请求,并将新的文件描述符加入集合,下一次一起监听
if(FD_ISSET(sockfd,&tmpfds))
{
confd=accept(sockfd,NULL,NULL);
if(confd==-1)
{
FD_CLR(sockfd,&rdfds);
close(sockfd);
continue;
}
maxfd=maxfd>confd?maxfd:confd;
FD_SET(confd,&rdfds);
}
//遍历所有已经连接的客户端中是否有事件发生
for(i=sockfd+1;i<=maxfd;i++)
{
if(FD_ISSET(i,&tmpfds))
{
ret=HandleConnection(i);
if(ret==-1)
{
printf("连接异常\n");
FD_CLR(i,&rdfds);
close(i);
continue;
}
else if(ret==0)
{
printf("关闭连接\n");
FD_CLR(i,&rdfds);
close(i);
continue;
}
}
}
}
close(sockfd);
return 0;
}
运行结果:一个服务器连接两个客户端(可以连接更多的客户端)
select的缺点
1.select监听的文件描述符集合是一个数组,有上限(1024个)
2.select监听的文件描述符集合在应用层,内核层监听事件后需要传递给用户层带来资源开销
3.select需要用户手动查找产生事件的文件描述符
4.select只能工作在水平触发模式(低速模式)而无法工作在边沿触发模式(高速模式)
水平触发模式:
事件就绪时,假设对事件没做处理,内核会反复通知事件就绪
边沿触发模式:
事件就绪时,假设对事件没做处理,内核不会反复通知事件就绪
2.poll
poll (监听的文件描述符没有上限)
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
功能:
监听文件描述符集合,工作方式类似于select
参数:
fds:文件描述符集合首地址
nfds:文件描述符集合的数组的长度
timeout:超时时间,单位毫秒,-1表示永久等待
返回值:
成功返回产生事件文件描述符个数
失败返回-1
超时仍然没有产生的事件返回0
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};将可能发生事件的文件描述符加入数组中,当某个文件描述符的事件和revents参数按位与为真,表示有事件发生
管道和终端写入数据
write.c与第一个相同
read.c
#include "../head.h"
int main(void)
{
int fd = 0;
char tmpbuff[4096] = {0};
int nready=0;
struct pollfd fds[2]={0};
mkfifo("/tmp/myfifo", 0777);
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);//传入数组的大小没有限制(select有限制(最大为1024)),timeout参数传-1,表示阻塞监听(一直等待有无事件发生)
if(nready==-1)
{
perror("failed to poll");
return -1;
}
if(fds[0].revents&POLLIN) //revents是操作系统自己监听,与要发生的事件按位与,发生事件则if有效
{
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);
return 0;
}
利用poll实现TCP多并发服务器模型
client.c与上面相同
server.c
#include "../head.h"
int CreateTcpConnection()
{
int sockfd;
int ret=0;
struct sockaddr_in recvaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
return -1;
}
recvaddr.sin_family=AF_INET;
recvaddr.sin_port=htons(50000);
recvaddr.sin_addr.s_addr=inet_addr("192.168.0.187");
ret=bind(sockfd,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(ret==-1)
{
return -1;
}
ret=listen(sockfd,20);
if(ret==-1)
{
return -1;
}
return sockfd;
}
int HandleConnection(int confd)
{
char tmpbuff[1024]={0};
ssize_t nsize=0;
//接收数据并打印
nsize=recv(confd,tmpbuff,sizeof(tmpbuff),0);
if(nsize==-1)
{
return -1;
}
else if(nsize==0)
{
return 0;
}
printf("RECV:%s\n",tmpbuff);
//返回数据
sprintf(tmpbuff,"%s--echo",tmpbuff);
nsize=send(confd,tmpbuff,strlen(tmpbuff),0);
if(nsize==-1)
{
return -1;
}
return nsize;
}
//初始化数组
int InitFds(struct pollfd *fds,int maxnum)
{
int i=0;
for(i=0;i<maxnum;i++)
{
fds[i].fd=-1;
}
return 0;
}
//将可能发生事件的文件描述符加入
int AddFds(struct pollfd *fds,int maxnum,int newfd,short event)
{
int i=0;
for(i=0;i<maxnum;i++)
{
if(fds[i].fd==-1)
{
fds[i].fd=newfd;
fds[i].events=event;
break;
}
}
return 0;
}
//删除文件描述符
int Delfds(struct pollfd *fds,int maxlen,int tmpfd)
{
int i=0;
for(i=0;i<maxlen;i++)
{
if(fds[i].fd==tmpfd)
{
fds[i].fd=-1;
break;
}
}
return 0;
}
int main()
{
int sockfd;
int i=0;
int maxfd=0;
int nready=0;
int ret=0;
int confd;
struct pollfd fds[1000];
//创建套接字
sockfd=CreateTcpConnection();
if (-1 == sockfd)
{
printf("创建监听套接字失败\n");
return -1;
}
InitFds(fds,1000);
AddFds(fds,1000,sockfd,POLLIN);
while(1)
{
nready=poll(fds,1000,-1);//监听
if(nready==-1)
{
perror("failed to poll");
return -1;
}
for(i=0;i<1000;i++)
{
if (-1 == fds[i].fd)
{
continue;
}
if((fds[i].fd==sockfd)&&(fds[i].revents&fds[i].events))//监听sockfd
{
confd=accept(sockfd,NULL,NULL);
if(confd==1)
{
perror("failed to accept");
Delfds(fds,1000,sockfd);
close(sockfd);
continue;
}
AddFds(fds,1000,confd,POLLIN);
}
else if((fds[i].fd!=sockfd)&&(fds[i].revents&fds[i].events))//监听客户端
{
ret=HandleConnection(fds[i].fd);
if(ret==-1)
{
printf("接收异常\n");
close(fds[i].fd);
Delfds(fds,1000,fds[i].fd);
continue;
}
else if(0==ret)
{
printf("连接关闭\n");
close(fds[i].fd);
Delfds(fds,1000,fds[i].fd);
continue;
}
}
}
}
close(sockfd);
return 0;
}
poll的缺点
1.poll监听的文件描述符集合在应用层,内核层监听事件后需要传递给用户层带来资源开销
2.poll需要用户手动查找产生事件的文件描述符
3.poll只能工作在水平触发模式(低速模式)而无法工作在边沿触发模式(高速模式)
3.epoll
1.epoll_create
int epoll_create(int size);
功能:
在内核层创建一张epoll监听的事件表
参数:
size:监听的事件表大小
返回值:
成功返回新的文件描述符
失败返回-1
2.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:事件结构体
返回值:
成功返回0
失败返回-1参数3的结构体
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 */
};events:
EPOLLIN :有数据来临
EPOLLOUT :有数据要写
EPOLLET :边沿触发
3. epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
功能:
监听epfd对应的事件表中是否有事件发生
参数:
epfd:文件描述符
events:存放产生事件的数组空间首地址
maxevents:最多存放数组元素个数
timeout:超时时间,单位毫秒,-1表示永久等待
返回值:
成功返回实际发生事件的个数
失败返回-1
超时仍然没有产生事件返回0
管道和终端写入数据
write.c与第一个相同
read.c
#include "../head.h"
int main()
{
int epfd=0;
int fd=0;
int ret=0;
int nready=0;
char tmpbuff[2500]={0};
struct epoll_event env;
struct epoll_event retenv[2];
mkfifo("/tmp/myfifo", 0777);
fd = open("/tmp/myfifo", O_RDONLY);
if (-1 == fd)
{
perror("fail to open");
return -1;
}
//创建一张内核监听的事件表
epfd=epoll_create(2);
if(epfd==-1)
{
perror("failed to epoll_create");
return -1;
}
//将fd文件描述符加入事件表中
env.events=EPOLLIN;
env.data.fd=fd;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&env);
if(-1==ret)
{
perror("failed to epoll_ctl");
return -1;
}
//将stdin加入事件表中
env.events=EPOLLIN;
env.data.fd=0;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,0,&env);
if(-1==ret)
{
perror("failed to epoll_ctl");
return -1;
}
while(1)
{
nready=epoll_wait(epfd,retenv,2,-1); //监听epfd对应的事件表中是否有事件发生 ,将产生事件的数组放到retenv中
if(-1==nready)
{
perror("failed to epoll_wait");
return -1;
}
for(int i=0;i<nready;i++)
{
if(retenv[i].data.fd==fd)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
read(fd, tmpbuff, sizeof(tmpbuff));
printf("FIFO:%s\n", tmpbuff);
}
else if(retenv[i].data.fd==0)
{
memset(tmpbuff, 0, sizeof(tmpbuff));
gets(tmpbuff);
printf("STDIN:%s\n", tmpbuff);
}
}
}
close(epfd);
return 0;
}
利用poll实现TCP多并发服务器模型
client.c与上面相同
server.c
//监听文件描述符集合,将发生事件的文件描述符对应的数据加入实际发生事件集合retenv
//如果是sockfd发生事件,则处理连接,并将新的文件描述符加入retenv中
//负责客户端连接的文件描述符发生事件,则执行响应客户端函数
#include "../head.h"
int CreateTcpConnection()
{
int sockfd;
int ret=0;
struct sockaddr_in recvaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
return -1;
}
recvaddr.sin_family=AF_INET;
recvaddr.sin_port=htons(50000);
recvaddr.sin_addr.s_addr=inet_addr("192.168.0.187");
ret=bind(sockfd,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(ret==-1)
{
return -1;
}
ret=listen(sockfd,20);
if(ret==-1)
{
return -1;
}
return sockfd;
}
int HandleConnection(int confd)
{
char tmpbuff[1024]={0};
ssize_t nsize=0;
//接收数据并打印
nsize=recv(confd,tmpbuff,sizeof(tmpbuff),0);
if(nsize==-1)
{
return -1;
}
else if(nsize==0)
{
return 0;
}
printf("RECV:%s\n",tmpbuff);
//返回数据
sprintf(tmpbuff,"%s--echo",tmpbuff);
nsize=send(confd,tmpbuff,strlen(tmpbuff),0);
if(nsize==-1)
{
return -1;
}
return nsize;
}
int AddFds(int epfd,int fd,uint32_t tmpevent)
{
struct epoll_event env;
env.events=tmpevent;
env.data.fd=fd;
int ret=0;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&env);
if(ret==-1)
{
perror("failed to epoll_ctl");
return -1;
}
return 0;
}
int Delfds(int epfd,int fd)
{
int ret=0;
ret=epoll_ctl(epfd,EPOLL_CTL_ADD,fd,NULL);
if(ret==-1)
{
perror("failed to epoll_ctl");
return -1;
}
return 0;
}
int main()
{
int sockfd;
int i=0;
int epfd=0;
struct epoll_event env;
struct epoll_event retenv[100];
int nready=0;
int ret=0;
int confd;
struct pollfd fds[1000];
//创建套接字
sockfd=CreateTcpConnection();
if (-1 == sockfd)
{
printf("创建监听套接字失败\n");
return -1;
}
epfd=epoll_create(100);
if(epfd==-1)
{
perror("failed to epoll_create");
return -1;
}
AddFds(epfd,sockfd,EPOLLIN);//添加sockfd到文件描述符集合中
while(1)
{
//监听文件描述符集合,将发生事件的文件描述符对应的数据加入实际发生事件集合retenv中
nready=epoll_wait(epfd,retenv,100,-1);
if(nready==-1)
{
perror("failed to poll");
return -1;
}
for(i=0;i<nready;i++)
{
//如果是sockfd发生事件,则处理连接,并将新的文件描述符加入retenv中
if(retenv[i].data.fd==sockfd)
{
confd=accept(sockfd,NULL,NULL);
if(confd==-1)
{
perror("failed to accept");
Delfds(epfd,sockfd);
close(sockfd);
continue;
}
AddFds(epfd,confd,EPOLLIN);
}
else if(retenv[i].data.fd!=sockfd)//负责客户端连接的文件描述符发生事件,则执行响应客户端函数
{
ret=HandleConnection(retenv[i].data.fd);
if(ret==-1)
{
printf("接收异常\n");
close(retenv[i].data.fd);
Delfds(epfd,retenv[i].data.fd);
continue;
}
else if(0==ret)
{
printf("连接关闭\n");
close(retenv[i].data.fd);
Delfds(epfd,retenv[i].data.fd);
continue;
}
}
}
}
close(epfd);
return 0;
}
epoll的优点:
1.epoll没有文件描述符上限限制
2.epoll监听的事件表在内核层,内核监听事件不需要操作用户层空间提高效率
3.epoll会获得产生事件的文件描述符,不需要用户查找
4.epoll可以工作在边沿触发模式(高速模式),提高效率
进程实现TCP并发服务器模型
#include "head.h"
int CreateListenSocket(const char *pip, int port)
{
int sockfd = 0;
int ret = 0;
struct sockaddr_in seraddr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (-1 == sockfd)
{
return -1;
}
seraddr.sin_family = AF_INET;
seraddr.sin_port = htons(50000);
seraddr.sin_addr.s_addr = inet_addr("192.168.0.187");
ret = bind(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
if (-1 == ret)
{
return -1;
}
ret = listen(sockfd, 10);
if (-1 == ret)
{
return -1;
}
return sockfd;
}
int HandleConnection(int confd)
{
char tmpbuff[4096] = {0};
ssize_t nsize = 0;
nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
if (-1 == nsize)
{
return -1;
}
else if (0 == nsize)
{
return 0;
}
printf("RECV:%s\n", tmpbuff);
sprintf(tmpbuff, "%s --- echo", tmpbuff);
nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
if (-1 == nsize)
{
return -1;
}
return nsize;
}
void handler(int signo)
{
wait(NULL);
return;
}
int main(void)
{
int sockfd = 0;
int confd = 0;
int ret = 0;
pid_t pid;
signal(SIGCHLD, handler);
sockfd = CreateListenSocket(SER_IP, SER_PORT);
if (-1 == sockfd)
{
printf("创建监听套接字失败\n");
return -1;
}
while (1)
{
confd = accept(sockfd, NULL, NULL);
if (-1 == confd)
{
printf("处理连接失败\n");
return -1;
}
pid = fork();
if (-1 == pid)
{
perror("fail to fork");
return -1;
}
if (0 == pid)
{
while (1)
{
ret = HandleConnection(confd);
if (-1 == ret)
{
printf("接收异常!\n");
break;
}
else if (0 == ret)
{
printf("关闭连接!\n");
break;
}
}
close(confd);
close(sockfd);
exit(0);
}
}
close(sockfd);
return 0;
}
线程实现并发服务器模型
#include "../head.h"
int CreateTcpConnection()
{
int sockfd;
int ret=0;
struct sockaddr_in recvaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
{
return -1;
}
recvaddr.sin_family=AF_INET;
recvaddr.sin_port=htons(50000);
recvaddr.sin_addr.s_addr=inet_addr("192.168.0.187");
ret=bind(sockfd,(struct sockaddr *)&recvaddr,sizeof(recvaddr));
if(ret==-1)
{
return -1;
}
ret=listen(sockfd,20);
if(ret==-1)
{
return -1;
}
return sockfd;
}
int HandleConnection(int confd)
{
char tmpbuff[1024]={0};
ssize_t nsize=0;
//接收数据并打印
nsize=recv(confd,tmpbuff,sizeof(tmpbuff),0);
if(nsize==-1)
{
return -1;
}
else if(nsize==0)
{
return 0;
}
printf("RECV:%s\n",tmpbuff);
//返回数据
sprintf(tmpbuff,"%s--echo",tmpbuff);
nsize=send(confd,tmpbuff,strlen(tmpbuff),0);
if(nsize==-1)
{
return -1;
}
return nsize;
}
void *threadfun(void *arg)
{
int confd=(int)arg;
int ret=0;
while(1)
{
ret=HandleConnection(confd);
if(ret==-1)
{
printf("连接出错\n");
break;
}
else if(ret==0)
{
printf("连接关闭\n");
break;
}
}
}
int main()
{
int sockfd;
int i=0;
int maxfd=0;
fd_set rdfds;
fd_set tmpfds;
int nready=0;
int ret=0;
int cnt=0;
pthread_t tid;
pthread_attr_t attr;//设置线程属性
pthread_attr_init(&attr);//初始化线程属性
//将线程属性设置为分离属性,线程结束自动被系统回收
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
//创建套接字
sockfd=CreateTcpConnection();
if (-1 == sockfd)
{
printf("创建监听套接字失败\n");
return -1;
}
while(1)
{
int confd;
confd=accept(sockfd,NULL,NULL);
if(confd==-1)
{
printf("连接失败");
break;
}
else
{
ret=pthread_create(&tid,&attr,threadfun,(void *)confd);
if(ret==-1)
{
perror("failed to pthread_create");
break;
}
}
cnt++;
}
close(sockfd);
return 0;
}