TCP并发服务器

avatar
作者
筋斗云
阅读量:0

1.阻塞IO

        CPU占用率低,等待资源时将任务挂起,不占用CPU资源,等到拿到资源后继续向下执行
2.非阻塞IO 
       能够让任务不阻塞,效率低,因为没有数据时,CPU一直空转

fcntl(fd,F_GETFL);获得文件描述符的属性

flags |= O_NONBLOCK;在现有属性中加入非阻塞属性 

fcntl(fd,F_SETFL,flags);将新属性设置回文件描述符

#include "head.h"  int main(void) {     int fd = 0;     char tmpbuff[4096] = {0};     int flags;     ssize_t nsize = 0;     char *pret = NULL;      mkfifo("/tmp/myfifo", 0777);     fd = open("/tmp/myfifo", O_RDONLY);     if (-1 == fd)     {         perror("fail to open");         return -1;     }      /* 获得fd文件描述符的属性 */     flags = fcntl(fd, F_GETFL);      /* 在现有属性中加入非阻塞属性 */     flags |= O_NONBLOCK;      /* 将新属性设置回fd文件描述符 */     fcntl(fd, F_SETFL, flags);      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 (NULL != pret)         {             printf("STDIN:%s\n", tmpbuff);         }       }          close(fd);      return 0; }

3.异步IO
    将一个文件描述符设定为异步IO,当IO有事件发生时,内核会向用户层发送SIGIO信号提醒用户层处理事件 
 signal(SIGIO,handle);

flag = fcntl(fd,F_GETFL);   

flag |= O_ASYNC;//将fd设置为异步IO(文件描述符,发生可读事件时,会发送信号通知)
fcntl(fd,F_SETFL,flag);//把异步IO事件的接收进程设置为当前进程,通知给当前进程
fcntl(fd,F_SETOWN,getpid());

#include "head.h"  int fd = 0;  void handler(int signo) {     char tmpbuff[4096] = {0};      memset(tmpbuff, 0, sizeof(tmpbuff));     read(fd, tmpbuff, sizeof(tmpbuff));     printf("RECV:%s\n", tmpbuff);      return; }  int main(void) {     char tmpbuff[4096] = {0};     int flags;      signal(SIGIO, handler);      mkfifo("/tmp/myfifo", 0777);     fd = open("/tmp/myfifo", O_RDONLY);     if (-1 == fd)     {         perror("fail to open");         return -1;     }      flags = fcntl(fd, F_GETFL);     flags |= O_ASYNC;     //将fd设置为异步IO(文件描述符发生可以读的事件,会发送信号通知)     fcntl(fd, F_SETFL, flags);     //通知给当前进程     fcntl(fd, F_SETOWN, getpid());      while (1)     {         memset(tmpbuff, 0, sizeof(tmpbuff));         gets(tmpbuff);         printf("STDIN:%s\n", tmpbuff);     }          close(fd);      return 0; }

4.多路复用IO
   select
        监听文件描述符集合,将所有要监听的事件加入集合中,使用select监听所有事件,当集合中有事件发生,  select不再阻塞,同时select会将产生事件的文件描述符留在集合中,而把没有产生事件的文件描述符从集合中踢出,所以留在集合中的文件描述即为产生事件的文件描述符,对其处理即可

注意:因为select会把没有产生事件的文件描述符从集合中踢出,所以要用一个临时变量tmp来使用select

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
          功能:
            监听文件描述符是否有事件发生
          参数:
            nfds:最大文件描述符的值 + 1 
            readfds:读文件描述符集合
            writefds:写文件描述符集合
            exceptfds:异常文件描述符集合
            timeout:超时时间
          返回值:
            成功返回产生事件的文件描述符个数
            失败返回-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 CreateTcpConnection(const char *pip, int port) {     int sockfd = 0;     int ret = 0;     struct sockaddr_in seraddr;     seraddr.sin_family = AF_INET;     seraddr.sin_port = htons(port);     seraddr.sin_addr.s_addr = inet_addr(SER_IP);      sockfd = socket(AF_INET, SOCK_STREAM, 0);     if (-1 == sockfd)     {         return -1;     }      ret = connect(sockfd, (struct sockaddr *)&seraddr, sizeof(seraddr));     if (-1 == ret)     {         return -1;     }      return sockfd; }  int HandleConnection(int sockfd) {     char tmpbuff[4096] = {0};     static int cnt = 0;     ssize_t nsize = 0;      sprintf(tmpbuff, "hello world --- %d", cnt);     nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);     if (-1 == nsize)     {         return -1;     }     cnt++;      memset(tmpbuff, 0, sizeof(tmpbuff));     nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);       if (-1 == nsize)     {         return -1;     }     else if (0 == nsize)     {         return 0;     }          printf("RECV:%s\n", tmpbuff);      return nsize; }  int main(void) {     int sockfd = 0;     int ret = 0;      sockfd = CreateTcpConnection(SER_IP, SER_PORT);     if (-1 == sockfd)     {         printf("连接服务器异常\n");         return -1;     }      while (1)     {         ret = HandleConnection(sockfd);         if (-1 == ret)         {             printf("连接出错!\n");             break;         }         else if (0 == ret)         {             printf("连接关闭\n");             break;         }          sleep(1);     }          close(sockfd);      return 0; }

 server.c

#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(port);     seraddr.sin_addr.s_addr = inet_addr(pip);     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; }  int main(void) {     int sockfd = 0;     int confd = 0;     int ret = 0;     fd_set rdfds;     fd_set tmpfds;     int nready = 0;     int maxfd = 0;     int i = 0;      //创建监听套接字     sockfd = CreateListenSocket(SER_IP, SER_PORT);     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 (-1 == nready)         {             perror("fail to select");             return -1;         }          //如果sockfd产生事件,处理新的连接请求,并将新的文件描述符加入集合,下次一起监听         if (FD_ISSET(sockfd, &tmpfds))         {             confd = accept(sockfd, NULL, NULL);             if (-1 == confd)             {                 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 (-1 == ret)                 {                     printf("连接异常\n");                     FD_CLR(i, &rdfds);                     close(i);                     continue;                 }                 else if (0 == ret)                 {                     printf("连接断开\n");                     FD_CLR(i, &rdfds);                     close(i);                     continue;                 }             }         }     }          close(sockfd);      return 0; }

    广告一刻

    为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!