Socket通信中的实用属性

来源:互联网 发布:软件著作权保护中心 编辑:程序博客网 时间:2024/06/06 04:41

以前实用套接字只是最简单的使用,没有涉及到socket属性的设置,其实socket的属性设置还是很实用的,本文主要解析一下keep-alive和time-out这两个属性。

一、Keep-Alive

tcp建立连接需要3次握手,而拆链需要4次握手,在很多实际使用场合,socket的拆链并非能够正常完成,比如通信的一端突然掉电或者网络中断,而另一端可能对此并无感知,这就造成了有假链接的情况,而socket的keep-alive属性,就可以自己探测链接的有效与否,如果对端没有对KeepAlive包进行正常的响应,则会则发送RST包关闭连接。

KeepAlive参数

(1)SO_KEEPALIVE选项,将这个选项设置为1,代表打开KeepAlive机制
(2)TCP_KEEPIDLE选项,代表如果TCP连接上有设置的时间没有任何数据包传输,则启动保活机制,发送TCP Keep-alive机制,默认为2小时
(3)TCP_KEEPINTVL选项,代表如果启动保活机制,则每隔设置时间发送一个Keep-alive包。默认为75秒
(4)TCP_KEEPCNT选项,代表如果对端对设置次数的Keep-alive数据包都没有正常响应,则判断对端已经崩溃,默认为9

示例代码

int keepalive = 1; // 开启keepalive属性int keepidle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测int keepinterval = 5; // 探测时发包的时间间隔为5 秒int keepcount = 3; // 探测尝试的次数。如果第1次探测包就收到响应了,则后2次的不再发,如果3次探测包都没有响应,则会rst关闭连接
setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepalive , sizeof(keepalive ));setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle ));setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval ));setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount ));

需要头文件

#include <netinet/tcp.h>

特别注意:如果发送方发送的数据包没有收到接收方回复的ACK数据包,则TCP Keep-alive机制就不会被启动,而TCP会启动超时重传机制,这样就使得TCP Keep-alive机制在未收到ACK包时失效,为解决这一问题,Linux Kernel 2.6.37中增加了一个叫做TCP_USER_TIMEOUT的socket选项,TCP_USER_TIMEOUT选项是TCP层的socket选项,选项接受unsigned int类型的值,值为数据包被发送后未接收到ACK确认的最大时长,以毫秒为单位,例如设置为10000时,代表如果发送出去的数据包在十秒内未收到ACK确认,则下一次调用send或者recv,则函数会返回-1,errno设置为ETIMEOUT,代表connection timeout

示例代码

unsigned int timeout = 1000;
setsockopt(fd, IPPROTO_TCP, TCP_USER_TIMEOUT, &timeout, sizeof(timeout));


除了设置TCP_USER_TIMEOUT可解决问题外,使用发送超时属性也可以达到同样的目的。

二、Time-Out

1、说明

(1)读超时:由于read操作会一直阻塞,设置了读超时后,如果到了超时时间仍没有数据,read会抛出一个SocketTimeoutException,并会返回读错误,程序需要捕获这个异常,但是当前的socket连接仍然是有效的;

(2)写超时:socket的写超时是基于TCP的超时重传,超时重传是TCP保证数据可靠性传输的一个重要机制,其原理是在发送一个数据报文后就开启一个计时器,在一定时间内如果没有得到发送报文的确认ACK,那么就重新发送报文,如果重新发送多次之后,仍没有确认报文,就发送一个复位报文RST,然后关闭TCP连接


2、示例代码

struct timeval timeout={3,0};//3sint ret=setsockopt(sock_fd,SOL_SOCKET,SO_SNDTIMEO,(const char*)&timeout,sizeof(timeout));int ret=setsockopt(sock_fd,SOL_SOCKET,SO_RCVTIMEO,(const char*)&timeout,sizeof(timeout));//ret==0 则为成功,-1为失败int recvd=recv(sock_fd,buf,1024,0);if(recvd==-1&&errno==EAGAIN){    printf("timeout\n");}
int sendflag=send(sock_fd,buf,strlen(buf));
if(sendflag == -1&&errno=EAGAIN)
{
    printf("timeout\n");
}










0 0