ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

Linux高并发学习----一请求一线程/select/poll/epoll基本使用

2021-12-25 11:58:01  阅读:165  来源: 互联网

标签:listenfd epoll int pollfds Linux ---- fd printf connfd


1、概述

作为服务端,同时支持多用户连接是必然要求,在刚开始学习网络编程时,咱们所想到的几种常见用法如下:
1、一个请求对应一个线程:即给每一个新连接用户分配一个新线程,在该线程处理业务,这种情况显然只适用于很小规模连接的场景,毕竟线程资源是有限的,一般的pc能开到几百个线程就不错了。
2、select:能够将大量事件存入对应集合中,只需要遍历集合,处理不同类型事件即可,但是支持的socket连接有限,一般是1024。
3、poll:类似于select,但是处理事件时更加方便,不需要设置多个事件集合,且支持的socket连接较select也大幅提升。
4、epoll:让Linux成为服务器端开发主战场的得力功臣,支持单机百万并发连接,相较于select/poll,不需要对事件集合一一遍历(有些连接处于就绪态,没有事件触发),效率很高。

2 、一请求一线程

在这种情况下,每当accept一个连接,则创建一个新线程,在线程中处理用户事件即可,下面的代码省略了socket从创建到listen的阶段。

    for(;;)
    {
         connfd = accept(listenfd, (struct sockaddr*)&client_adr, &client_adr_lenth);
         pthread_t threadId;
         pthread_create(&threadId, NULL, threadFunc, (void*)&connfd);
    }
    close(listenfd);

对应的线程入口函数如下:

void *threadFunc(void *args)
{
     int cntFd = *(int *)args;
     if(cntFd > 0)
          printf("recv a connection! clientfd = %d\n", cntFd);
     else
     {
          printf("accept error!\n");
          exit(1);
     }
     while (1)
     {
          int n = recv(cntFd, buf, 256, 0);
          if(n > 0)
          {
               printf("recv : %s\n", buf);
               write(cntFd, str, strlen(str));
          }
          else
          {
               printf("对端fd = %d 断开连接!\n", cntFd);
               close(cntFd);
               break;
          }   
     }   
}

3、select

同样省略socket从创建到listen的阶段,给出select示例代码如下:

fd_set rfds, rset, wfds, wset;
	FD_ZERO(&rfds);
	FD_SET(listenfd, &rfds);
	FD_ZERO(&wfds);
	int max_fd = listenfd;
	while (1) 
     {
		rset = rfds;
		wset = wfds;
          // rset是读事件集合,wset是写事件集合,后续事件处理从对应集合中读取即可
		int nready = select(max_fd+1, &rset, &wset, NULL, NULL);  
		if (FD_ISSET(listenfd, &rset))  // 首先处理完成三次握手的连接
          { 
			struct sockaddr_in client;
		     socklen_t len = sizeof(client);
               if ((connfd = accept(listenfd, (struct sockaddr *)&client, &len)) == -1) 
               {
                    printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
                    return 0;
               }
			FD_SET(connfd, &rfds);
			if (connfd > max_fd) max_fd = connfd;
			if (--nready == 0) continue;
		}

		int i = 0;
		for (i = listenfd + 1;i <= max_fd;i ++) 
          {
               if (FD_ISSET(i, &rset)) 
               { 
                    n = recv(i, buff, MAXLNE, 0);
                    if (n > 0) 
                    {
                         buff[n] = '\0';
                         printf("recv msg from client fd = %d: %s\n", i, buff);
                         FD_SET(i, &wfds);
                         send(i, buff, n, 0);
                    } 
                    else if (n == 0) 
                    { 
                         FD_CLR(i, &rfds);
                         printf("对端断开连接!fd = %d\n", i);
                         close(i);				
                    }
                    if (--nready == 0)  break;
               }
		}
	}

4、poll

	 struct pollfd pollfds[MAX_POLL_SIZE]; // 存放发生的事件集合
     pollfds[0].fd = listenfd;
     pollfds[0].events = POLLIN;
     pollfds[0].revents = 0;
     int maxfd = listenfd;

     for(int i = 1; i < MAX_POLL_SIZE; ++i)
          pollfds[i].fd = -1;

     while(1)
     {
          int nready = poll(pollfds, maxfd+1, -1);
          if(nready < 0)
          {
               if(errno == EINTR)  continue;
               break;
          }
          else if(nready == 0) // 超时
               continue;

          if(pollfds[0].revents & POLLIN)   // 完成三次握手的连接
          {
               struct sockaddr_in clientAddr;
               socklen_t len = sizeof(clientAddr);
               connfd = accept(listenfd, (struct sockaddr*)&clientAddr, &len);
               if(fcntl(connfd, F_SETFL, O_NONBLOCK) == -1)
               {
                    printf("fcntl() fail!\n");
                    return -1;
               }
               pollfds[connfd].fd = connfd;
               pollfds[connfd].events = POLLIN;
               pollfds[connfd].revents = 0;
               if(connfd > maxfd)  maxfd = connfd;
               
               printf("new connnection : %d accepted!\n", connfd);
               if(--nready == 0)   continue;
          }

          for(int i = listenfd; i <= maxfd; ++i)
          {
               if(pollfds[i].revents & POLLIN)
               {
                    // 有数据到达
                    int n = recv(i, buf, 256, 0);
                    if(n == 0)
                    {
                         printf("对端 %d 断开连接!\n", i);
                         pollfds[i].fd = -1;
                         close(i);
                    }
                    else if(n < 0)
                    {
                         if(errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR)
                         {
                              printf("异常错误! errno = %d:%s\n", errno, strerror(errno));
                              return -1;
                         }
                    }
                    else
                    {
                         buf[n] = '\0';
                         printf("recv msg: %s\n", buf);
                         send(i, buf, n, 0);
                    }
                    if(--nready == 0) break;
               }
          }
     }

5、epoll

     int epollfd = epoll_create(1);  
     struct epoll_event events[512], ev;
     ev.events = EPOLLIN;
     ev.data.fd = listenfd;
     epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev);
     while(1)
     {
          int nready = epoll_wait(epollfd, events, 512, -1);
          if(nready == -1)
               continue;    
          for(int i = 0; i < nready; ++i)
          {
               int clientfd = events[i].data.fd;
               if(clientfd == listenfd)  // 完成三次握手的连接
               {
                    struct sockaddr_in clientAdr;
                    socklen_t len = sizeof(clientAdr);
                    connfd =  accept(clientfd, (struct sockaddr*)&clientAdr, &len);
                    printf("new connection: %d\n", connfd);
                    ev.data.fd = connfd;
                    ev.events = EPOLLIN;
                    epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &ev);
               }
               else
               {
                    if(events[i].events & EPOLLIN) // 读事件
                    {
                         printf("recv:\n");
                         int n = recv(clientfd, buf, 256, 0);
                         if(n == 0)
                         {
                              printf("client fd = %d closed!\n", clientfd);
                              ev.events = EPOLLIN;
                              ev.data.fd = clientfd;
                              epoll_ctl(epollfd, EPOLL_CTL_DEL, clientfd, &ev);
                              close(clientfd);
                         }
                         else
                         {
                              buf[n] = '\0';
                              printf("%s\n", buf);
                              send(clientfd, buf, n, 0);
                         }
                    }
               }
          }
     }

6、后续

本篇文章使用了几种最基本的支持多连接的情况,下一篇文章将介绍使用epoll的reactor写法。
代码中有问题的欢迎评论区留言哈!

标签:listenfd,epoll,int,pollfds,Linux,----,fd,printf,connfd
来源: https://blog.csdn.net/hjlogzw/article/details/122109997

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有