Linux嵌入式网络 _ 网络信息检索、网络属性设置、超时检查、心跳检测

一、网络信息检索

1、网络信息检索 常用函数

  • gethostname();     获得主机名
  • getpeername();     获得与套接字相连的远程协议地址
  • getsockname();     获得本地套接口协议地址;
  • gethostbyname();  根据主机名 取得主机信息

              endhostent()  :使用endhostent() 清空 申请的 结构体的空间;

  • gethostbyaddr();         根据主机地址取得主机信息
  • getprotobyname();      根据协议名取得 主机协议信息
  • getprotobynumber();   根据协议号取得主机协议信息
  • getservbyname();       根据服务器名取得 相关服务信息
  • getservbyport();          根据端口号取得相关服务信息

2、在客户端代码中,使用 gethostbyname()函数去 输入域名  的方法 去连接服务器;

#include <netdb.h>
extern int h_errno;	
struct hostent *gethostbyname(const char *name);

name 指向主机名的指针(域名或 IP地址)


#include <sys/socket.h>       /* for AF_INET */
struct hostent *gethostbyaddr(const void *addr,socklen_t len, int type);

注意:
    IPv4中使用gethostbyname()函数完成主机名到地址解析,这个函数仅仅支持IPv4,且不允许调用者指
定所需地址类型的任何信息,返回的结构只包含了用于存储IPv4地址的空间。IPv6中引入了getaddrinfo()
的新API,它是协议无关的,即可用于IPv4,也可用于IPv6.
  • 返回值:
  struct hostent {
        char  *h_name;            /* official name of host */
        char **h_aliases;         /* alias list */
        int    h_addrtype;        /* host address type */
        int    h_length;          /* length of address */
        char **h_addr_list;       /* list of addresses地址列表,指向主机的多个网络地址(网络字节序32位整数). */
    }          
    #define h_addr h_addr_list[0] /* for backward compatibility */
  • 结构体中,地址列表的存放示例:

                  

3、错误处理 herror()、hstrerror()

    extern int h_errno;//出错号
        void herror(const char *s);/打印错误信息
        const char *hstrerror(int err);//打印错误信息

4、客户端使用 gethonstbyname 指定域名  连接 服务器示例:

#include "net_pth.h"
#include <netdb.h>

void usage(const char* s){
	printf("\n %s serv_ip ser_port \n",s);
	printf("\n\t serv_ip: server ip address");
	printf("\r\n serv_port: server port(>5000)\n\n");
}

int main(int argc, const char *argv[])
{
	int fd = -1;
	int port = 0;
	struct sockaddr_in sin;
	struct hostent *hs = NULL;
	/* 1、创建socket fd */
	if((fd = socket(AF_INET , SOCK_STREAM,0)) < 0 ){ //创建TCP 通信 Socket文件描述符
		perror("socket");
		exit(1);
	}
	if(argc != 3 ){
		usage(argv[0]);
		exit(1);
	}
	port = atoi(argv[2]);
	if (port < 5000){
		usage(argv[0]);
		exit(1);
	}
	
	if((hs = gethostbyname(argv[1])) == NULL){  //根据主机名 获取 主机信息
		perror("gethostbyname");
		exit(1);
	}

	/* 2、连接服务器 */
	/* 2.1 填充 struct sockaddr_in 结构体变量 */
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);

	sin.sin_addr.s_addr = *(uint32_t *)hs->h_addr; //把获取的主机IP地址,填充进入结构体中
	endhostent();
	hs = NULL;
    printf("Client staring .... OK! \n");

	/* 2、2 连接服务器 */
	if( connect(fd ,(struct sockaddr *)&sin,sizeof(sin)) < 0){
		perror("connect");
		exit(1);
	}

	/* 3、读写数据*/
	char buf[BUFSIZ];
	while(1){
		bzero(buf, sizeof(buf));
		if(fgets(buf,BUFSIZ-1,stdin) == NULL){
			continue;
		}
		write(fd , buf , strlen(buf));
		if(!strncasecmp(buf,QUIT_STR,strlen(QUIT_STR))){
			break;
		}
	}
	/* 4、关闭套接字*/
	close(fd);
}

二、网络属性设置

 

1、网络属性设置接口

  • 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);
    1. level 指定控制套接字的层次,可以取三种值:
      1. SOL_SOCKET:   通用套接字选项 (应用层)
      2. IPPROTO_IP:    IP选项;(网络层)
      3. IPPROTO_TCP:  TCP选项(传输层)
  • 以setsockopt为主;
  • optname 指定控制的方式(选项的名称),我们下面详细解释;

                           

   —  划线部分一般是对应层,常用控制的方式;

2、示例

在以上例子中 ,替换其中的参数,得到一般的想要的效果;

其中发送与接收超时的数据类型为:struct  timeval结构体类型;

其中 setsockopt()函数中 参数const void *optval  ---  传给实现效果的相应的参数;

其中timeval 的定义如下:
struct timeval{

      long  tv_sec;            /*seconds : 秒*/

      long  tv_usec;         /*microseconds: 微妙 */

}
/*允许广播 */

int  b_br = 1;

setsockopt(fd , SOL_SOCKET , SO_BROADCAST, &b_br, sizeof(int));

/*设置 接收超时 */

struct  timeval  tout;

tout.tv_sec = 5;

tout.tv_usec = 0;

setsockopt(fd,SOL_SOCKET , SO_RCVTIMEO , &tout , sizeof(tout));

三、超时检查

  • 网络超时:在网络通信中,很多操作会使得进程阻塞;
  • TCP套接字中的recv/accept/connect;
  • UDP套接字中的recvfrom
  • 超时检测的必要性
    1. 避免进程在没有数据时无限制地阻塞;
    2. 当设定的时间到时,进程从原操作返回继续运行;

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读取数据;

2、网络超时优化 第二种方法:用select 是否 ‘ready’

参考代码如下:

struct fd_set  rdfs;
while(1)
{
    struct timeval tv = {5,0};
    FD_ZERO(&rdfs);
    FD_SET(socket,&rdfs);
    if(select(sockfd -1,&rdfs,NULL,&tv) > 0)//socket就绪
    {
        recv() / recvfrom ()  // 从socket 读取数据
    }
}

3、网络超时优化 第三种方法:设置定时器(timer),捕捉SIGALRM信号

参考代码如下:

void handler(int signo) {return ;}

struct sigaction act;
sigaction(SIGALRMR , NULL ,&act);
act.sa_flags &= ~SA_RESTART;//清除掉SIGALRM信号SA_RESTART
sigaction(SIGALRM,&act,NULL);
alarm(5);
if(recv(, , , ) < 0) ......;

四、心跳检测

1、如何在linux中动态检查到是否有网络以及网络中途的掉线/连接的检查?

  • 应用层

              —  心跳检测;

  • 内核中

              —  网卡驱动中 2.6内核里面,使能1s的周期性检查定时器;

              —  网卡硬件或者我们通过GPIO,插拔网线时候产生中断,处理相应中断  ;//立即检测到

2、心跳检测的两种方法

方法一:

客户端 隔一定的时间向服务器发送一段数据,告诉服务器客服端在保持连接;规定在一定的次数,接收不到对方的回应则判定异常;

方法二:

  • 可通过setkeepAlive()函数 ,来改变套接字的属性;(默认两个小时去检测一次客户端是否掉线)

参考代码如下:

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));
}

void cli_data_handle(void *arg)
{
    int newfd = *(int *)arg;

    int keepAlive = 1;   //设定keepAlive
    int keepIdle = 5;   //开始首次keepAlive探测前的TCP空闲时间
    int kepInterval = 5; //两次keepAlive 探测间的 间隔时间
    int keepCount = 3;  // 判定断开前的keepAlive 的探测次数
    
    setKeepAlive(newfd ,keepAlive ,keepIdle ,kepInterval ,keepCount );

    //..和newfd进行数据读写
    int ret = -1;
    char buf[BUFSIZE];
    char resp_buf[BUFSIZE + 10];
    while(1) 
    {
        bzero(buf,BUFSIZE);
        do{
            ret = read (newfd , buf ,BUFSIZE - 1);
           }while(ret < 0 && BINTR == errno);
        if(ret < 0)
        {
            perror("read");
            exit(1);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值