`
tcspecial
  • 浏览: 895994 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

Linux select/epoll网络模型

阅读更多

select,epoll网络模型经常在面试中出现,epoll是对poll的优化,是linux下最优秀的网络模型

epoll优点:

# 相对select,没有最大并发数限制 /proc/sys/file-max

# 数据传递(用户空间跟内核空间)通过共享内存(mmap)方式

# epoll_wait 直接返回被触发的fd对应的一块buffer,不需要遍历所有的fd

 

一.Linux select模型

流程:

1. 声明数组fd_A,添加多个socket client fd

2. 监听端口

3. 将sock_fd 和 数组fd不为0描述符放入select将检查的fd_set中

4. 处理 fdsr可以接收数据的连接; 如是sock_fd,添加新连接至fd_A;  

详见: http://blog.chinaunix.net/uid-25808509-id-2233262.html

 

// select_tcp_server.c   
#include <stdio.h>  
#include <stdlib.h>  
#include <unistd.h>  
#include <errno.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/socket.h>  
#include <netinet/in.h>  
#include <arpa/inet.h>  
  
#define MYPORT 1234    // the port users will be connecting to  
#define BACKLOG 5     // how many pending connections queue will hold  
#define BUF_SIZE 200  
  
int fd_A[BACKLOG];    // accepted connection fd  
int conn_amount;    // current connection amount  
  
void showclient()  
{  
    int i;  
    printf("client amount: %d\n", conn_amount);  
    for (i = 0; i < BACKLOG; i++) {  
        printf("[%d]:%d ", i, fd_A[i]);  
    }  
    printf("\n\n");  
}  
  
int main(void)  
{  
    int sock_fd, new_fd;  // listen on sock_fd, new connection on new_fd  
    struct sockaddr_in server_addr;    // server address information  
    struct sockaddr_in client_addr; // connector's address information  
    socklen_t sin_size;  
    int yes = 1;  
    char buf[BUF_SIZE];  
    int ret;  
    int i;  
  
    if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {  
        perror("socket");  
        exit(1);  
    }  
  
    if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {  
        perror("setsockopt");  
        exit(1);  
    }  
      
    server_addr.sin_family = AF_INET;         // host byte order  
    server_addr.sin_port = htons(MYPORT);     // short, network byte order  
    server_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP  
    memset(server_addr.sin_zero, '\0', sizeof(server_addr.sin_zero));  
  
    if (bind(sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1) {  
        perror("bind");  
        exit(1);  
    }  
  
    if (listen(sock_fd, BACKLOG) == -1) {  
        perror("listen");  
        exit(1);  
    }  
  
    printf("listen port %d\n", MYPORT);  
  
    fd_set fdsr;  
    int maxsock;  
    struct timeval tv;  
  
    conn_amount = 0;  
    sin_size = sizeof(client_addr);  
    maxsock = sock_fd;  
    while (1) {  
        // initialize file descriptor set  
        FD_ZERO(&fdsr);  
        FD_SET(sock_fd, &fdsr);  
  
        // timeout setting  
        tv.tv_sec = 30;  
        tv.tv_usec = 0;  
  
        // add active connection to fd set  
        for (i = 0; i < BACKLOG; i++) {  
            if (fd_A[i] != 0) {  
                FD_SET(fd_A[i], &fdsr);  
            }  
        }  
  
        ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);  
        if (ret < 0) {  
            perror("select");  
            break;  
        } else if (ret == 0) {  
            printf("timeout\n");  
            continue;  
        }  
  
        // check every fd in the set  
        for (i = 0; i < conn_amount; i++) {  
            if (FD_ISSET(fd_A[i], &fdsr)) {  
                ret = recv(fd_A[i], buf, sizeof(buf), 0);  
                if (ret <= 0) {        // client close  
                    printf("client[%d] close\n", i);  
                    close(fd_A[i]);  
                    FD_CLR(fd_A[i], &fdsr);  
                    fd_A[i] = 0;  
                } else {        // receive data  
                    if (ret < BUF_SIZE)  
                        memset(&buf[ret], '\0', 1);  
                    printf("client[%d] send:%s\n", i, buf);  
                }  
            }  
        }  
  
        // check whether a new connection comes  
        if (FD_ISSET(sock_fd, &fdsr)) {  
            new_fd = accept(sock_fd, (struct sockaddr *)&client_addr, &sin_size);  
            if (new_fd <= 0) {  
                perror("accept");  
                continue;  
            }  
  
            // add to fd queue  
            if (conn_amount < BACKLOG) {  
                fd_A[conn_amount++] = new_fd;  
                printf("new connection client[%d] %s:%d\n", conn_amount,  
                        inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));  
                if (new_fd > maxsock)  
                    maxsock = new_fd;  
            }  
            else {  
                printf("max connections arrive, exit\n");  
                send(new_fd, "bye", 4, 0);  
                close(new_fd);  
                break;  
            }  
        }  
  
        showclient();  
    }  
  
    // close other connections  
    for (i = 0; i < BACKLOG; i++) {  
        if (fd_A[i] != 0) {  
            close(fd_A[i]);  
        }  
    }  
  
    exit(0);  
}

 

二. linux epoll模型

//epoll_tcp_server.c
#include <stdio.h>
#include <unistd.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <list>
#include <stdlib.h>
#include <string.h>


#define BUF_SIZE 1024
#define SERV_PORT 1234
#define EPOLL_RUN_TIMEOUT -1
#define EPOLL_SIZE 10000

#define STR_WELCOME "welcome to seChat! You ID is:Client #%d"
#define STR_MESSAGE "Client #%d>>%s"
#define STR_NOONE_CONNECTED "Noone connected to server except you"

#define CHK(eval) if(eval<0){ perror("eval"); exit(-1); }
#define CHK2(res,eval) if((res = eval)<0){ perror("eval"); exit(-1); }


using namespace std;

list<int> clients_list;


// 设置非阻塞
int SetNoblocking(int sockfd)
{
	CHK(fcntl(sockfd,F_SETFL,fcntl(sockfd,F_GETFD,0)|O_NONBLOCK));
	return 0;
}


// 处理消息
int HandleMsg(int client)
{
	char buf[BUF_SIZE],message[BUF_SIZE];
	bzero(buf,BUF_SIZE);
	bzero(message,BUF_SIZE);

	int len;
	CHK2(len,recv(client,buf,BUF_SIZE,0));
	printf("Accept:%s\n",buf);

	// 客户端关闭
	if(len==0)
	{
		CHK(close(client));
		clients_list.remove(client);
	}else
	{
		// 向客户端发送消息
		if(clients_list.size()==1)
		{
			CHK(send(client,STR_NOONE_CONNECTED,strlen(STR_NOONE_CONNECTED),0));
			return len;
		}

		sprintf(message,STR_MESSAGE,client,buf);

		list<int>::iterator it;
		for(it = clients_list.begin(); it!=clients_list.end();it++)
		{
			if(*it!=client)
			{
				CHK(send(*it,message,BUF_SIZE,0));
			}
		}
	}

	return len;
}


int main(int argc,char **argv)
{
	int listener;
	struct sockaddr_in addr,their_addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(SERV_PORT);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");
	socklen_t socklen;
	socklen = sizeof(struct sockaddr_in);

	static struct epoll_event ev,events[EPOLL_SIZE];
	ev.events = EPOLLIN|EPOLLET; // 读感兴趣,边沿触发 

	char message[BUF_SIZE];
	int epfd;
	clock_t tStart;
	int client,res,epoll_events_count;

	CHK2(listener,socket(AF_INET,SOCK_STREAM,0));
	SetNoblocking(listener);

	CHK(bind(listener,(struct sockaddr*)&addr,sizeof(addr)));
	CHK(listen(listener,1));

	CHK2(epfd,epoll_create(EPOLL_SIZE));
	ev.data.fd = listener;
	CHK(epoll_ctl(epfd,EPOLL_CTL_ADD,listener,&ev));

	while(1)
	{
		CHK2(epoll_events_count,epoll_wait(epfd,events,EPOLL_SIZE,EPOLL_RUN_TIMEOUT));

		tStart = clock();

		int i;
		for(i=0;i<epoll_events_count;i++)
		{
			if(events[i].data.fd == listener) // new connection
			{
				CHK2(client,accept(listener,(struct sockaddr*)&their_addr,&socklen));

				SetNoblocking(client);
				ev.data.fd = client;

				CHK(epoll_ctl(epfd,EPOLL_CTL_ADD,client,&ev)); // register

				clients_list.push_back(client); // add to list

				bzero(message,BUF_SIZE);
				res = sprintf(message,STR_WELCOME,client);
				CHK2(res,send(client,message,BUF_SIZE,0));
			}else
			{
				CHK2(res,HandleMsg(events[i].data.fd));
			}

			printf("Statistics: %d events handled at: %.2fs\n",epoll_events_count,(double)(clock()-tStart)/CLOCKS_PER_SEC); }
	}

	close(listener);
	close(epfd);

	return 0;
}

 

    TCP测试客户端,模拟1万个连接

// huge_tcp_client.c 
#define EPOLL_SIZE 10000
using namespace std;

char message[BUF_SIZE];
list<int> list_of_clients;
int res;
clock_t tStart;

int main(int argc,char **argv)
{
	int sock;
	struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(SERV_PORT);
	addr.sin_addr.s_addr = inet_addr("127.0.0.1");

	tStart = clock();

	for(int i=0;i<EPOLL_SIZE;i++)
	{
		CHK2(sock,socket(AF_INET,SOCK_STREAM,0));
		CHK(connect(sock,(struct sockaddr*)&addr,sizeof(addr))<0);
		list_of_clients.push_back(sock);

		bzero(&message,BUF_SIZE);
		CHK2(res,recv(sock,message,BUF_SIZE,0));
		printf("Accept:%s\n",message);	
	}

	list<int>::iterator it;
	for(it = list_of_clients.begin();it!=list_of_clients.end();it++)
	{
		close(*it);
	}

	printf("Test passed:.2fs\n",(double)(clock()-tStart)/CLOCKS_PER_SEC);
	printf("Total server connections:%d\n",EPOLL_SIZE);

	return 0;
}

 

分享到:
评论

相关推荐

    高性能跨平台网络通信框架.rar

    高性能跨平台网络通信框架代码,支持linux,windows,macos 等操作系统,可以编译使用, 描述 - ***Server*** 基于IOCP / EPOLL通信模型,并结合缓存池、私有堆等技术实现高效内存管理,支持超大规模、高并发通信...

    linux_epoll模型

    详细介绍了epoll模型,并且介绍了select模型,以及两者的区别

    select poll epoll 代码实例

    select poll epoll 代码实例

    C++教程网《Linux网络编程》视频百度云地址

    Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章。 Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP...

    linux epoll模型

    所以,剩下的select模型基本上就成为我们在linux上面的唯一选择,其实,如果加上no-block socket的配置,可以完成一个"伪"AIO的实现,只不过推动力在于你而不是os而已。不过传统的select/poll函数有着一些无法忍受...

    EPOLL模型详解

    这时候你一是可以选择修改这个宏然后重新编译内核,不过资料也同时指出这样会带来网络效率的下降,二是可以选择多进程的解决方案(传统的 Apache方案),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,...

    libevent for qt网络模块,直接替换qt的select模型,支持epoll,select,pool.使用非常简单,无需修改以前的代码结构

    最近在开发im服务器 需要大并发链接 QT默认的是使用select模型的 这种轮询方式非常慢 在高并发连接 我们需要epoll才能发挥linux服务器的性能 而且使用简单 整个服务端代码架构无需修改 直接可以使用 只要在 main...

    论文研究-LINUX平台下基于EPOLL的FTP服务器设计与实现 .pdf

    LINUX平台下基于EPOLL的FTP服务器设计与实现,王亚昌,,网络服务器通常采用多进程、多线程并发机制或者是基于select/poll的I/O复用模型实现,传统的FTP服务器大都使用前者。本文针对Linux 2.6内�

    EPOLL模型:关于并发连接的处理

    Linux 2.6内核中提高网络I/O性能的新方法-epoll I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select函数。 1、为什么select落后  首先,在Linux内核中,select所用到的FD_SET是有限的,即内核...

    linux网络编程

    17socket编程(十二) select限制 poll 18socket编程(十三) epoll使用 epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程...

    linux epoll机制详解

    在linux 没有实现epoll事件驱动机制之前,我们一般选择用select或者poll等IO多路复用的方法来实现并发服务程序。在linux新的内核中,有了一种替换它的机制,就是epoll。 select()和poll() IO多路复用模型 select的...

    Linux网络编程5种模型实现源代码

    linux下网络编程5种模型Select,Poll,Epoll,多线程,多进程,实现源代码,注释较为详细,方便初学者学习

    Linux网络编程 视频 教程

    Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章。 Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP...

    socket网络编程-epoll-水平触发和边缘触发源码

    (2)epoll技术的性能,可以说非常惊艳,它是能够使单台计算机支撑数百万甚至数十万上百万并发的核心技术,远优于其他I/O模型或I/O函数(如select、poll函数),select和poll这类技术因为系统内部实现问题,当并发...

    [免费]2018年C++教程网的linux网络编程视频百度云下载链接.rar

    Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章。 Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP...

    c++教程网的linux网络编程视频下载

    Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章。 Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP...

    浅谈Linux 网络 I/O 模型简介(图文)

    根据UNIX网络编程对I/O模型的分类,UNIX提供了5种I/O模型。 1.1、阻塞I/O模型 最常用的I/O模型,默认情况下,所有文件操作都是阻塞的。 比如I/O模型下的套接字接口:在进程空间中调用recvfrom,其系统调用直到数据包...

    LINUX网络编程

    基于LINUX的SOCKET网络编程,使用SELECT模型,IO阻塞模型,EPOLL模型

    C++教程网视频:linux网络编程

    Linux网络编程(总共41集) 讲解Linux网络编程知识,分以下四个篇章。 Linux网络编程之TCP/IP基础篇 Linux网络编程之socket编程篇 Linux网络编程之进程间通信篇 Linux网络编程之线程篇 Linux网络编程之TCP/IP...

Global site tag (gtag.js) - Google Analytics