Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1467016
  • 博文数量: 842
  • 博客积分: 12411
  • 博客等级: 上将
  • 技术积分: 5772
  • 用 户 组: 普通用户
  • 注册时间: 2011-06-14 14:43
文章分类

全部博文(842)

文章存档

2013年(157)

2012年(685)

分类: LINUX

2012-05-17 16:57:43

Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-style-qformat:yes; mso-style-parent:""; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:10.5pt; mso-bidi-font-size:11.0pt; font-family:"Calibri","sans-serif"; mso-ascii-font-family:Calibri; mso-ascii-theme-font:minor-latin; mso-fareast-font-family:宋体; mso-fareast-theme-font:minor-fareast; mso-hansi-font-family:Calibri; mso-hansi-theme-font:minor-latin; mso-bidi-font-family:"Times New Roman"; mso-bidi-theme-font:minor-bidi; mso-font-kerning:1.0pt;} table.MsoTableGrid {mso-style-name:网格型; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-unhide:no; border:solid windowtext 1.0pt; mso-border-alt:solid windowtext .5pt; mso-padding-alt:0cm 5.4pt 0cm 5.4pt; mso-border-insideh:.5pt solid windowtext; mso-border-insidev:.5pt solid windowtext; mso-para-margin:0cm; mso-para-margin-bottom:.0001pt; text-align:justify; text-justify:inter-ideograph; mso-pagination:none; font-size:10.0pt; font-family:"Times New Roman","serif";}

 

getsockopt/setsockopt系统调用】  

   

功能描述:

获取或者设置与某个套接字关联的选项。选项可能存在于多层协议中,它们总会出现在最上面的套接字层。当操作套接字选项时,选项位于的层和选项的名称必须给出。为了操作套接字层的选项,应该将层的值指定为SOL_SOCKET。为了操作其它层的选项,控制选项的合适协议号必须给出。例如,为了表示一个选项由TCP协议解析,层应该设定为协议号TCP

 

套接字选项最常用于在套接字层调整参数,包括调整错误处理、数据缓冲、地址处理、端口处理和收发数据的超时参数等。在这些选项当中,套接字层的SO_RCVTIMEO选项经常用来调整read()recv()recvfrom()函数的超时参数。

 

在默认情况下,read()recv()recvfrom()函数以阻塞方式读取数据,当函数被调用时,将会无限期地等待,直到数据被接收或出现错误。如果在某种实现情况下必须在数据未能及时到达时做出反应,则这种行为是不符合要求的。因此,可以使用SO_RCVTIMEO套接字选项,来设置读取数据的操作在返回调用函数之前的最长等待时间。

 

用法: 

#include

#include

 

int getsockopt(int sock, int level, int optname, void *optval, socklen_t *optlen);

 

int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);

 

参数:  

sock:将要被设置或者获取选项的套接字。

level:选项所在的协议层。可以取三种值:

1)SOL_SOCKET:通用套接字选项.

2)IPPROTO_IP:IP选项.

3)IPPROTO_TCP:TCP选项.

optname:需要访问的选项名,详细描述见表1

optval:对于getsockopt(),指向返回选项值的缓冲。对于setsockopt(),指向包含新选项值的缓冲,是一个指向变量的指针,类型:整型,套接口结构,其他结构类型:linger{}, timeval{ }

optlenoptval 的大小,对于getsockopt(),作为入口参数时,选项值的最大长度。作为出口参数时,选项值的实际长度。对于setsockopt(),现选项的长度。

 

返回说明:  

成功执行时,返回0。失败返回-1errno被设为以下的某个值:  

EBADFsock不是有效的文件描述词

EFAULToptval指向的内存并非有效的进程空间

EINVAL:在调用setsockopt()时,optlen无效

ENOPROTOOPT:指定的协议层不能识别选项

ENOTSOCKsock描述的不是套接字

 

1:参数optname指定控制的方式(选项的名称),详细说明如下表:

 

选项名称

简要说明

数据类型

SOL_SOCKET

SO_BROADCAST

允许发送广播数据

int

SO_DEBUG

允许调试

int

SO_DONTROUTE

不查找路由

int

SO_ERROR

获得套接字错误

int

SO_KEEPALIVE

保持连接

int

SO_LINGER

延迟关闭连接

struct linger

SO_OOBINLINE

带外数据放入正常数据流

int

SO_RCVBUF

接收缓冲区大小

int

SO_SNDBUF

发送缓冲区大小

int

SO_RCVLOWAT

接收缓冲区下限

int

SO_SNDLOWAT

发送缓冲区下限

int

SO_RCVTIMEO

接收超时

struct timeval

SO_SNDTIMEO

发送超时

struct timeval

SO_REUSERADDR

允许重用本地地址和端口

int

SO_TYPE

获得套接字类型

int

SO_BSDCOMPAT

BSD系统兼容

int

IPPROTO_IP

IP_HDRINCL

在数据包中包含IP首部

int

IP_OPTINOS

IP首部选项

int

IP_TOS

服务类型

 

IP_TTL

生存时间

int

IPPRO_TCP

TCP_MAXSEG

TCP最大数据段的大小

int

TCP_NODELAY

不使用Nagle算法

int

 

的例子

下面的例子代码为一个套接口改变了发送以及接收缓冲区的尺寸。在设置完这些选项以后,程序会得到并报告实际的缓冲区尺寸。

#include

#include

#include

#include

#include

#include

#include

#include

 

int main(int argc,char **argv)

{

    int z;

    int s=-1;            /* Socket */

    int sndbuf=0;        /* Send buffer size */

    int rcvbuf=0;        /* Receive buffer size */

    socklen_t optlen;        /* Option length */

 

    /*

     * Create a TCP/IP socket to use:

     */

    s = socket(PF_INET,SOCK_STREAM,0);

    if(s==-1)

    {

        printf("socket error\n");

        return -1;

    }

 

    /*

     * set the SO_SNDBUF size :

     */

    sndbuf = 5000;    /* Send buffer size */

    z = setsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf));

    if(z)

    {

        printf("setsockopt error\n");

        return -1;

    }

 

    /*

     * Set the SO_RCVBUF size:

     */

    rcvbuf = 8192;    /* Receive buffer size */

    z = setsockopt(s,SOL_SOCKET,SO_RCVBUF,&rcvbuf,sizeof(rcvbuf));

    if(z)

    {

        printf("setsockopt error\n");

        return -1;

    }

 

    /*

     * As a check on the above ....

     * Get socket option SO_SNDBUF:

     */

    optlen = sizeof(sndbuf);

    z = getsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndbuf,&optlen);

    if(z)

    {

        printf("getsockopt error\n");

        return -1;

    }

 

    assert(optlen == sizeof(sndbuf));

 

    /*

     * Get socket

     option SO_RCVBUF:

     */

    optlen = sizeof(rcvbuf);

    z = getsockopt(s,SOL_SOCKET,SO_RCVBUF,&rcvbuf,&optlen);

    if(z)

    {

        printf("getsockopt error\n");

        return -1;

    }

    assert(optlen == sizeof(rcvbuf));

 

    /*

     * Report the buffer sizes:

     */

    printf("Socket s: %d\n",s);

    printf(" Send buf: %d bytes\n",sndbuf);

    printf(" Recv buf: %d bytes\n",rcvbuf);

 

    close(s);

    return 0;

}

 

 

的例子

1、设置缓冲区大小的例子:

#include

#include

#include

#include

#include

#include

#include

#include

 

int main(int argc,char **argv)

{

    int z;

    int s=-1;            /* Socket */

    int sndbuf=0;        /* Send buffer size */

    int rcvbuf=0;        /* Receive buffer size */

    socklen_t optlen;        /* Option length */

 

    /*

     * Create a TCP/IP socket to use:

     */

    s = socket(PF_INET,SOCK_STREAM,0);

    if(s==-1)

    {

        printf("socket error\n");

        return -1;

    }

 

    /*

     * set the SO_SNDBUF size :

     */

    sndbuf = 5;    /* Send buffer size */

    z = setsockopt(s,SOL_SOCKET,SO_SNDBUF,&sndbuf,sizeof(sndbuf));

    if(z)

    {  

        printf("setsockopt error\n");

        return -1;

}

……

}

 

2、设置接收超时的例子:

#include

#include

#include

#include

#include

#define MYPORT 6666

 

main()

{

        int sock,new_fd;

        int sin_size;

        struct sockaddr_in my_addr;

        struct sockaddr_in their_addr;

        struct timeval recv_timeout;

        int len,recv_size;

        char buf[255];

        sock=socket(AF_INET,SOCK_STREAM,0);

        if(sock<0)

        {

                printf("socket is error");

                return -1;

        }

        my_addr.sin_family=AF_INET;/*hostbyteorder*/

        my_addr.sin_port=htons(MYPORT);/*short,network byte order*/

        my_addr.sin_addr.s_addr=INADDR_ANY;

        bzero(&(my_addr.sin_zero),8);/*zero the rest of the struct*/

        if(bind(sock,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<0)

        {

                close(sock);

                printf("bind error");

                return -1;

        }

        if(listen(sock,5)<0)

        {

                close(sock);

                printf("listen error");

                return -1;

        }

        printf("listen right\n");

        sin_size=sizeof(struct sockaddr_in);

        new_fd=accept(sock,&their_addr,&sin_size);

        if(new_fd<0)

        {

                close(sock);

                printf("accept error");

                return -1;

        }

        printf("their_ip is %s\n",inet_ntoa(their_addr.sin_addr));

        printf("their_port is %d\n",their_addr.sin_port);

        //setsockopt

        recv_timeout.tv_sec=10;

        recv_timeout.tv_usec=0;

        int z=setsockopt(new_fd,SOL_SOCKET,SO_RCVTIMEO,&recv_timeout,sizeof(recv_timeout));

        if(z)

        {

                close(sock);

                printf("setsockopt error\n");

                return -1;

        }

        buf[0]="\0";

        recv_size=recv(new_fd,buf,255,0);

        if(recv_size<0)

        {

                close(sock);

                printf("recv error\n");

                return -1;

        }

……

}

 

  Timeout参数是一个指向struct timeval类型的指针,它可以使select()在等待timeout长时间后没有文件描述符准备好即返回。struct timeval数据结构为:

  struct timeval {

   int tv_sec; /* seconds */

   int tv_usec; /* microseconds */

};

 

在此例中,使用socket()bind()函数创建了一个套接字,并绑定端口,然后使用setsockopt()函数设置了这个套接字的接收超时参数,超时参数存放在一个timeval结构中,具体分析如下:

1)传给函数的第1个参数为需要设置选项的套接字描述符。

2)第2个参数为所设置选项的协议层次,在本例中使用了整型常量SOL_SOCKET,它表示在套接字层设置选项。

3)第3个参数为套接字选项标志,在本例中为整型常量SO_RCVTIMEO

4 函数的第4个和第5个参数是根据第2个与第3个参数即套接字选项的协议层次与选项值确定的。当使用SOL_SOCKETSO_RCVTIMEO选项时,传给第4个参数的是一个指向timeval结构的指针,传给第5个参数的是timeval结构的长度(以字节为单位)timeval结构中的tv_secttv_usec成员的值表示从第1个参数处传入的套接字描述符读取数据的超时参数。

 

的其它用法:

1.closesocket()(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket

BOOL bReuseaddr=TRUE;

setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));

2. 如果要已经处于连接状态的socket在调用closesocket()后强制关闭,不经历TIME_WAIT的过程:

BOOL bDontLinger = FALSE;

setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));

3.send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限:

struct timeval tv;

tv.sec=1000;//1

//发送时限

setsockopt(socket,SOL_SOCKET,SO_SNDTIMEO,(struct timeval *)&tv,sizeof(tv));

//接收时限

setsockopt(socket,SOL_SOCKET,SO_RCVTIMEO,( struct timeval *)&tv,sizeof(tv));

4.send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节

(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中发送数据

和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发:

// 接收缓冲区

int nRecvBuf=32*1024;//设置为32K

setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));

//发送缓冲区

int nSendBuf=32*1024;//设置为32K

setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));

5. 如果在发送数据时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响程序的性能:

int nZero=0;

setsockopt(socket,SOL_SOCKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));

6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区)

int nZero=0;

setsockopt(socket,SOL_SOCKET,SO_RCVBUF,(char *)&nZero,sizeof(int));

7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:

BOOL bBroadcast=TRUE;

setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));

8.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(),以前我们一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了,如何设置让程序满足具体应用的要求(即让没发完的数据发送出去后在关闭socket)

struct linger {

u_short l_onoff;

u_short l_linger;

};

struct linger m_sLinger;

m_sLinger.l_onoff=1;//(closesocket()调用,但是还有数据没发送完毕的时候容许逗留)

// 如果m_sLinger.l_onoff=0;则功能和2.)作用相同;

m_sLinger.l_linger=5;//(容许逗留的时间为5)

setsockopt(s,SOL_SOCKET,SO_LINGER,( struct linger *)&m_sLinger,sizeof(m_sLinger));

  阻塞函数在完成其指定的任务以前不允许程序调用另一个函数。例如,程序执行一个读数据的函数调用时,在此函数完成读操作以前将不会执行下一程序语句。当服务器运行到accept语句时,而没有客户连接服务请求到来,服务器就会停止在accept语句上等待连接服务请求的到来。这种情况称为阻塞(blocking)。而非阻塞操作则可以立即完成。比如,如果你希望服务器仅仅注意检查是否有客户在等待连接,有就接受连接,否则就继续做其他事情,则可以通过将Socket设置为非阻塞方式来实现。非阻塞socket在没有客户在等待时就使accept调用立即返回。

  #include

  #include

  ……

sockfd = socket(AF_INET,SOCK_STREAM,0);

fcntl(sockfd,F_SETFL,O_NONBLOCK)

……

  通过设置socket为非阻塞方式,可以实现"轮询"若干Socket。当企图从一个没有数据等待处理的非阻塞Socket读入数据时,函数将立即返回,返回值为-1,并置errno值为EWOULDBLOCK。但是这种"轮询"会使CPU处于忙等待方式,从而降低性能,浪费系统资源。而调用select()会有效地解决这个问题,它允许你把进程本身挂起来,而同时使系统内核监听所要求的一组文件描述符的任何活动,只要确认在任何被监控的文件描述符上出现活动,select()调用将返回指示该文件描述符已准备好的信息,从而实现了为进程选出随机的变化,而不必由进程本身对输入进行测试而浪费CPU开销Select函数原型为:

int select(int numfds,fd_set *readfds,fd_set *writefds, fd_set *exceptfds,struct timeval *timeout);

  其中readfdswritefdsexceptfds分别是被select()监视的读、写和异常处理的文件描述符集合。如果你希望确定是否可以从标准输入和某个socket描述符读取数据,你只需要将标准输入的文件描述符0和相应的sockfd加入到readfds集合中;numfds的值是需要检查的号码最高的文件描述符加1,这个例子中numfds的值应为sockfd+1select返回时,readfds将被修改,指示某个文件描述符已经准备被读取,你可以通过FD_ISSSET()来测试。为了实现fd_set中对应的文件描述符的设置、复位和测试,它提供了一组宏:

  FD_ZERO(fd_set *set)----清除一个文件描述符集;

  FD_SET(int fd,fd_set *set)----将一个文件描述符加入文件描述符集中;

  FD_CLR(int fd,fd_set *set)----将一个文件描述符从文件描述符集中清除;

  FD_ISSET(int fd,fd_set *set)----试判断是否文件描述符被置位。

 

首先:

int sock;

sock= socket(AF_INET,SOCK_STREAM,0);

 

struct sockaddr_in addr;       //告诉sock 应该在什么地方listen

memset(&addr,0,sizeof(addr));

addr.sin_family=AF_INET;

addr.sin_port=htons(11111);    //端口

addr.sin_addr.s_addr=htonl(INADDR_ANY);           //在本机的所有ip上开始监听

 

bind (sock,(struct sockaddr *)&addr,sizeof(addr));//bind....

 

listen(sock,5);                               //最大5个队列

 

int socka;                         //这个用来接受一个连接

fd_set rfd;                        // 描述符集 这个将用来测试有没有一个可用的连接

struct timeval timeout;

 

FD_ZERO(&rfd);                     //总是这样先清空一个描述符集

 

timeout.tv_sec=60;                // select函数用到这个

timeout.tv_usec=0;

 

u_long ul=1;

 

ioctlsocket(sock,FIONBIO,&ul);    //用非阻塞的连接

 

//现在开始用select

FD_SET(sock,&rfd);    //sock放入要测试的描述符集,就是说把sock放入了rfd里面,这样下一步调用selectrfd进行测试的时候就会测试sock(因为我们将sock放入的rdf) 一个描述符集可以包含多个被测试的描述符

if(select(sock+1,&rfd,0,0, &timeout)==0)    // select的第一个参数是可以忽略的(这样写是为了保持和linux下一致) 第二个参数放入需要测试的读描述符集(也就是说如果这里面有一个描述符可以读取了,select就返回) 第三个放入需要测试的写描述符集,第四个放入"可执行描述符集",第五个参数是超时时间(如果过了这个超时时间依然没有描述符准备好,select也返回.(如果为NULL,那就一直等到一个描述符集变成准备好的状态)

{ //这个大括号接上面的,返回0那么就超过了timeout预定的时间

 

//处理....

 

}

 

if(FD_ISSET(sock,&rfd))

{      //有一个描述符准备好了

 

socka=accept(sock,0,0);       //好了 接受它吧

 

//你还要判断一下socka是不是有效的socket才行....

 

----------------------------------------------------------------------------------------------------------------------

 

一般的情况下

 

假设你要判断两个socket 是否可读可写,那就这样:

 

假设 socka sockb 是两个socket 他们已经被连接上,并且能够收发数据

 

fd_set rfd,wfd;//一个用来测试读,一个用来测试写

 

FD_ZERO(&rfd);

 

FD_ZERO(&wfd);

 

FD_SET(socka,&rfd);//socka放入读描述符集

 

FD_SET(sockb,&rfd);//sockb放入读描述符集

 

FD_SET(socka,&wfd);socka放入写描述符集

 

FD_SET(sockb,&wfd);sockb放入写描述符集

 

if(SOCKET_ERROR!=select(0,&rfd,&wfd,0,0))      //测试这两个描述符集,永不超时,其中rfd只用来测试读,wfd只用来测试写

 

{      //没有错误

 

if(FD_ISSET(socka,&rfd))    //socka可读

 

{...}

 

if(FD_ISSET(sockb,&rfd)   //sockb可读

 

{...}

 

if(FD_ISSET(socka,&wfd) //socka 可写

 

{...}

 

if(FD_ISSET(sockb,&wfd) //sockb可写

 

{...}

 

}

 

当一个用户30秒内没有任何活动,就说明客户端出现了什么问题。这个时候我们就应该在服务器上主动断开连接,实现这个的机制就是超时机制,

  

    服务器客户端间的超时机制需要满足的技术特性:

    一,需要一个定时器功能。能够在一定时间后正确的发出通知。

    二,定时器时间可以在程序运行中动态修改。

    三,要有退出机制。当程序接受到事件后能相应作出处理

    四,能够安全的在多线程环境下使用

 

 

       关于超时机制的实现。考虑实现方法如下

   方案一:使用select自身提供的超时机制。

              优点:系统自带,省时省力。

    缺点:当使用select自身提供的超时机制的时候,发现客户端无法正常连接服务器端。

              结果:放弃

阅读(744) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~