socket编程—select方法使用

原创 2016年05月31日 20:11:18

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()的妙用

http://fanqiang.chinaunix.net/a4/b7/20010913/0900001283.html【 原文由 cpu 所发表 】  用过 WinSock API 网友们知道:Wi...
  • cation
  • cation
  • 2007年05月03日 23:18
  • 18291

linux非阻塞式socket编程之select()用法

Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 connect、accept、recv或recvfrom这样的阻塞程序...
  • w397090770
  • w397090770
  • 2011年09月27日 15:10
  • 44574

socket select函数的详细讲解

原型int select(int nfds,fd_set* readfds,fd_set* writefds,fd_set* exceptfds,const struct timeval* timeo...
  • gooer
  • gooer
  • 2009年03月02日 23:41
  • 95509

C++服务器(六):socket 异步模型与select 的实现

之前在另一篇博客上提到一些关于socket 的异步模型的资料,其中有一篇博客写得很详细,在此附上链接: socket阻塞与非阻塞,同步与异步、I/O模型[1]这篇博客已经讲得很好了。但是我还是觉得...
  • u014613043
  • u014613043
  • 2016年04月07日 20:12
  • 3374

socket 多个客户端下select的写法

看windows网络编程第二版的时候,看到select模型,书上只有一个客户端连接的例子,如果是多个客户端的话,服务端就需要适当的改一下,下面是我自己改的步骤,不过还有很多细节没有处理,将就着看看; ...
  • u010521560
  • u010521560
  • 2017年06月25日 21:45
  • 332

windows 下 select 配合socket实现多路复用

这是在做C语言聊天平台解决服务器和多客户端网络连接的主要本分代码和思路...
  • qq_29690869
  • qq_29690869
  • 2017年03月14日 20:52
  • 477

linux下socket编程 select实现非阻塞模式多台客户端与服务器通信

select函数原型如下: int select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct t...
  • tingyuanss
  • tingyuanss
  • 2015年04月22日 08:24
  • 7752

socket通信之五:select多路复用的客户/服务器模型

前面一篇介绍了服务器端使用多线程的方式来处理多个客户端的请求的,但是当客户端数量增多时线程数量会急剧增加,导致消耗大量的资源。 于是就引出了服务器端的一种新的模型。 1. ...
  • u012501459
  • u012501459
  • 2015年09月02日 14:52
  • 3276

socket之select模式

windows提供了选择(select)、异步选择(WSAAsyncSelect)、事件选择(WSAEventSelect)、重叠I /O(overlapped I/O)和完成端口(completio...
  • ganpengjin1
  • ganpengjin1
  • 2015年03月27日 16:45
  • 2826

Socket编程知识必学/SELECT 编程

Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 connect、accept、recv或recvfrom这样的阻塞程序...
  • maopig
  • maopig
  • 2012年02月19日 16:56
  • 7656
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:socket编程—select方法使用
举报原因:
原因补充:

(最多只允许输入30个字)