锐英源软件
第一信赖

精通

英语

开源

擅长

开发

培训

胸怀四海 

第一信赖

当前位置:锐英源 / 在线教育 / SOCKET网络通信开发公开课 / Sockets-Address and Hosts Socket地址和主机
服务方向
人工智能数据处理
人工智能培训
kaldi数据准备
小语种语音识别
语音识别标注
语音识别系统
语音识别转文字
kaldi开发技术服务
软件开发
运动控制卡上位机
机械加工软件
软件开发培训
Java 安卓移动开发
VC++
C#软件
汇编和破解
驱动开发
联系方式
固话:0371-63888850
手机:138-0381-0136
Q Q:396806883
微信:ryysoft

3.16 Sockets-Address and Hosts Socket地址和主机


早期的互联网类似现在的局域网,是美国试验性质的产品。在通信协议标准化后,慢慢扩大范围和增加内容。内容一般是放到主机上。这里提到的主机是互联网上提供数据的计算机,而我们个人用的计算机一般是客户端。客户端访问主机,要么是浏览主机上的html网页,要么是连接主机上的数据库,要么是连接主机上的通信服务进行通信。在互联网统一组网环境里,IP地址代表了一个主机,但一个主机可以有多个IP。IP往往是和网卡对应,一台主机上可以安装多个网卡。本章讲了IP地址在编程上的处理细节。
要点

  • socket地址结构
  • 转换IP地址
  • 主机信息

3.16.1 Socket地址结构

  • 每类socket有关联的地址结构。虽然一个通用的地址结构定义的有(struct sockadd),但是永远不会直接使用它;它只是以指针类型用在某些默认情况。
  • 尽而,常见的处理策略如下:

struct sockaddr_un addr;

connect(sd,(struct sockaddr*)&addr,sizeof(addr));

  • 有很多类型的地址结构;实际上,有一个结构是适合于所有协议簇的。比如,上面的代码片段适合于unix 域 socket,或本地socket,这个结构体定义如下:

#include <sys/socket.h>

#include <sys/un.h>

#define UNIX_PATH_MAX 108

struct sockaddr_un {

sa_family_t sun_family; /* AF_UNIX*/

char sun_path[UNIX_PATH_MAX];/*pathname*/

};

(参考man unix)

  • 注意:第一个成员的类型是sa_family_t,这个成员在所有地址类型里都有。在这个情况下,sun_family成员必须设置为AF_UNIX,否则会有错误产生。
  • 第二个成员,sun_path给定一个在文件系统里的带NULL结束符的路径名。
  • 另外一个常用的类型是适用于IPV4协议的类型,如下:

#include <sys/socket.h>

#include <netinet/in.h>

struct sockaddr_in  {
sa_family_t sin_family;/*adderss  family:AF_INET*/
u_int16_t sin_port;/*port in network byte  order*/
struct in_addr sin_addr;/*Internet address*/
};
/*Internet adderss  .*/
struct in_addr {
int_addr_t  s_addr;/*adderss in network byte order*/
};
注意:int_addr_t只是一个无符号32位整数(参考man 7 ip)。

  • 注意,一些实现方案里还带一个sin_len成员,它指定了结构体对象的长度,但是Linux并不支持这个,且这个方案也不是Posix要求的。
  • 第一个成员给定了地址簇;这种情况下,它必须总是AF_INET。第二个成员指定了端口,必须为网络字节顺序。
  • 第三个成员是类型为in_addr的结构体,它只包含一个32位的整数,以网络字节顺序指定了IPV4地址。注意,当处理这个成员的数量值时,你把它看做结构体,还是整数,这非常重要。下面任何一个方法都是合法的:

struct sockaddr_in addr;

struct inaddr x=addr.sin_addr;/*以结构体形式访问*/

in_addr_t y=addr.sin_addr.s_addr;/*以整数形式访问*/

  • 我们随后会讨论怎样把传统的IP地址形式(比如192.168.1.100)或域名形式(www.axian.com)转换为整数的方法;这需要一些转换函数,这些函数能够做正反两方面的转换。
  • 正如我们所说,每个协议簇有它自己的地址结构,所以如果你遍历一下系统的头文件,你会找到类型的结构体定义,象sockaddr_in6,sockaddr_ipx,sockaddr_rose,sockaddr_ax25等等。但是请注意,在每种情况下,第一个成员给定地址簇,且任何多字节值必须以网络顺序(调用转换函数转换一下就可以了)。

3.16.2 转换IP地址

  • IP地址结构体包含了s_addr成员,它是一个32位网络顺序IP地址。地址通常是点分隔形式的字符串提供,比如192.168.1.1。下面代码段是用来转换这些字符串:

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <netdb.h>

struct in_addr {
unsigned long int s_addr;
}
int  
inet_aton(const char *cp,struct in_addr*inp);
char  *inet_ntoa(struct in_addr in);
in_addr_t  inet_addr(const char *cp);
  • inet_aton()函数的第一个参数是以点分隔的字符串,返回转换出来的结果到第二个参数指针里。成功转换返回非0,失败返回0。
  • inet_addr()函数把结构体值转为一个字符串。结构体里的值要以网络字节顺序。无效的输入会返回INADDR_NONE。然而,这个函数已经废弃了。
  • inet_ntoa()函数做相反方向的转换,把一个以网络字节顺序保存的网络地址转换到以点分隔的字符串里。注意,返回的字符串保存在一个静态分配的缓冲里,后面的调用会重写这个缓冲,如果你需要结果IP,这个值你要做拷贝另存。

char cip[16];

strcpy(cip,inet_ntoa());

  • 这些函数不支持域名形式。

3.16.3 主机信息

  • 截止到现在,我们只是讨论了数字形式的互联网地址,或它们的以点分隔的形式,比如0XC0A80100或192.168.1.0。然而,使用域名形式(wisestudy.cn)更直观,有三个理由来支持使用:
  • 易记
  • 数字可能会变,但是表示的名字不会变。
  • 移植向IPV6时,名字不会变,但是数字更难记了,因为更长了。1.1.192.168.1.0
  • DNS系统用来在IP数字和域名之间进行映射。
  • 客户端和服务器端都是通过resolver库来和DNS系统交互,这个库里主要函数如下:

#include <sys/socket.h>

#include <netdb.h>

struct hostent *gethostbyname(const char *name);

struct hostent *gethostbyaddr(const char *addr,int len,int type);

  • gethostbyname()函数的参数是一个主机的名称(要么是域名,要么是带点分隔的IP数字),返回结果是指向hostent结构体对象的指针,该指针指向对象里会有名称IP地址的转换结果。
  • gethostbyaddr()函数做了相反的转换。它的第一个参数*addr指向一个包含了二进制地址的in_addr结构体。这个结构体的长度由第二个参数控制,第三个参数是地址簇,它必须是AF_INET。
  • 成功转换返回一个hostent结构体,定义如下:

struct hostent {
       char *h_name;/*主机官方名称*/
       char **h_aliases;/*别名列表*/
       int h_addrtype;/*主机地址类型*/
       int h_length; /*地址长度*/
       char **h_addr_list;//地址的列表*/
}
#define h_addr h_addr_list[0] /*为了向后兼容*/

  • h_name成员包含一个指针,指向所谓的主机canonical名称;如果指定的服务移动到其它主机上,canonical名称和实际的主机名称会不一样,用现在流行的对用户透明的说法来讲,它可能是ftp.axian.com来代替axian.com
  • h_aliases参数包含以空结束的数组,数组里有别名的名称。
  • h_addrytype成员总是AF_INET.
  • h_length成员给出了地址的长度,以字节为单位。
  • 最后一个成员h_addr_list是一个以空结束的网络地址(以网络字节顺序)数组。名称h_addr可以用来简化使用,它代表了这个数组的第一个元素。早期的实现里没有这个数组。
  • 注意地址指针是一个in_addr结构,不是4字节整数形式的,所以你必须做如下的事情:

struct hostent *hostentry;

struct in_addr addr;

memcpy(&addr,hostentry->h_addr,hostentry->h_length);

printf(“The address is %X\n”,ntohl(addr.s_addr));

  • 注意最近发布的bind库允许指定name参数为带点格式,也就是说,gethostbyname(“redhat.com”和gethostbyname(“209.132.177.50”)结果是一样的。
  • 注意:gethostbyname()和gethostbyaddr()返回的指针是指向静态数据的,这样的数据在随后调用里会被覆盖,不能随后重用的。只是拷贝hostent结构体也不全面,因为它包含指针。这样一个更细心和深层次的拷贝就需要了。
  • 如果这些函数失败,使用perror()可以获取失败原因。然而,也能用下面函数来获取:

extern int h_errno;

void herror(const char *s);

const char *hstrerror(int err);

herror()的工作模式和perror()是一样的。hstrerror()获取和错误相关的描述文本的指针。这些函数认为是要废弃的,但是依然广泛使用。

  • h_errno变量可能值有

HOST_NOT_FOUND:指定主机没找到

NO_ADDRESS,NO_DATA:名称无效,但是有IP地址

NO_REOVERY:超出范围外的名称服务错误

TRY_AGAIN:暂时性错误,可随后再试

  • 注意:如果指定的主机可以从本地获取(也就是说,在/etc/hosts文件里指定的有),gethostbyname()会返回成功,但是不和DNS服务器交互来验证本地信息;这时候结果是不可靠的。这也是黑客经常用的攻击方法之一。
  • 为了获取或修改本地机器上的主机名称,可以使用下面的函数:

int gethostname(char*name,size_t len);

int sethostname(const char *name,size_t len);
这些函数在成功时返回0。获取函数的第二个参数获取到名称的最大长度;长名称会被截取掉。设置函数的第二个参数是新名称的实际长度。

只有超级用户才能调用sethostname(),其它用户能够用gethostname()来获取。
友情链接
版权所有 Copyright(c)2004-2021 锐英源软件
公司注册号:410105000449586 豫ICP备08007559号 最佳分辨率 1024*768
地址:郑州市金水区郑州大学北校区院(文化路97号院)内