您的位置:首页 > 理论基础 > 计算机网络

linux—TCP_server端编写之利用select()函数编写可多用户同时访问

2017-07-31 14:13 323 查看

1. 回顾多进程、多线程编写的server

之前我们编写了多进程、多线程的tcp_server,但我们发现虽然多线程和多进程的编写简单,但太占资源了,当客户端连接稍微多一点,服务器就有可能奔溃。所以我们今天要利用一种io模式:i/o复用(多路转接)

2. 什么是多路转接

我们在IO数据时,通常时间是在等,我们普通read()函数,write()函数等一次只能等一个文件描述符,这样的效率太低了。所以我们就有了多路转接的IO方式,这种IO方式是,一次等待多个文件描述符,只要有一个就绪,则返回。

3. 函数select

int select(int nfds, fd_set *readfds, fd_set *wirtefds,\
fd_set     *exceptfds, struct timeval *timeout);


返回值:有多少个描述符就绪

nfds: 需要监视的文件描述符最大值+1;

rdset, wrset, exset分别对应于需要检测的可读⽂文件描述符的集合,可写⽂文件描述符的集 合及异常⽂文件描述符的集合。

struct timeval结构⽤用于描述 —— 一段时间长度,如果在这个时间内,需要监视的描述符没有事件发生则函数返回,返回值为0。

4. 代码:

因为socket的创建,绑定,设置监听之前我已经写过了所以这里就不写了,有需要的可以点击链接参考参考:

LINUX–TCP_server端的编写 创建监听套接字 listen_socket

int main(int argc, char *argv[])
{
if(argc != 3){
usarg(argv[0]);
return 0;
} //提供手册,从命令行输入本地ip和端口号
int listen_sock = startup(argv[1], atoi(argv[2]));
//listen_socke
int fds[MAX] = {0}; // select的参数都是输入输出型的,需要自己记录文件描述符
int i = 0;
fds[0] = listen_sock;//先将监听套接字添加到数组中
for(i=1; i<MAX; i++)
{
fds[i] = -1;
}//清空数组
fd_set readfds;
while(1)
{
FD_ZERO(&readfds);
int count = listen_sock;
for(i=0; i<MAX; i++)
{
if(fds[i] != -1)
{
count = ((count>fds[i])?count:fds[i]);
FD_SET(fds[i], &readfds);
}//添加数组的文件描述到参数readfds中,我们现在只关心读事件。
}
int ret = select(count+1, &readfds, NULL, NULL, NULL);
if(ret == 0)//我们设置的是阻塞式,所以基本不会==0
{printf("timeout!!!\n");
}
else if(ret < 0)//出错
{
perror("select");
return 5;
}
else if(ret > 0)//>0时,一定有文件描述符的那个事件就绪
{
for(i=0; i<MAX; i++)//遍历判断是那个文件描述符的那个时间
{
if(fds[i] == -1)
continue;
if(i == 0 && FD_ISSET(listen_sock, &readfds))
{//listen_socket的读事件就绪
struct sockaddr_in client;
int len = sizeof(client);
int new_sock = accept(listen_sock, (struct sockaddr *)&client, &len);//读
printf("client : [%s][%d],socket: [%d]\n"           ,inet_ntoa(client.sin_addr),ntohs(client.sin_port),new_sock);
for(i=4; i<MAX; i++ )
{
if(fds[i] == -1)
break;
}//找数组中可以添加的位置,添加新的sock
if(i == MAX)
close(new_sock);
else
fds[i] = new_sock;
}
else if(FD_ISSET(fds[i], &readfds))
{
char buf[1024];
ssize_t s= read(fds[i], buf, sizeof(buf)-1);
if(s < 0)
{
perror("read");
}
else if(s == 0)
{
printf("client[%d] quit!\n", fds[i]);
close(fds[i]);
fds[i] = -1;
}
else
{
printf("client[%d]: %s\n",fds[i],buf);
}
}
}
}
}
return 0;
}


5. select函数的优点与缺点

优点:占用资源少,当用户多时性能较好

缺点:1、select可监听的文件描述符有上限制;

2、因为select参数是输入输出型的,所以每次重新设置select时,都需遍历式设置,对性能有一定的影响

3、用户增多时,多次重复遍历和频繁内核与进程数据拷贝(多次的返回)

6.解决的办法

多路转接的函数还有poll. epoll ,

epoll可以完全解决这个问题
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  函数 select server tcp linux
相关文章推荐