`
tcspecial
  • 浏览: 913901 次
  • 性别: 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;
}

 

分享到:
评论

相关推荐

    linux下epoll网络模型介绍

    ### Linux 下 epoll 网络模型介绍 在深入探讨 Linux 下的 epoll 模型之前,我们先了解一下 epoll 的背景以及它为何成为 Linux 内核中 I/O 多路复用的关键技术之一。 #### 1. epoll 的背景与优势 早期 Linux 内核...

    Linux IO模型/epoll

    总结来说,Linux的epoll模型是为了解决高并发I/O问题而设计的,通过线程池配合epoll,可以在多核CPU环境下有效地管理网络连接,优化系统资源的使用,提升服务器性能。在选择模型时,需要根据具体应用场景和需求来...

    linux网络编程基础,包括tcp/upd,select/poll/epoll

    在Linux系统中,网络编程是实现跨机器通信的关键技术,主要涉及TCP(传输控制协议)和UDP(用户数据报协议)这两种传输层协议,以及I/O多路复用技术如select、poll和epoll。这些知识点是每一个资深的Linux程序员必须...

    C/C++ 学习代码实例 - socket编程代码(包括阻塞式、非阻塞式:select/epoll模式)

    3. **epoll**:这是 Linux 提供的一种高效 I/O 多路复用机制。epoll 使用“事件就绪通知”的方式,只有当文件描述符上有事件发生时,才会通知用户进程。这种方式减少了不必要的系统调用,提高了性能。epoll 有两种...

    网络io与io多路复用select/poll/epoll 服务器并发代码实现

    本文将深入探讨网络I/O的基本概念,以及select、poll和epoll这三种I/O多路复用机制,并通过实际代码示例展示其在服务器并发中的应用。 首先,理解网络I/O。在网络通信中,数据传输通常涉及发送(output)和接收...

    Linux C++ epoll使用范例

    在Linux系统中,`epoll`是用于I/O多路复用的一种高效机制,尤其适合高并发、大连接数的网络编程场景。本资源提供的"Linux C++ epoll使用范例"包含了客户端、服务端以及一个测试程序,旨在帮助开发者更好地理解和运用...

    linux内核select/poll,epoll实现与区别

    - 在Linux中,`select`、`poll`和`epoll`都是为了实现多路复用I/O,但随着技术的发展,`epoll`成为了现代应用的首选,尤其是在高并发网络服务中。理解这些机制的实现细节有助于优化代码,提高系统的响应速度和资源...

    Linux网络通信epoll模型

    总的来说,epoll模型是Linux系统中实现高并发网络服务的重要工具,它通过优化的事件通知机制,显著提升了程序处理能力,使得开发者能够构建出更加稳定、高效的网络应用。通过阅读和理解`epollServer.cpp`和`client....

    EPOLL-linux下select-poll的增强版

    【EPOLL - Linux 下的 Select 和 Poll 增强版】 EPOLL 是 Linux 内核提供的 I/O 多路复用技术,它是 Select 和 Poll 的更高效版本,特别是在处理大量并发连接时。EPOLL 提供了一种基于事件的异步 I/O 模型,能够...

    linux下Epoll模型实例代码

    Epoll模型相比传统的select、poll等I/O多路复用模型,具有更好的性能和可扩展性。这个"linux下Epoll模型实例代码"是一个展示如何在Linux环境下使用Epoll进行I/O事件监控的程序示例。 Epoll的核心概念包括以下几个...

    linux 网络库 C++ epoll

    该网络库的核心是epoll模型的封装,通过C++的面向对象特性,将epoll的创建、监听、事件注册和轮询等操作进行了抽象,简化了开发者对底层细节的处理。这样,开发者可以更专注于业务逻辑,而无需关心底层的I/O操作。 ...

    Linux网络通信select模型

    下面将详细探讨Linux网络通信中的select模型。 首先,`select`函数是Linux系统调用之一,它的主要作用是监控一组文件描述符,等待它们变为可读、可写或出现错误状态。这使得开发者可以构建高效的并发服务器,避免了...

    epoll网络模型

    2. **EPOLL模型**:在`epoll`中,主要有三个关键函数:`epoll_create`、`epoll_ctl`和`epoll_wait`。 - `epoll_create`:创建一个`epoll`实例,返回一个描述符。 - `epoll_ctl`:用于管理和修改`epoll`实例中的...

    网络编程epoll模型

    网络编程中的`epoll`模型是Linux操作系统提供的一种高效、高性能的I/O多路复用技术,主要用于管理和调度大量的并发连接。在这个模型中,`epoll`替代了传统的`select`和`poll`,解决了它们在处理大量文件描述符时性能...

    linux_epoll模型

    总的来说,理解并熟练掌握`epoll`模型是Linux系统编程和高性能网络服务开发中的重要技能。通过对`select`和`epoll`的区别进行深入分析,开发者可以更好地优化自己的程序,以适应不断增长的并发连接需求。

    select网络模型-Linux

    在Linux操作系统中,`select`网络模型是一种古老但仍然广泛使用的I/O多路复用机制。它是编程中处理多个文件描述符(FDs)状态变化的一种方式,允许程序在一个单一的调用中等待多个文件描述符中的任何一个变为可读、...

    linux_threadpool.zip_epoll select _epoll thread_epoll编程_thread

    epoll是Linux内核提供的更为高效的一种I/O多路复用技术,相比select和poll,它能处理更多的并发连接,并且有更好的性能。epoll采用“事件驱动”模型,通过`epoll_create()`、`epoll_ctl()`和`epoll_wait()`三个系统...

    Linux下基于EPOLL机制的海量网络信息处理模型.pdf

    在Linux/QT平台上实现基于EPOLL的海量网络信息处理模型,一般步骤如下: 1. **创建EPOLL实例**:首先,通过`epoll_create()`函数创建一个EPOLL实例,用于管理和监控文件描述符集合。 2. **注册事件**:使用`epoll_...

    linux c epoll服务器windows 客户端通信

    Epoll是Linux内核提供的一种高效I/O事件通知机制,而Windows Socket API是微软提供的用于网络编程的标准接口。 首先,我们来理解Linux C的epoll服务器。Epoll(Event Poll)是Linux 2.6内核引入的改进版的poll机制...

    epoll模型的一个例子

    `epoll`模型是解决高并发服务器性能瓶颈的有效手段之一,它比传统的`select`和`poll`模型更加先进,能够更有效地管理和监控文件描述符(FDs)的状态变化。 `epoll`的核心概念包括`epoll_create`、`epoll_ctl`和`...

Global site tag (gtag.js) - Google Analytics