TCP并发服务器模型

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;
}

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值