网络超时检测
应用场景
在网络通信中,很多操作会使得进程阻塞
TCP套接字中的recv/accept
UDP套接字中的recvfrom
超时检测的必要性
避免进程在没有数据时无限制地阻塞
实现某些特定协议要求,比如某些设备规定,发送请求数据后,如果多长时间后没有收到来自设备的回复,需要做出一些特殊处理
简述Dos攻击
所谓Dos,翻译为拒绝服务攻击,攻击者的主要目的是想办法让目标机器停止提供服务或者资源访问,这些资源包括磁盘空间、内存、进程甚至网络带宽,从而阻止正常用户的访问
对服务器试试觓服务攻击的方法:
迫使服务器缓冲区写满,不再接收新的请求
使用IP进行欺骗,迫使服务器把合法用户的连接复位,影响合法用户的连接
利用函数参数设置网络超时检测
使用select/poll/epoll函数最后一个参数可以设置超时
//1.select设置超时
struct timeval tm={2,0}
sret=select(maxfd+1,&tempfds,NULL,NULL,&tm);
//关于超时参数
struct timeval{
long tv_sec;//秒
long tv_usec;//微秒
};
//poll设置超时
int poll(struct pollfd *fds,nfds_t nfds,int timeout);
//关于超时参数
//单位为毫秒,当参数为-1时表示阻塞
//epoll设置超时
int epoll_wait(epfd,events,20,2000);
//设置超时后返回值都为<0 error =0 超时 >0正确
利用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层/网络层
选项名称 | 说明 | 数据类型 |
---|---|---|
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 |
API接口
int getsockopt(int sockfd,int level,int optname,void *optval,socklen_ *optlen);
int setsockopt(int sockfd,int level,int optname,void *optval,socklen_ *optlen);
//功能:获得/设置套接字属性
/*
参数:
sockfd:套接字描述符
level:协议层
SOL_SOCKET(应用层)
IPPROTO_TCP(传输层)
IPPRPTP_IP(网络层)
optname:选项名
SO_BROADCAST 允许发送广播数据 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
optval:选项值
optlen:选项值大小指针
*/
//设置超时检测操作
struct timeval{
long tv_sec;//秒
long tv_usec;//微秒
};
//设置超时
struct timeval tm={2,0};
setsockopt(acceptfd,SOL_SOCKET,SO_RECVTIMEO,&tm,sizeof(tm));
//补充:
//设置端口和地址重用
int optval=1;
setsockoptval(sockfd,SOL_SOCKET,SO_REUSEADDR,&optval,sizeof(optval));
//利用alarm定时器进行设置
alam(5);
int segaction(int signum,const struct sigaction *act,struct sigaction *oldact);
//功能:对接收的指定信号处理
/*
参数:
signum:信号
sigaction(SIGALRM,NULL,&act)//获取原有属性
act.sa_handler=handler;//修改属性
sigaction(SIGALRM,&act,NULL);//将修改的属性设置回去
*/
/*注意:
在recv前调用alarm函数
alarm的SIGALRM信号产生后会打断(终端)下面的系统调用recv
打断之后相当于recv执行完毕
*/
广播
理论
在此之前,包含网络基础部分的内容,发送方式只有一个接收方,称为单播 如果同时发送给局域网中的所有主机,称为广播 只有用户数据报协议(使用UDP协议)套接字才能被称为广播 一般被设计成局域网搜索协议 关于广播地址(这里是网络基础中Ip部分的内容,就不再叙述了)
广播发送流程
创建用户数据报套接字 缺省创建的套接字不允许广播数据包,需要设置属性(setsockopt)
int on=1; setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
接收方地址指定为广播地址 指定端口信息 发送数据包
广播接收流程
创建用户数据报套接字 绑定IP地址和端口 绑定的端口必须和发送方指定的端口相同 等待接受数据
组播
单播方式只能发给一个接收方 广播方式发给所有主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信 组播是一个人发送,加入到多播组的人接收数据 多播方式既可以发给多个主机,又能避免像广播那样带来过多的负载
关于组播地址
不分网络地址和主机地址,组播地址属于D类IP
组播发送
创建用户数据报套接字 接收方指定为组播地址 指定端口信息 发送数据包
组播接收
创建用户数据报套接字 加入多播组 绑定IP地址和端口 等待接收数据
加入多播组核心代码
struct ip_mreq{
struct in_addr imr_multiaddr;//指定多播组IP
struct in_addr imr_interface;//本地网卡地址,通常指定为INADDR_ANY 0.0.0.0
};
struct ip_mreq;
bzero(&mreq,sizeof(mreq));
mreq.imr_multiaddr.s_addr=inet_addr("ip地址");
mreq.imr_interface.s_addr=INADDR_ANY;
setsockopt(sockfd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq));
本地套接字通信
特性
socket同样可以用于本地通信 创建套接字时使用本地协议PF_UNIX(或者PF_LOCAL) 分为流式套接字通信和用户数据报套接字通信 和其他进程间通信方式相比使用方便,效率更高 常用于前后台进程通信
核心代码
#include <sys/socket.h>
#include <sys/un.h>
unix_socket=socket(AF_UNIX,type,0);
struct sockaddr_un{
sa_family_t sun_family;//AF_UNIX
char sun_path[UNIX_PATH_MAX];//本地路径
};
struct sockaddr_un myaddr;
bzero(&myaddr,sizeof(myaddr));
myaddr.sun—_family=AF_UNIX;
strcpy(myaddr.sun_path,"本地路径");