套接字地址结构

IP地址

IPv4地址:32位,4字节,每一个字节是一个十进制整数,用“数-点”表示

IPv6地址:128位,8个16位的部分,每个部分表示为一个十六进制数,用“:”分隔。

两种数据类型in_addr和in6_addr分别表示32位的IPv4地址和128位的IPv6地址,分别定义如下:

#include <netinet/in.h>
typedef uint32_t in_addr_t;
struct in_addr{
    in_addr_t  s_addr;
};
struct in6_addr{
    uint8 s6_addr[16];
};

域名地址

网络中的机器也可用域名表示,每一个域名对应一个IP地址。UNIX系统中用一个主机网络地址数据库/etc/hosts来存放主机名与主机IP地址之间的映射。

gethostbyname是从主机地址数据库获得地址信息,这个信息的获取有两种途径,通过网络中的域名系统或者通过hosts文件。在/etc/host.conf中包含名字解析配置文件,其中有一行order指明了这两种方法的解析顺序。比如,order hosts,bind,那么gethostbyname会先查看/etc/hosts文件,如果在这个文件中找不到此域名,那么他会打开一个通向域名服务器的UDP套接字,请求域名服务器来解析这个域名。域名服务器的IP地址在/etc/resolv.conf中给出。

#include <netdb.h>
/*
hostent结构:
char *h_name:主机的正式名字
char **h_aliases:主机的可选别名数组
int h_addrtype:主机地址类型,AF_INET或AF_INET6或其他类型
int h_length:每一个地址的长度,以字节为单位。AF_INET是4,AF_INET6为16
char **h_addr_list:指向主机网络地址数组的指针。因为主机可能与多个网络相连,就有多个网络地址。
*/
struct hostent *gethostbyname(const char *name);//返回主机name的地址信息
struct hostent *gethostbyaddr(const void *addr, size_t length, int type); //返回IP地址为addr的主机的有关信息
void sethostent(int stayopen); //打开主机网络地址数据库,若stayopen非0,则后续gethostbyname;gethostbyaddr;gethostent将不会关闭数据库。
struct hostent *gethostent(); //返回主机地址数据库中的下一项
void endhostent(); //关闭主机地址数据库

注意sethostent这个函数是打开主机网络地址数据库,为什么stayopen非0的话后续查询不会使数据库关闭呢。因为这个过程涉及到与域名服务器通信,默认情况下用UDP套接字进行临时的连接,并且查询结束后就会被关闭。但是如果参数非0,那么创建的是TCP套接字,查询结束后仍保持连接,因此如果要查询多个主机地址,可以将stayopen置1.

服务与端口号

端口号区别同一台计算机中不同的服务进程。端口号是16位无符号整数,0~65535.

UNIX系统里的标准服务,在/etc/services或域名服务器中。

netdb.h中定义了servent类型用于表示服务的信息:

char *s_name:服务程序的名字

char *s_aliases:服务程序的别名

int s_port:服务程序端口号,按网络字节序给出

char *s_proto:与该服务一起使用的协议名

当对标准服务所用的端口号不确定时,可以用函数获取:

#include <netdb.h>
struct servent *getservbyname(const char *name, const char *proto); //返回使用协议proto且名为name的服务的有关信息。
struct servent *getservbyport(int port, const char *proto); //返回使用端口port且使用的协议为proto的服务的有关信息。
void setservent(int stayopen); //打开服务数据库准备开始扫描。若stayopen非0,那么他会设置一标志使得后继调用的getservbyname;getservbyport;getservent等函数不会关闭该数据库
struct servent *getservent(void);//返回数据库下一项
void endservent(void); //关闭服务数据库

套接字地址结构

不同的通信域有不同的地址结构,IPv4地址的数据类型是sockddr_in结构,IPv6地址的数据类型是sockddr_in6结构。

struct sockddr_in{
    sa_family_t sin_family; // 地址族
    in_port_t sin_port;  //16位端口号,网络字节序
    struct in_addr sin_addr; // 32位IPv4地址,网络字节序
    unsigned char sin_zero[8]; //保留
 };
struct sockddr_in6{
    sa_family_t sin6_family;
    in_port_t sin6_port;  
    uint32_t sin6_flowinfo;  //为处理实时服务引入的流标号和优先级信息
    struct in6_addr sin6_addr; //128位IPv6地址,网络字节序
};

但是为了支持不同的协议族,很多套接字函数都要求用通用套接字地址结构。类型是sockaddr.

struct sockaddr{
    sa_family_t sa_family;  //地址族,AF_xxx
    char sa_data[];              //实际地址数据
 }
在需要sockaddr类型的上下文中,通常需要将专用的特殊地址结构类型强制类型转换,如struct sockaddr_un name; ...; bind(sockfd, (struct sockaddr *)&name, sizeof(name));
由于不同的计算机,在存储器中存储方式不同,可能是大端模式或小端模式,所以两个计算机想要通信必须用同样的模式,规定大端模式字节顺序为网络字节顺序。所以上面这些地址结构那些成员必须都是按照网络字节顺序表示的。

转换网络字节顺序的函数:

#include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);
uint32_t htonl(uint32_t hostlong);
uint16_t ntohs(uint16_t netshort);
uint32_t ntohl(uint32_t netlong);
h表示host,n表示net,s表示short,l表示long。

前两个用来把主机字节顺序转换成网络字节序,后两个相反。第1,第3个用来转换成员sin_port,第2,第3个用来转换sin_addr

下面一组函数用来把点-数表示的IPv4地址或冒号表示的IPv6地址转换成网络字节序:

#include <arpa/inet.h>
int inet_aton(const char *name, struct in_addr *addr); //将name从数-点表示的IPv4地址转换成二进制,存在addr中的in_addr中
char *inet_ntoa(struct in_addr inaddr);  //转换网络序的32位IPv4地址到标准的数-点表示的字符串
int inet_pton(int family, const char *nameptr, void *addrptr); // 可以处理IPv4或IPv6,p表示地址的字符串形式,n表示地址的二进制形式
const char *inet_ntop(int family, void *addrptr, char *nameptr, size_t len); //len可以使用<netinet/in.h>中的两个宏:INET_ADDRSTRLEN(16); INET6_ADDRSTRLEN(46)



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值