Unix网络编程之一

来源:互联网 发布:windows 引导修复 编辑:程序博客网 时间:2024/05/18 03:04

  • 名字与地址转换
    • 域名系统
      • gethostbynamegethostbyaddr
      • getserverbynamegetserverbyport
      • getaddrinfo
      • gai_strerror
      • freeaddrinfo
      • getnameinfo

名字与地址转换

域名系统

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 16int 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 4096int 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 sshtrying 127.0.1.1SSH-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
原创粉丝点击