您的位置:首页 > 运维架构 > Linux

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);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐