网络信息检索,网络属性设置及超时检测

1.网络信息检索函数

1.1API

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

#include <netdb.h>
extern int h_errno;

/*
功能:根据主机名获取主机信息
参数:主机名字(IP/域名)
返回值:成功返回结构体指针,失败返回NULL
*/
struct hostent *gethostbyname(const char *name);

hostent结构体

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;       /* 地址列表 */
}
#define h_addr h_addr_list[0] /* for backward compatibility */

h_addr_list:An array of pointers to network addresses for the host 
            (32位网络字节序), terminated by a null pointer.

例如当域名为www.baidu.com时候此时会会存在许多IP地址与该域名绑定,而此处直接取第一个作为结果返回

注意:
1)此处函数设置错误号的时候是设置在h_errno里面,打印函数为void herror(const char *s)或者const char *hstrerror(int err).在使用完hostent结构体之后需要通过void endhostent(void)函数释放结构体
2)此函数仅用于IPV4域名解析

示例:

#include "common.h"

void tips(char *s)
{
    printf("
%s serv_ip serv_port", s);
    printf("
	 serv_name:server domain name or ip address");
    printf("
	 serv_port:server port(port > 5000)

");
}
int main(int argc, char **argv)
{
    int port = -1;/*目标服务器端口 */
    int fd = -1;
    struct sockaddr_in sin;
    struct hostent *hs = NULL;
    char buff[BUFSIZ];
    int ret = -1/*write函数返回值 */;
    /*创建socket */
    if(argc != 3)
    {
        tips(argv[0]);
        exit(-1);
    }

    port = atoi(argv[2]);
    if(port < 5000)
    {
        tips(argv[0]);
        exit(-1);        
    }
    if((hs = gethostbyname(argv[1])) == NULL)
    {
        herror("gethostbyname");
        exit(-1);
    }
    if((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket");
        exit(-1);
    }
    /*结构体成员清零 */
    bzero(&sin, sizeof(sin));
    /*填充结构体 */
    sin.sin_family = AF_INET;
    sin.sin_port = htons(port);/*网络字节序端口号 */
#if 1
    sin.sin_addr.s_addr = *((uint32_t *)hs->h_addr);/*先指针类型转换,再取值 */
    endhostent();/*释放节点内存 */
    hs = NULL;/*防止野指针 */
#else
    if(inet_pton(AF_INET, argv[1], (void *)&sin.sin_addr.s_addr) != 1)
    {
        perror("inet_pton");
        exit(-1);
    }
#endif
    /*连接服务器 */
    if(connect(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0)
    {
        perror("connect");
        exit(-1);
    }
    /*读写数据 */
    while(1)
    {
        bzero(buff, BUFSIZ);
        if(fgets(buff, BUFSIZ - 1, stdin) == NULL)
        {
            continue;
        }
        write(fd, buff, strlen(buff));
        if(strcmp(buff, QUIT_STR) == 0)
        {
            printf("Client is exting...
");
            break;
        }
    }
    /*关闭套接字 */
    close(fd);
    return 0;
}

/*common.h*/
#ifndef __TCPC1_H__
#define __TCPC1_H__
/*网络相关头文件 */
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>

#include <fcntl.h>

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>

#include <errno.h>

#include <pthread.h>

#include <signal.h>
#include <sys/wait.h>

#include <netdb.h>
#define SERV_PORT 5001
#define SERV "0.0.0.0"
#define SERV_IP_ADDR "47.105.70.108"
#define h_addr h_addr_list[0]
#define BACKLOG 5
#define QUIT_STR "quit
"

#endif

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

2.网络属性设置

2.1概念

  • 在linux系统中,每一个文件描述符在内核里面都对应一个结构体,里面存储着该文件描述符的相关属性,使用socket函数创建套接字是以默认方式创建,而当我们要想自己设置网络套接字属性时候就需要以下函数

2.1API

  • setsockopt/getsockopt

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

/*
功能:设置网络属性
参数:
参数1:套接字文件描述符
参数2:指定控制套接字的层次
      SOL_SOCKET  通用套接字(应用层)
      IPPROTO_IP  IP选项(网络层)
      IPPROTO_TCP  TCP选项(传输层)
参数3:指定控制的方式
参数4:见表
参数5:选项长度
返回值:成功返回0,失败返回-1
*/
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 b_reuse = 1;
setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int));/*允许重用本地地址*/

3.网络超时设置

3.1网络超时必要性

  • 在网络通信中,许多操作会使得进程阻塞(TCP中的recv,accept,connect,UDP中的recvfrom)
  • 超时检测避免了进程在没有数据时候无限阻塞
  • 当设定时间到达,进程从原操作返回继续执行

3.2设置网络超时方法

1.设置socket属性SO_RCVTIMEO

  • 参考代码

struct timeval tv;
tv.tv_sec = 5;/*5s超时*/
tv.tv_usec = 0;
setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));

2.使用select检测socket是否ready

  • 参考代码

struct fd_set rdfs;
struct timeval tv;
tv.tv_sec = 5;/*5s超时*/
tv.tv_usec = 0;
FD_ZERO(&rdfs);
FD_SET(socket, &rdfs);
if(select(socket+1, &rdfs, NULL, NULL, &tv) > 0)
{
  recv()/recvfrom();
}

3.设置定时器,捕捉SIGALRM信号

  • 参考代码

void handler(int signo)
{
  ...;
}
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)
{
  ...
}

3.3设置心跳间隔

1.系统实现

int keepAlive = 1;/*是否设置keepAlive*/
int keepIdle = 5;/*如该连接在5秒内没有任何数据往来,则进行探测*/
int keepInterval = 5;/*前后两次探测之间的时间间隔,单位是秒*/
int keepCount = 3;/*关闭一个非活跃连接之前的最大重试次数*/

setKeepAlive(newfd, keepAlive, int keepIdle, keepInterval, keepCount);

void setKeepAlive(int sockfd, int attr_on, socket_t idle_time, socket_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 *)&keepCount, sizeof(keepCount));
}

2.数据交互双方每隔一段时间,一方发送数据,对方给出特定应答,在设定最大超时时间内没应答则判定异常

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容