您的位置:首页 > 编程语言

socket编程—select方法使用

2016-05-31 20:11 573 查看
0.背景

最近要写一个RPC库,即在客户端向服务端发送请求,服务器计算并返回结果,要求实现服务端能同时接收多个客户端请求但是不能使用线程库,根据提示我知道了可以使用select函数来完成非阻塞方式工作的程序,于是我就开始了select方法的学习。

1.概念

Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序(比如我),他们只是习惯写诸如connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。

可是使用Select就可以完成非阻塞(所谓非阻塞方式non-block,就是进程或线程执行此函数时不必非要等待事件的发生,一旦执行肯定返回,以返回值的不同来反映函数的执行情况,如果事件发生则与阻塞方式相同,若事件没有发生则返回一个代码来告知事件未发生,而进程或线程继续执行,所以效率较高)方式工作的程序,它能够监视我们需要监视的文件描述符的变化情况——读写或是异常。

2.编程

我第一步是准备先熟悉select的简单使用,所以我在我的CentOS服务器上先编辑好如下代码:

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/select.h>
#include<sys/un.h>
#include<string.h>
#include<arpa/inet.h>
int main()
{
int server_fd;
int client_fd;
struct sockaddr_in myaddr;
struct sockaddr_in clientaddr;
int clientaddr_len=sizeof(clientaddr);
int maxfdp;
fd_set fds;
struct timeval timeout={3,0};
myaddr.sin_family=AF_INET;
myaddr.sin_addr.s_addr=htonl(INADDR_ANY);
myaddr.sin_port=htons(4600);
char buf[16];
//socket
server_fd=socket(AF_INET,SOCK_STREAM,0);
if(server_fd==-1)
{
perror("socket error");
exit(1);
}
//bind
if(bind(server_fd,(struct sockaddr *)&myaddr,sizeof(myaddr))==-1)
{
perror("bind error");
exit(1);
}
//listen
if(listen(server_fd,20)==-1)
{
perror("listen error");
exit(1);
}
printf("listening~\n");
client_fd=accept(server_fd,(struct sockaddr *)&clientaddr,&clientaddr_len);
while(1)
{
FD_ZERO(&fds);
FD_SET(server_fd,&fds);
FD_SET(client_fd,&fds);
maxfdp=server_fd>client_fd?server_fd+1:client_fd+1;
switch(select(maxfdp,&fds,&fds,NULL,&timeout))
{
case -1:
exit(-1);
break;
case 0:
sleep(2);
printf("time out~ \n");
break;
default:
if(FD_ISSET(client_fd,&fds))
{
sleep(2);
recv(client_fd,buf,100,0);
printf("receive from client %s\n",buf);
}
}
}

close(server_fd);
}


客户端程序比较简单,所以我就不上代码了,这里重点想讨论一下我在完成这个程序中遇到的问题。
3.问题

在完成这个程序之前我看过许多解释select方法的博客,有Windows环境下的,有linux环境下的,但是都没有能正确运行的代码,我想大家也是为了捍卫自己的版权,所以贴的都是伪码,不过也多亏了大家的共享精神我最终才能解决这些小问题。

1)在使用fd_set这个结构体声明变量的时候,我本来写的是:

struct fd_set fds

这也是很多博主的写法,但是我这样写在CentOS下用gcc编译会报错,搜索那个错误提示也没有找到相关联的答案,所以我就去掉了前面的struct关键字,然后一套编译下来,居然连一个警告都没有就通过了。

2)原本我根据别人的代码模仿着写的时候在监听之后并没有写accept这个过程,因为别人都没写

,然后编译通过没问题,运行起来的时候就一直处于监听状态了,我在客户端反复执行连接都没有任何反应,所以,别人的方法终究只能作为参考啊,最后还是需要自己动手动脑。

3)我在网上又搜索了一阵子之后,意识到了上面那个问题,所以同样依葫芦画瓢将accept方法写在了死循环里面,但是在FD_ISSET方法判断socket描述符是否可读写时,我又犯了一个错,因为我判断的是server_fd,这个描述符无论有没有连接请求状态都是不变的,所以在调试程序的时候同样无法连通。

最后意识到上述问题之后,我想到,既然select要检测的是变幻的socket描述符,那结合之前accept方法返回的客户端描述符,怎么做方法就很明显了,即用select方法来检测accept返回的文件描述符,也就是有客户端连接请求就可以在select的switch语句中进行发送和接收了,如程序所示。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  socket select 服务器