Linux下使用select处理socket数据
2018-01-21 14:40
435 查看
IO口复用
以前写的服务端都是阻塞+多线程模型 socket—–>bind——>listen——>循环accept——>客户端连接——>创建进程或者线程接受数据。显然当有大量长连接时,会创建大量的进程或者线程消耗大量系统资源。IO口复用简单的说就是一个线程中处理多个文件描述符连接。设置好我们关注的文件描述符后。系统会循环查询文件描述符是否可读、可写、或者有异常,当查询到有一个满足时就会设置文件描述符状态并返回。
select,poll,epoll
Linux 下的IO口复用主要有下面几个函数select ,poll,epoll。-select是数组实现的它监控的文件描述符有限。调用select时内核会循环查询文件描述符的状态所以随着监控的文件描述符增多性能也会随之下降。select返回时内核还会复制文件描述符状态到用户空间。
-poll 和select原理差不多,但是poll是用链表实现它没有文件描述符上限
-epoll 当事件发生时 epoll会直接回调函数,它不会随着文件描述数量增多导致性能的下降,而且epoll通过mmap来映射内存的不会从内存空间复制数据到用户空间
通过上面的说我们明显感觉到epoll是最好的但是由于epoll比较复杂且只有在linux 2.6后内核系统才支持。相比select的跨平台代码可移植好,在文件描述符小的情况下性能还是不错的。具体选择哪一个根据应用场景来选择吧。
select
/* 函数描述:阻塞的等待文件描述符条件发生 参数:int nfds ,文件描述符的最大的值+1 fdset *readfds 监控可读文件的描述集 fdset* writefds 监控可写文件的描述集 fd_set * exceptfds 监控异常文件描述集 struct timeval * timeout 超时时间,多长时间没有事件发生自动返回 返回值: -1 错误 0 超时返回 大于0 返回准备好的文件描述符符 */ int select( int nfds, fd_set FAR* readfds, fd_set * writefds, fd_set * exceptfds, const struct timeval * timeout);
服务端代码
/* ============================================================================ Name : select.c Author : Version : Copyright : Your copyright notice Description : socket IO复用 接受cleint的数据并打印 4000 出来 ============================================================================ */ #include <stdio.h> #include <stdlib.h> #include<sys/socket.h> #include<sys/select.h> #include<sys/types.h> #include<sys/un.h> /*------------------------------------------------------------------------- * 宏定义 * -----------------------------------------------------------------------*/ #define ERR 1 #define OK 0 #define SER_NAME "/tmp/test.com" #define MAX_NUM_CLIENT 5 #define BUFF_SIZE 1024 #define LOG(fmt, ...) printf(fmt, ##__VA_ARGS__) /*------------------------------------------------------------------------- * 全局变量定义 * -----------------------------------------------------------------------*/ /*服务器socket 描述符*/ int g_ser_fd=-1; fd_set g_fd_set; int g_client_fds[MAX_NUM_CLIENT]; int g_max_fd; char g_buff[BUFF_SIZE]; /*------------------------------------------------------------------------- * 函数实现 * -----------------------------------------------------------------------*/ /* * 函数名:init * 函数功能:初始化 * 参数 * * 返回值 * ERR 失败 * OK 成功 * */ int init() { int ret=-1; int i=0; struct sockaddr_un ser_addr; /*创建socket*/ g_ser_fd = socket(AF_UNIX,SOCK_STREAM,0); if(g_ser_fd<0) { LOG("创建SOCKET失败\n"); return ERR; } /*地址初始化*/ memset(&ser_addr,'\0',sizeof(ser_addr)); ser_addr.sun_family=AF_UNIX; strncpy(&ser_addr.sun_path,SER_NAME,sizeof(SER_NAME)-1); /*绑定地址*/ unlink(SER_NAME); ret = bind(g_ser_fd,(struct sockaddr *)&ser_addr,sizeof(ser_addr)); if(ret<0) { LOG("绑定SOCKET失败\n"); close(g_ser_fd); g_ser_fd=-1; return ERR; } /*监听*/ ret = listen(g_ser_fd,MAX_NUM_CLIENT); if(ret<0) { LOG("绑定SOCKET失败\n"); close(g_ser_fd); g_ser_fd=-1; return ERR; } LOG("sever listen is ok\n"); /*g_fd_set*/ FD_ZERO(&g_fd_set); FD_SET(g_ser_fd,&g_fd_set); /*g_client_fds*/ for(i=0;i<MAX_NUM_CLIENT;i++) { g_client_fds[i]=-1; } /*g_max_fd*/ g_max_fd=g_ser_fd; return OK; } /* * 函数名:handle_client_connection * 函数功能:处理客户端的链接 * 参数 * * 返回值 * ERR 失败 * OK 成功 * */ int handle_client_connection() { int i=0; //struct sockaddr_un addr; /*地址初始化*/ //memset(&addr,'\0',sizeof(addr)); //addr.sun_family=AF_UNIX; //strncpy(&addr.sun_path,SER_NAME,sizeof(SER_NAME)-1); //accept(g_ser_fd,(struct sockaddr*)&addr,sizeof(addr)); int new_fd = accept(g_ser_fd,NULL,NULL); if(new_fd<0) { LOG("accept error\n"); return ERR; } for(i=0;i<MAX_NUM_CLIENT;i++) { if(-1==g_client_fds[i]) { g_client_fds[i]=new_fd; break; } } if(MAX_NUM_CLIENT==i) { LOG("accept client is full\n"); close(new_fd); return ERR; } if(g_max_fd<new_fd) { g_max_fd=new_fd; } FD_SET(new_fd,&g_fd_set); LOG("new client %d connect is ok\n",new_fd); return OK; } /* * 函数名:handle_client_connection * 函数功能:处理客户端数据 * 参数 int index 客户端sockdf 索引 * * 返回值 * ERR 失败 * OK 成功 * */ int handle_client(int index) { int ret=-1; ret = recv(g_client_fds[index],g_buff,BUFF_SIZE,0); if(ret < 0) { //LOG("接受数据失败\n"); } /*client 断开链接处理*/ else if(0==ret) { FD_CLR(g_client_fds[index],&g_fd_set); close(g_client_fds[index]); LOG("client %d close \n",g_client_fds[index]); } else { g_buff[ret]='\0'; LOG("recv client data : %s\n",g_buff); } } int main(void) { int ret = -1,i=0; fd_set read_set; ret = init(); if(OK!=ret) { LOG("初始化失败\n"); return -1; } /*select*/ for(;;) { memcpy(&read_set,&g_fd_set,sizeof(read_set)); ret = select(g_max_fd+1,&read_set,NULL,NULL,NULL); /*select 错误*/ if(ret<0) { LOG("select error\n"); FD_ZERO(&g_fd_set); FD_SET(g_ser_fd,&g_fd_set); for(i=0;i<MAX_NUM_CLIENT;i++) { if(g_client_fds[i]>0) { close(g_client_fds[i]); g_client_fds[i]=-1; } } FD_ZERO(&g_fd_set); FD_SET(g_ser_fd,&g_fd_set); continue; } /*select 超时*/ if (0==ret) { LOG("select time out\n"); continue; } /*client 的连接处理*/ if(FD_ISSET(g_ser_fd,&read_set)) { handle_client_connection(); continue; } /*client 数据处理*/ for(i=0;i<MAX_NUM_CLIENT;i++) { if(0<g_client_fds[i]) { if(FD_ISSET(g_client_fds[i],&read_set)) { handle_client(i); } } } } close(g_ser_fd); }
相关文章推荐
- Linux socket之四:使用POLL机制处理多连接
- Linux socket之四:使用POLL机制处理多连接
- 【Linux编程】用select处理普通数据和带外数据
- Linux socket之四:使用POLL机制处理多连接
- Linux socket之四:使用POLL机制处理多连接
- Linux socket之四:使用POLL机制处理多连接
- linux网络编程之socket:使用fork并发处理多个client的请求
- linux 网络编程:使用两线程实现socket同时收发数据
- linux网络编程:使用多进程实现socket同时收发数据
- PL/SQL编辑数据"这些查询结果不可更新,请包括ROWID或使用SELECT...FOR UPDATE获得可更新结果"处理
- Linux C语言编程-Linux网络通信--Linux上使用套接字(socket)来处理信息---编写一个单进程非阻塞多客户的套接字客户端
- Linux socket编程之使用epoll处理海量连接(程序例程)
- linux网络编程之socket(四):使用fork并发处理多个client的请求和对等通信p2p
- linux非阻塞的socket发送数据出现EAGAIN错误的处理方法
- Linux 网络socket等数据使用巧妙之处
- 网络编程(53)—— Windows下使用WSAAsyncSelect实现窗口处理socket消息
- linux网络编程之socket(四):使用fork并发处理多个client的请求和对等通信p2p
- (转)PL/SQL编辑数据"这些查询结果不可更新,请包括ROWID或使用SELECT...FOR UPDATE获得可更新结果"处理
- PL/SQL编辑数据"这些查询结果不可更新,请包括ROWID或使用SELECT...FOR UPDATE获得可更新结果"处理
- 中标麒麟(linux)下QUdpSocket无法使用组播发送数据问题