目录
前言
1.网络信息检索函数
gethostname() 获得主机名
getpeername() 获得与套接口相连的远程协议地址
getsockname() 获得本地套接口协议地址
gethostbyname() 根据主机名取得主机信息 -> endhostent()
gethostbyaddr() 根据主机地址取得主机信息
getprotobyname() 根据协议名取得主机协议信息
getprotobynumber() 根据协议号取得主机协议信息
getservbyname() 根据服务名取得相关服务信息
getservbyport() 根据端口号取得相关服务信息
代码演示域名解析:
#include "net.h"
int main(int argc, const char *argv[])
{
int fd;
struct sockaddr_in cin;
//定义hostent结构体
struct hostent *hs = NULL;
//使用DNS域名解析
if((hs = gethostbyname(SERV_IP_ADDR)) == NULL){
perror("gethostbyname");
exit(0);
}
//1.创建socket fd
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0){
perror("socket");
exit(0);
}
//2.connect函数
bzero(&cin,sizeof(cin));
cin.sin_family = AF_INET;
cin.sin_port = htons(SERV_PORT);
#if 0
cin.sin_addr.s_addr = inet_addr(SERV_IP_ADDR);//ipv4专用
#else
//h_addr = h_addr_list[0]
//cin.sin_addr.s_addr = *(uint32_t*)hs->h_addr;
cin.sin_addr.s_addr = *(uint32_t*)*hs->h_addr_list;
endhostent();//释放结构体
hs = NULL;
#endif
//连接
if( connect(fd,(struct sockaddr*)&cin,sizeof(cin)) < 0){
perror("connect");
exit(0);
}
//3.读写
int ret;
char buf[BUFSIZ];//512
while(1){
bzero(buf,sizeof(buf));
if(fgets(buf,sizeof(buf)-1,stdin) == NULL){
continue;
}
do{
ret = write(fd,buf,BUFSIZ-1);
}while(ret < 0 && EINTR == errno);
if(ret < 0){
perror("read");
exit(0);
}
if(!strncasecmp(buf,QUIT,strlen(QUIT))){
printf("通话结束\n");
break;
}
}
close(fd);
return 0;
}
2.网络属性配置
getsockopt和setsockopt
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)
前面的代码案例中,服务端允许地址快速重用时,使用了setsocket
level指定控制套接字的层次.可以取三种值:
1) SOL_SOCKET:通用套接字选项. (应用层)
2) IPPROTO_TCP:TCP选项. 传输层)
3) IPPROTO_IP:IP选项.(网络层)
optname指定控制的方式(选项的名称),我们下面详细解释
optval获得或者是设置套接字选项.根据选项名称的数据类型进行转换
3.网络超时
在网络通信中,很多操作会使得进程阻塞
TCP套接字中的recv/accept/connect
UDP套接字中的recvfrom
超时检测的必要性:
避免进程在没有数据时无限制地阻塞
当设定的时间到时,进程从原操作返回继续运行
3.1 网络超时检测1
设置socket的属性 SO_RCVTIMEO
参考代码如下:
struct timeval tv;
tv.tv_sec = 5; // 设置5秒时间
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET,SO_RCVTIMEO,&tv,sizeof(tv));//设置接收超时
recv() / recvfrom()//从socket读取数据
3.2 网络超时检测2
用select检测socket是否’ready’
参考代码如下:
struct fd_set rdfs;
struct timeval tv = {5 , 0}; // 设置5秒时间
FD_ZERO(&rdfs);
FD_SET(sockfd, &rdfs);
if (select(sockfd+1, &rdfs, NULL, NULL, &tv) > 0) // socket就绪
{
recv() / recvfrom() // 从socket读取数据
}
3.3 网络超时检测3
设置定时器(timer), 捕捉SIGALRM信号
参考代码如下:
void handler(int signo) { return; }
struct sigaction act;
sigaction(SIGALRM, NULL, &act);
act.sa_handler = handler;
act.sa_flags &= ~SA_RESTART;
sigaction(SIGALRM, &act, NULL);
alarm(5);
if (recv(,,,) < 0) ……
代码案例:
1.server端
#include "net.h"
int main(int argc, const char* argv[])
{
int fd;
struct sockaddr_in sin;
//signal(SIGCHLD,handler);
//1.创建socket fd
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket");
exit(0);
}
/*允许绑定地址快速重用 */
int b_reuse = 10;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));
/*允许阻塞 */
struct timeval tout;
tout.tv_sec = 10;
tout.tv_usec = 0;
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tout, sizeof(tout));//接收阻塞
//2.bind函数
bzero(&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(SERV_PORT);
//优化1
#if 0
sin.sin_addr.s_addr = htonl(INADDY_ANY);
#else
if (inet_pton(AF_INET, SERV_IP_ADDR, &sin.sin_addr) < 0) {
perror("inet_pton");
exit(0);
}
#endif
if (bind(fd, (struct sockaddr*)&sin, sizeof(sin)) < 0) {
perror("bind");
exit(0);
}
//3.listen()函数
listen(fd, 5);
//4.1 accept
int newfd = -1;
struct sockaddr_in cin;
socklen_t socklen = sizeof(cin);
//5.
int ret, readwrite;
char buf[BUFSIZ];
char buf02[BUFSIZ + 10];
//阻塞等待客户端连接
newfd = accept(fd, (struct sockaddr*)&cin, &socklen);
if (newfd < 0) {
perror("accept");
exit(0);
}
char ipv4_addr[16];
if (!inet_ntop(AF_INET, &cin.sin_addr, ipv4_addr, socklen)) {
perror("inet_ntop");
exit(0);
}
printf("client(%s,%d) is connect\n", ipv4_addr, ntohs(cin.sin_port));
while (1) {
bzero(buf, sizeof(buf));
bzero(buf02, sizeof(buf02));
readwrite = read(newfd, buf, BUFSIZ - 1);
if (errno == EAGAIN) {//没有数据可读
printf("读超时10si,提除客户端\n");
break;
}
if (readwrite < 0) {
perror("read");
break;
}
if (!readwrite) {
continue;
}
printf("result=%s\n", buf);
//返回消息给发送端
strncpy(buf02, BUFADD, strlen(BUFADD));
strcat(buf02, buf);
do {
readwrite = write(newfd, buf02, strlen(buf02));
} while (readwrite < 0 && EINTR == errno);
if (!strncasecmp(buf, QUIT, strlen(QUIT))) {
printf("通话结束fd = %d\n", newfd);
}
/*over...*/
}
//ret--;
close(fd);
return 0;
}
2.client端
#include "net.h"
void checkParam(char*s){
printf ("\n%s serv_ip serv_port", s);
printf ("\n\t serv_ip: serv_iperver ip address");
printf ("\n\t serv_port: server port(>5000)\n\n");
}
int main(int argc, char **argv)
{
int fd;
int port = -1;
struct sockaddr_in cin;
if(argc != 3){
checkParam(argv[0]);
exit(0);
}
//1.创建socket fd
fd = socket(AF_INET,SOCK_STREAM,0);
if(fd < 0){
perror("socket");
exit(0);
}
struct timeval ti;
ti.tv_sec = 10;//允许超时10s
ti.tv_usec = 0;
//setsockopt (fd, SOL_SOCKET, SO_SNDTIMEO, &ti, sizeof (ti));
setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&ti, sizeof (ti));
//2.connect函数
bzero(&cin,sizeof(cin));
cin.sin_family = AF_INET;
if((port = atoi(argv[2])) < 5000){
checkParam(argv[2]);
exit(0);
}
cin.sin_port = htons(port);
if(inet_pton(AF_INET,argv[1],&cin.sin_addr) < 0){
perror("inet_pton\n");
exit(0);
}
if( connect(fd,(struct sockaddr*)&cin,sizeof(cin)) < 0){
perror("connect");
exit(0);
}
//3.读写--使用select函数
int ret;
char buf[BUFSIZ];//512
while(1){
printf("abc\n");
bzero(buf,sizeof(buf));
if(fgets(buf,sizeof(buf)-1,stdin) == NULL){
continue;
}
do{
ret = send(fd,buf,BUFSIZ-1,0);
}while(ret < 0 && EINTR == errno);
if(ret < 0){
perror("read");
exit(0);
}
if(!strncasecmp(buf,QUIT,strlen(QUIT))){
printf("通话结束\n");
break;
}
//读取服务端返回的消息,若是超时没有读取到数据,则退出客户端
bzero(buf,strlen(buf));//清零
ret = recv(fd,buf,BUFSIZ-1,0);
if(ret < 0){
perror("read");
exit(0);
}
if(!ret){
printf("超时没收到消息,服务器踢掉客户端\n");
break;
}
printf("来自服务端的数据:%s\n",buf);
if(!strncasecmp(buf+strlen(BUFADD),QUIT,strlen(QUIT))){
printf("send client 断开连接\n");
break;
}
}
close(fd);
return 0;
}
思考:
试总结如何在linux中动态检查到是否有网络以及网络中途的掉线/连接的情况检查?
1.应用层
心跳检测方法 1:数据交互双方隔一段时间,一方发送一点数据到对方,对方给出特定的应答。如果超过设定次数大小的时间内还是没有应答,这时候认为异常
方法2:改变套接字的属性
函数定义:
void setKeepAlive (int sockfd, int attr_ on, socklen_ .t idle_ time,socklen_ .t interval, socklen_ .t cnt){
setsockopt (sockfd, SOL_ SOCKET, SO_ KEEPALIVE, (const char *) &attr. _on, sizeof (attr_ _on));
setsockopt (sockfd, SOL_ _TCP, TCP_ _KEEPIDLE, (const char *) &idle_ _time, sizeof (idle_ time));
setsockopt (sockfd, SOL_ _TCP, TCP_ _KEEPINTVL, (const char *) &interval, sizeof (interval));
setsockopt (sockfd, SOL_ TCP,TCP_ _KEEPCNT,(const char *) &cnt, sizeof (cnt));
}
使用:
int keepAlive = 1;
//设定KeepAlive
int keepIdle = 5;
//开始首次KeepAlive探测前的TCP空闭时间
int keepInterval = 5;
//两次KeepAlive探测间的时间间隔
int keepCount = 3;
//判定断开前的KeepAlive探测次数
setKeepAlive (newfd, keepAlive, keepIdle, keepInterval, keepCount) ;2.内核中
网卡驱动中 2.6内核里面,使能1s的周期性检查定时器
网卡硬件或者我们通过GPIO,插拔网线时候产生中断,处理相应中断 //立即检测到