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/udpstruct 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;就不返回主机字符串
- Unix网络编程之一
- unix网络编程之一简述
- Unix/Linux 编程:网络编程之一 Socket
- Unix网络编程服务器设计方式之一
- unix网络编程之一TCP/UDP
- UNIX网络编程之一:代码环境搭建
- UNIX网络编程之一、如何运行书籍自带源码
- UNIX网络编程学习之一:socket的简介
- UNIX网络编程
- UNIX网络编程
- 关于Unix网络编程
- unix网络编程
- UNIX网络编程
- UNIX网络编程
- UNIX网络编程
- UNIX网络编程
- Linux/UNIX网络编程
- Unix网络编程
- 数据表特殊字段在报表中的表示方法
- Qt之多线程(五)
- 为什么spring注入接口正确而注入接口的实现类错误?
- 使用Py2Exe for Python3创建自己的exe程序
- [react native]学习日志---es6语法学习
- Unix网络编程之一
- Qt之多线程(六)
- position
- 郝斌的C语言基础 163 冒泡排序
- Git基本用法
- Python Tkinter实现的简单计算器
- 高压VF角度计算函数(同异步电机通用) sub_ang_cal()
- 回调函数
- HashSet