linux select服务端,linux下的select函数和一种并发服务器的实现

select函数

函数原型:

int select(int nfds, fd_set *readfds, fd_set *writefds,

fd_set *exceptfds,struct timeval *timeout);

man page 中对select函数的说明如下:

select() and pselect() allow a program to monitor multiple file descriptors, waiting until one or more of the file descriptors become "ready" for some class of I/O operation (e.g., input possible). A file descriptor is considered ready if it is possible to perform the corresponding I/O operation (e.g.,(2)) without blocking.

也就是说,select函数可以监控多个文件描述符,等待直到其中一个或多个文件可以进行读取或写入操作而不被阻塞时,才返回。

参数:

nfds:  要监视的fd最大值加1,设置select监视的文件描述符范围可以提高效率

readfds: 要进行读取操作的文件描述符集

writefds:  要进行写入操作的文件描述符集

exceptfds: 要监视异常的文件描述符集

timeout: 使用struct timeval描述的超时时间,设为NULL时,表示永远等待

返回值:

返回-1表示出错

返回0表示没有文件描述符准备好

返回正值表示已经准备好的描述符个数

fd_set:

fd_set用来描述一个文件描述符集。可以使用以下四个接口对它进行操作。

#include

voidFD_ZERO(fd_set, *fdset);

voidFD_CLR(int fd, fd_set, *fdset);

voidFD_SET(int fd, fd_set, *fdset);

intFD_ISSET(int fd, fd_set, *fdset);

FD_ZERO 用来清空一个fdset(清除所有位)

FD_CLR  用来清除fdset中指定位

FD_SET 用来设置fdset中指定位

FD_ISSET 测试指定位是否设置,已设置返回非零,否则返回0

3个fd_set指针可以为NULL,表示不关心相应的文件集。

函数在调用select前,应该先使用FD_ZERO清空每个要使用的fd_set,然后使用FD_SET设置关心的文件描述符。在select返回后使用FD_ISSET检查每个关心的位是否仍旧设置。

select函数在实现并发服务器时是很好用的,这时我们可以每建立一个连接都加入fdset中进行监视,当任意一个连接可读时再去读取数据然后处理,这样可以很简单地实现并发。下面是一个使用select实现的TCP并发服务器的具体实现的例子:

/

//       tcp-server.c

#include #include #include #include #include #include #include #include

#include #include #include

#define MAX_MESSAGE_LEN 63

#define MAX_CONNECTION_NUM 8

typedef struct

{

int sock;

struct sockaddr_in client_addr;

void * prvivate;  //连接私有数据,用来存放连接上下文等

} conn_t;

static conn_t conn[MAX_CONNECTION_NUM] ;

int proc_msg(int conn_hd, char *buf, int len);

//增加一个连接

//初始化conn_t数据结构,并将socket加入监视的文件描述符集

static int add_conn(int sck, struct sockaddr_in *client_addr)

{

int i;

for(i=0; i

{

if(conn[i].sock == -1)

{

#if 0

//为连接私有数据申请空间

conn[i].private = malloc(...);

if(conn[i].private == NULL)

{

ERR_PRINT("Alloc memmory failed.\n");

return -1;

}

memset(conn[i].private, 0,  ...);

#endif

if( (sck+1) > fdmax)

{

fdmax = sck+1;

}

conn[i].sock = sck;

FD_SET(sck, &fdset_rd);

memcpy(&conn[i].client_addr, client_addr, sizeof(struct sockaddr_in));

return i;

}

}

return -1;

}

//删除一个连接

//从监视的文件描述符集中删除socket

//并销毁conn_t

static int del_conn(int hd)

{

int max=0;

int i;

if(hd >= MAX_CONNECTION_NUM)

return -1;

if(conn[hd].sock == -1)

return -1;

FD_CLR(conn[hd].sock, &fdset_rd);

close(conn[hd].sock);

conn[hd].sock = -1;

if(fdmax == conn[hd].sock+1)

{

for(i=0;i

{

if( (conn[i].sock != -1) && ((conn[i].sock+1)>max) )

max = conn[i].sock+1;

}

fdmax = max;

}

#if 0

//释放申请的空间

if(conn[i].private !=  NULL)

{

p = conn[i].private;

conn[i].private = NULL;

free(conn[i].private);

}

#endif

return 0;

}

int main(int argc, char* argv[])

{

struct sockaddr_in sock_addr, client_addr;

int sockfd, clientfd;

int i;

fd_set rdset;

struct timeval tv;

int len,ret;

char buff[MAX_MESSAGE_LEN+1] = {0};

memset(conn, 0, sizeof(conn));

for(i=0; i

{

conn[i].sock = -1;

}

//建立socket

sockfd = socket(AF_INET, SOCK_STREAM, 0);

if(sockfd == -1)

{

ERR_PRINT("Open socket error.\n");

return -1;

}

//设为非阻塞

if( fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1)

{

DBG_PRINT("Set server socket nonblock failed\n");

}

bzero(&sock_addr, sizeof(sock_addr));

sock_addr.sin_family = AF_INET;

sock_addr.sin_port = htons(IOSERVER_TCP_PORT);

sock_addr.sin_addr.s_addr = htons(INADDR_ANY);

bzero(&(sock_addr.sin_zero), 8);

//绑定端口

if(bind(sockfd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) != 0)

{

ERR_PRINT("TCP port binding faild. %s\n", strerror(errno));

return -1;

}

//开始监听

ret = listen(sockfd, MAX_CONNECTION_NUM);

if(ret)

{

ERR_PRINT("TCP server listen faild. %s\n", strerror(errno));

return -1;

}

//初始将服务器监听socket加入监视文件描述符集

FD_ZERO(&fdset_rd);

FD_SET(sockfd, &fdset_rd);

fdmax = sockfd+1;

while(1)

{

//每次调用select前都需要重新设置

memcpy(&rdset, &fdset_rd, sizeof(fd_set));

tv.tv_sec = 0;

tv.tv_usec = 0;

ret = select(fdmax, &rdset, NULL, NULL, &tv);

switch(ret)

{

case -1:

ERR_PRINT("select returned %d\n", ret);

break;

case 0:

break;

default:

//建立一个连接

if(FD_ISSET(sockfd, &rdset))

{

len = sizeof(client_addr);

clientfd = accept(sockfd, (struct sockaddr*)&client_addr, (socklen_t*)&len);

if(clientfd > 0)

{

DBG_PRINT("get one connection\n");

if(add_conn(clientfd, &client_addr) == -1)

{

ERR_PRINT("Add connetion failed,close this connection\n");

close(clientfd);

}

}

}

//扫描所有连接,并从准备好的连接接收数据并处理

for(i=0; i

{

if(conn[i].sock != -1)

{

memset(buff, 0, sizeof(buff));

if(FD_ISSET(conn[i].sock, &rdset))

{

len = recv(conn[i].sock, buff, MAX_MESSAGE_LEN,0);

if(len > 0)

{

porc_msg(&conn[i], buff, len);

}

}

}

}

break;

}

}

close(sockfd);

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值