Unix网络编程之一

名字与地址转换

域名系统

gethostbyname()/gethostbyaddr()

通过主机名查找IPV4地址
struct hostent *gethostbyname(const char *name);
//同过一个二进制的IP地址找到一个相应的主机名
//这两个不可重入函数,因为返回指向同一个静态变量结构的指针
struct hostent *gethostbyaddr(const void *addr,socklen_t len, int type);
struct hostent {
char h_name; / official name of host */
char *h_aliases; / alias list */
int h_addrtype; /* host address type : AF_INET */
int h_length; /* length of address */
char *h_addr_list; / list of addresses */
}

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <arpa/inet.h>

#define INET_ADDRSTRLEN 16

int main(int argc, char *argv[])
{

    char *ptr,**pptr;
    char str[INET_ADDRSTRLEN];
    struct hostent *hptr;
    while(--argc >0)
    {
        ptr = *++argv;
        if((hptr = gethostbyname(ptr))==NULL)
        {
            fprintf(stderr,"gethostbyname() error for host :%s: %s",\
                    ptr,hstrerror(h_errno));
            continue;
        }

        printf("主机域名为 :%s\n",hptr->h_name);

        /*别名主机地址列表*/
        for(pptr = hptr->h_aliases;*pptr != NULL;pptr++)
        {
            printf("\talias:%s\n",*pptr);
        }

        switch(hptr->h_addrtype)
        {
        case AF_INET:
            pptr = hptr->h_addr_list;
            for(;*pptr != NULL;pptr++)
            {
                printf("\taddress:%s\n",\
                       inet_ntop(hptr->h_addrtype,*pptr,str,sizeof(str)));
                break;

            }
        }
    }

    return 0;
}
//执行结果
./main www.sina.com
主机域名为 :polaris.sina.com.cn
    alias:www.sina.com
    alias:us.sina.com.cn
    alias:news.sina.com.cn
    alias:jupiter.sina.com.cn
    address:202.108.33.107

getserverbyname()/getserverbyport()

服务也通常有靠名字来认知,如果我们程序中通过名字而不是其端口牢指代一个服务器,而且从名字到端口的映射关系保存在一个文件中(/etc/services),那么即使端口号发生变动,我们只需修改/etc/services ,而不必重新编译应用程序

struct servent *getservbyname(const char *name, const char *proto);
struct servent *getservbyport(int port, const char *proto);

      struct servent {
           char  *s_name;       /* official service name */
           char **s_aliases;    /* alias list */
           int    s_port;       /* port number,network byte odeer */
           char  *s_proto;      /* protocol to use */

grep -e ^ssh /etc/services
ssh 22/tcp # SSH Remote Login Protocol
ssh 22/udp

struct servent *sptr
sptr = getservbyname(“ssh”,”tcp”) /SSH using TCP/
sptr = getservbyname(“ssh”,”udp”)/ssh using udp/

sptr = getservbyport(htons(22),”tcp”);

#include <stdio.h>
#include <stdlib.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>


#define MAXLINE 4096
int main(int argc, char *argv[])
{
    int sockfd,n;
    char recvline[MAXLINE+1];
    struct sockaddr_in servaddr;
    struct in_addr **pptr;
    struct in_addr *inetaddrp[2];
    struct in_addr inetaddr;
    struct hostent *hp;
    struct servent *sp;

    if(argc !=3)
    {
        fprintf(stderr,"usage: daytimetcpcli1<hostname> <server>");
        exit(1);
    }

    if((hp = gethostbyname(argv[1]))== NULL) /*通过域名获取IP地址*/
    {
        /*将一个字符串IP地址转换为一个32位的网络序列IP地址*/
        if(inet_aton(argv[1],&inetaddr)==0)
        {
            fprintf(stderr,"hostname error for %s: %s\n",argv[1],\
                    hstrerror(h_errno));
        }else
        {
            inetaddrp[0] = &inetaddr;
            inetaddrp[1] = NULL;
            pptr = inetaddrp;
        }
    }
    else
    {
        pptr = (struct in_addr **)hp->h_addr_list;
    }

    /*通过服务名获取端口号*/
    if((sp = getservbyname(argv[2],"tcp"))==NULL)
    {
        fprintf(stderr,"getservbyname() error for %s\n",argv[2]);
        exit(1);
    }

    for(;*pptr != NULL;pptr++)
    {
        sockfd = socket(AF_INET,SOCK_STREAM,0);
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = sp->s_port;
        memcpy(&servaddr.sin_addr,*pptr,sizeof(struct in_addr));

        printf("trying %s\n",inet_ntoa(servaddr.sin_addr));

        if(connect(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr))==0)
            break;
        fprintf(stderr,"connect error\n");
        close(sockfd);
    }

    if(*pptr == NULL)
    {
        fprintf(stderr,"unable to connect");
        exit(1);
    }

    while((n = read(sockfd,recvline,MAXLINE))>0)
    {
        recvline[n] = 0;
        fputs(recvline,stdout);
    }
    return 0;
}

//执行结果
./main  os ssh
trying 127.0.1.1
SSH-2.0-OpenSSH_7.2p2 Ubuntu-4ubuntu2.1

getaddrinfo()

gethostbyname()/gethostbyaddr这俩个函数只支持IPv4
getaddrinfo()函数能够处理名字到地址以及服务到端口的这两种转换,返回的是一个sockaddr结构,这些sockaddr结构随后可由套接字函数直接使用

int getaddrinfo(const char *node, const char *service,const struct addrinfo *hints,struct addrinfo **res);

      struct addrinfo {
           int              ai_flags;
           int              ai_family;
           int              ai_socktype;
           int              ai_protocol;
           socklen_t        ai_addrlen;
           struct sockaddr *ai_addr;
           char            *ai_canonname;
           struct addrinfo *ai_next;
       };

node:一个主机名或者地址串(IPv4的点分十进制串或者IPv6的16进制串)
service:服务名可以是十进制的端口号,也可以是已定义的服务名称,如ftp、http等
hints:可以是一个空指针,也可以是一个指向某个addrinfo结构体的指针,调用者在这个结构中填入关于期望返回的信息类型的暗示。举例来说:指定的服务既可支持TCP也可支持UDP,所以调用者可以把hints结构中的ai_socktype成员设置成SOCK_DGRAM使得返回的仅仅是适用于数据报套接口的信息。

res:本函数通过result指针参数返回一个指向addrinfo结构体链表的指针。
返回值:0——成功,非0——出错

ai_flags标志极其含义如下:

AI_PASSIVE 套接字永远被动打开
AI_CANONNAME 告知getaddrinfo函数返回主机的规范名字
AI_NUMERICHOST 防止任何类型的名字到地址的映射,node参数必须是一个字符串
AI_V4MAPPED 如果指定ai_family 成员的值为AF_INET6,那么如果没有可用的AAAA记录,就返回与A记录相对应的IPv4地址的IPV6映射
A_ALL 如果同时指定AI_V4MAPPED标志,那么除了返回AAAA记录想对应的ipv6地址外,还返回与A记录映射的ipv6地址

getnameinfo()他把套接字地址结构转换成主机名和服务器名

gai_strerror()

处理getadrinfo()的出错信息,返回:指向错误描述消息字符串的指针
const char *gai_strerror(int errcode);

freeaddrinfo()

void freeaddrinfo(struct addrinfo *res);
由于getaddrinfo()返回的所有存储空间都是动态获取的,这些存储空间通过freeaddrinfo还给系统

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <time.h>
#include <netdb.h>
#include <string.h>
#include <unistd.h>

#define MAXLINE 4096
/*getaddrinfo的接口函数 */
int tcp_connect(const char *host,const char *serv)
{
    int sockfd,n;
    struct addrinfo hints,*res,*ressave;
    bzero(&hints,sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if((n=getaddrinfo(host,serv,&hints,&res))!=0)
    {
        fprintf(stderr,"tcp_connect error for %s,%s,\%s",\
                host,serv,gai_strerror(n));
        exit(1);
    }
    ressave = res;

    do{ /*创建套接字描述符*/
        sockfd = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
        if(sockfd<0)
        {
            continue;
        }
        if(connect(sockfd,res->ai_addr,res->ai_addrlen)==0)
        {
            break; /*连接成功*/
        }
        close(sockfd);/*连接失败 关闭描述符号*/
    }while((res = res->ai_next)!=NULL);

    if(res == NULL)
    {
        fprintf(stderr,"tcp_connect error for %s ,%s",host,serv);
    }

    freeaddrinfo(ressave); /*释放内存*/
    return(sockfd);
}



int main(int argc, char *argv[])
{
    int sockfd,n;
    char recvline[MAXLINE+1];
    socklen_t len;
    struct sockaddr_storage ss;

    if(argc != 3)
    {
        fprintf(stderr,"main <hostname/IPaddress> <service/port#>");
        exit(1);
    }

    sockfd = tcp_connect(argv[1],argv[2]);

    len = sizeof(ss);
    getpeername(sockfd,(struct sockaddr*)&ss,&len); /*获取socket的对方地址*/


    char str[128];/*unix domian is largest*/
    char portstr[8];
    switch(ss.ss_family)
    {
    case AF_INET:
        {
            struct sockaddr_in *sin = (struct sockaddr_in*)&ss;
            if(inet_ntop(AF_INET,&sin->sin_addr.s_addr,str,sizeof(str))==NULL)
                break;
            if(ntohs(sin->sin_port)!=0)
            {
                snprintf(portstr,sizeof(portstr),":%d",ntohs(sin->sin_port));
                strcat(str,portstr);
            }
            break;
        }
    case AF_INET6:
        {
            struct sockaddr_in6 *sin6 =(struct sockaddr_in6*)&ss;
            if(inet_ntop(AF_INET6,&sin6->sin6_addr,str,sizeof(str))==NULL)
            {
                break;
            }
            if(ntohs(sin6->sin6_port)!=0)
            {
                snprintf(portstr,sizeof(portstr),"%d",ntohs(sin6->sin6_port));
                strcat(str,portstr);
            }
            break;
        }
    }

    printf("connect to : %s\n",str);
    while((n = read(sockfd,recvline,MAXLINE))>0)
    {
        recvline[n] = 0;
        fputs(recvline,stdout);
    }
    printf("Hello World!\n");
    return 0;
}

getnameinfo()

getnameinfo()是getaddrinfo()的互补函数 ,它以一个套接字地址为参数,返回其中的主机的一个字符和描述其中服务器的另一个字符串

int getnameinfo(const struct sockaddr *sa, socklen_t salen,char *host, socklen_t hostlen,char *serv, socklen_t servlen, int flags);

sockaddr指向一个套接字地址结构,其中包含待转换成直观可读的字符串的协议地址,salen是这个结构的长度,
host,hostlen指定主机字符串
serv,servlen指定服务器字符串,
如果把hostlen=0,servlen=0;就不返回主机字符串

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值