网络超时检测-11.9

应用场景
  • 在网络通信中,很多操作会使得进程阻塞:
    • TCP套接字中的recv/accept
    • UDP套接字中的recvfrom
  • 超时检测的必要性
    • 避免进程在没有数据时无限制地阻塞
    • 实现某些特定协议要求,比如某些设备规定,发送请求数据后,如果多长时间后没有收到来自设备的回复,需要做出一些特殊处理
  1. 自带超时参数的函数

如使用select/poll/epoll函数最后一个参数可以设置超时。

1)select设置超时

struct timeval tm = {2, 0};//设置2s打算阻塞
sret = select(maxfd + 1, &tempfds, NULL, NULL, &tm);
第五个参数:
 struct timeval {
     long    tv_sec;         /*秒*/
     long    tv_usec;        /*微秒*/
 };

2.poll

 int poll(struct pollfd *fds, nfds_t nfds, int timeout);
   第三个参数:时间单位是毫秒 -1阻塞, 2000=2s
   ret = poll(event, num, 2000);//超时检测时间为2s

3.epoll 设置的是epoll_wait


 int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);
  第四个参数:时间单位是毫秒 -1阻塞, 2000=2s
  ret = epoll_wait(epfd, events, 20, 2000);
设置超时后的返回值都为:<0  error
                        =0   超时
                        >0   正确 

2.利用setsockopt属性设置

Linux中socket属性

选项名称

说明

数据类型

 ====  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_REUSEADDR

允许重用本地地址和端口

int

SO_TYPE

获得套接字类型

int 

SO_BSDCOMPAT

与BSD系统兼容

                   int

==== IPPROTO_IP  网络层 ====

  IP_HDRINCL

         在数据包中包含IP首部

 int

IP_OPTINOS

 IP首部选项

 int

IP_TOS

服务类型

int

IP_TTL

生存时间

int

IP_ADD_MEMBERSHIP

将指定的IP加入多播组

  struct ip_mreq

==== IPPRO_TCP 传输层 ====

TCP_MAXSEG

TCP最大数据段的大小

int 

TCP_NODELAY

不使用Nagle算法

 int 

网络超时检测-11.9_第1张图片

功能:设置/获取网络属性;
原型:
       #include           /* See NOTES */
       #include 

       int getsockopt(int sockfd, int level, int optname,
 void *optval, socklen_t *optlen);
       int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
参数:
    int sockfd:指定要设置/获取哪个套接字的属性;
    int level:指定要控制的协议层次;
        SOL_SOCKET:应用层 通用套接字选项;  man 7 socket
        IPPROTO_TCP:TCP选项               man 7 TCP
        IPPROTO_UDP:UDP选项               man 7 UDP
        IPPROTO_IP:IP选项;                man 7 IP
    int optname:指定要控制的内容,指定控制方式;
      --- SOL_SOCKET: man 7 socket -----
        SO_REUSEADDR:允许端口快速重用   optval: int*
        SO_BROADCAST:允许广播          optval: int*   
        SO_RCVBUF/SO_SNDBUF:接收缓冲区 发送缓冲区大小
        SO_RCVTIMEO/SO_SNDTIMEO:接收超时时间,发送超时时间
    void *optval:根据optname不同,该类型不同;(数据类型
    socklen_t optlen/socklen_t *optlen:真实的optval指针指向的内存空间的大小;
返回值:
    成功,返回0
    失败,返回-1,更新errno;

网络超时检测-11.9_第2张图片

3、利用alarm定时器设置

#include 
unsigned int alarm(unsigned int seconds);
功能:在进程中设置一个定时器
参数:seconds:定时时间,单位为秒
返回值:如果调用此alarm()前,进程中已经设置了闹钟时间,则
	返回上一个闹钟时间的剩余时间,否则返回0。

alarm(5) 闹钟   定时器
//5秒之后会,会有一个信号产生(SIGALRM)
int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact);
 功能:对接收到的指定信号处理
	signum  信号		 
struct sigaction 
{
    void     (*sa_handler)(int); //信号处理函数
    void     (*sa_sigaction)(int, siginfo_t *, void *);  //信号处理函数
    sigset_t   sa_mask;
    int        sa_flags;      //信号属性; SA_RESTART自重启属性
#define SA_RESTART  0x10000000
    void     (*sa_restorer)(void);
           };     
    //设置信号属性
    struct sigaction act;
    sigaction(SIGALRM,NULL,&act);//获取原属性
    act.sa_handler=handler;//修改属性
    sigaction(SIGALRM,&act,NULL);//将修改的属性设置回去
   
注:在recv前调用alarm函数
   alarm的 SIGALRM信号产生后会打断(终端)下面的系统调用recv;
   打断后相当于recv错误返回。
   信号改变行为后,当前进程所有行为都被改变,若想要再次改变回原行为,需要再次执行sigaction.

注意:一个进程只能有一个闹钟时间。如果在调用alarm时已设置过闹钟时间,则之前的闹钟时间被新值所代替,时间到了会给进程发送一个SIGALRM信号,这个信号也有结束进程的功能

网络超时检测-11.9_第3张图片

#include
#include
#include

void handler(int sig)
{
    printf("timeout................\n");
}

int main(int argc, char const *argv[])
{   
    struct sigaction act;
    sigaction(SIGALRM,NULL,&act);//获取原属性
    act.sa_handler=handler;//修改属性
    sigaction(SIGALRM,&act,NULL);//将修改的属性设置回去

    // sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
    char buf[64]={};
    while (1)
    {   
        alarm(2);
        printf("hello\n");
        fgets(buf,sizeof(buf),stdin);
        printf("fgets阻塞接触\n");
        int ret=alarm(2);
        printf("%d\n",ret);
        read(0,buf,sizeof(buf));
        printf("read阻塞接触\n");
        printf("buf:%s\n",buf);
    }
    
    return 0;
}

网络超时检测-11.9_第4张图片

你可能感兴趣的:(网络编程,网络,linux,c语言,IO,tcp/ip,udp,嵌入式实时数据库)