#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>//使用signal函数
#include <sys/wait.h>//使用wait函数
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE);\
}while(0)
/*
函数只进行读超时检测,不进行读操作
fd文件描述符;wait_seconds为等待超时秒数,为0表示不检测超时
成功(未超时)返回0,失败返回-1,超时返回-1且errno=ETIMEDOUT
*/
int read_timeout(int fd, unsigned int wait_seconds)
{
int ret =0;
if(wait_seconds > 0)
{
fd_set read_fdset;//设置可读套接口集合
struct timeval timeout;//超时时间结点
FD_ZERO(&read_fdset);
FD_SET(fd, &read_fdset);
timeout.tv_sec = wait_seconds;//只关心秒
timeout.tv_usec =0;//不关心微秒
do
{
ret = select(fd+1,&read_fdset, NULL, NULL, &timeout);
}while(ret<0 &&errno == EINTR);//信号中断引起的失败,需要重启检测
if(ret==0)//没有检测到事件发生,即时间超时
{
ret=-1;
errno = ETIMEDOUT;
}
else if(ret == 1)
ret =0;
}
return ret;
}
/*
函数只进行读超时检测,不含写操作
fd为文件描述符
fd文件描述符;wait_seconds为等待超时秒数,为0表示不检测超时
成功(未超时)返回0,失败返回-1,超时返回-1且errno=ETIMEDOUT
*/
int write_timeout(int fd, unsigned int wait_seconds)
{
int ret =0;
if(wait_seconds > 0)
{
fd_set write_fdset;//设置可写套接口集合
struct timeval timeout;//超时时间结点
FD_ZERO(&write_fdset);
FD_SET(fd, &write_fdset);
timeout.tv_sec = wait_seconds;//只关心秒
timeout.tv_usec =0;//不关心微秒
do
{
ret = select(fd+1, NULL, &write_fdset, NULL, &timeout);
}while(ret<0 &&errno == EINTR);//信号中断引起的失败,需要重启检测
if(ret==0)//没有检测到事件发生,即时间超时
{
ret=-1;
errno = ETIMEDOUT;
}
else if(ret == 1)
ret =0;
}
return ret;
}
/*
accept_timeout - 带超时的accept
fd为套接字
addr:输出参数,返回对方地址
wait_seconds:等待超时秒数,如果为0表示正常模式
成功(未超时)返回已连接套接字,超时返回-1并且errno = ETIMEDOUT
*/
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret;
socklen_t addrlen = sizeof(struct sockaddr_in);
if(wait_seconds>0)
{
fd_set accept_fdset;
struct timeval timeout;
FD_ZERO(&accept_fdset);
FD_SET(fd, &accept_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do{
ret =select(fd+1, &accept_fdset,NULL, NULL, &timeout);
}while(ret<0 && errno==EINTR);
if(ret == -1)
return -1;
else if(ret==0)
{
errno =ETIMEDOUT;
return -1;
}
}
//进行下方代码时,ret初始为1
if(addr != NULL)//地址不为空
{
ret = accept(fd, (struct sockaddr*)addr, &addrlen);
}
else
{
ret=accept(fd, NULL, NULL);//地址为空
}
if(ret=-1)
ERR_EXIT("accept");
return ret;
}
/*
activate_nonblock —— 设置I/O为非阻塞模式
*/
void activate_nonblock(int fd)
{
int ret;
int flags = fcntl(fd, F_GETFL);//获取文件标志
if(flags == -1)
{
ERR_EXIT("fcntl");
}
flags |= O_NONBLOCK;//设置非阻塞模式
ret = fcntl(fd, F_SETFL, flags);
if(ret == -1)
ERR_EXIT("fcntl");
}
/*
deactivate_nonblock —— 设置I/O为阻塞模式
*/
void deactivate_nonblock(int fd)
{
int ret;
int flags = fcntl(fd, F_GETFL);
if(flags ==-1)
ERR_EXIT("fcntl");
flags &= ~O_NONBLOCK;
ret = fcntl(fd, F_SETFL, flags);
if(ret ==-1)
ERR_EXIT("fcntl");
}
/*
connect_timeout —— connect
addr为要连接的对方地址
wait_seconds:等待超时秒数,如果为0表示正常模式
成功(未超时)返回0,超时返回-1并且errno = ETIMEDOUT
*/
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
int ret;
socklen_t addrlen= sizeof(struct sockaddr_in);
if(wait_seconds > 0)//先改成非阻塞模式
activate_nonblock(fd);
ret = connect(fd, (struct sockaddr*)addr, addrlen);
if(ret <0 && errno == EINPROGRESS)//表示连接正在进行情况
{
fd_set connect_fdset;
struct timeval timeout;
FD_ZERO(&connect_fdset);
FD_SET(fd, &connect_fdset);
timeout.tv_sec = wait_seconds;
timeout.tv_usec = 0;
do{
//一旦连接建立,套接字可写,所以fd放进可写集合
ret =select(fd+1, NULL, &connect_fdset, NULL, &timeout);
}while(ret<0&&errno == EINTR);
if(ret==0)
{
ret=-1;
errno= ETIMEDOUT;
}
else if(ret<0)//发生错误
{
return -1;
}
else if(ret==1)
{
/*ret返回1,有两种情况,一种是连接建立成功,一种是套接字产生错误*/
/*此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取*/
int err;
socklen_t socklen = sizeof(err);
int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen);
if(sockoptret == -1)
{
return -1;
}
if(err ==0)
ret =0;
else
{
errno = err;
ret =-1;
}
}
}
if(wait_seconds >0)
{
deactivate_nonblock(fd);
}
return ret;
}
socket编程I/O超时函数select封装
猜你喜欢
转载自blog.csdn.net/qq_22753933/article/details/82844205
今日推荐
周排行