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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值