【UNIX网络编程(一)】套接字地址结构、网络字节顺序和地址转换功能

时间:2023-03-09 02:49:47
【UNIX网络编程(一)】套接字地址结构、网络字节顺序和地址转换功能

介绍:应该用在网络编程实现每个套接字地址结构。所以主套接字地址结构后前提网络计划编制,地址结构可以在两个方向上发送:从工艺到内核和内核处理。构中的二进制值之间进行转换。



大多数套接字函数都须要一个指向套接字地址结构的指针作为參数。不同协议都有自己的套接字地址结构。

通用的套接字地址结构是sockaddr。IPv4套接字地址结构是定义在头文件<netinet/in.h>中的sockaddr_in,其POSIX定义例如以下:

struct in_addr{
in_addr_t s_addr; /*32-bit IPv4 address*/
}; /*network byte ordered*/
struct sockaddr_in{
unit8_t sin_len; /*length of structure(16)*/
sa_family_t sin_family; /*AF_INET*/
in_port_t sin_port; /*16-bit TCP or UDP port number*/
/*network byte ordered*/
struct in_addr sin_addr; /*32-bit IPv4 address, network byte ordered*/
char sin_zero[8];
};

POSIX规范仅仅须要结构中的3个字段:sin_family、sin_addr和sin_port。

它们的数据类型也都在上面给出了。

注意:32位IPv4地址存在两种不同的訪问方法。举例来说,假设serv定义为某个网络套接字地址结构,那么serv.sin_addr将按in_addr结构引用当中的32位IPv4地址。而serv.sin_addr.s_addr将按in_addr_t(一般是一个无符号的32位整数)引用同一个32位IPv4地址。因此,必须正确使用IPV4地址。尤其是将它作为函数的參数时。由于编译器对传递结构和传递整数的处理时全然不同的。

通用套接字地址结构sockaddr定义在<sys/socket.h>头文件里。

struct sockaddr{
unit8_t sa_len; /*address family:AF_xxx value*/
sa_family_t sa_family; /*protocol-specific address*/
};

套接字函数被定义为以指向通用套接字地址结构的指针作为參数。如int bind(int, struct sockaddr *, socklen_t);因此。调用这些函数时必须将指向特定于协议的套接字地址结构的指针进行强制类型转换,变成指向某个通用套接字地址结构的指针。如struct
sockaddr_in serv;  bind(sockfd, (struct sockaddr *)&serv, sizeof(serv));该技巧差点儿用在了全部套接字函数中,一定要熟练掌握。

套接字地址结构有5种:IPv4、IPv6、Unix域、数据链路和存储。



地址转换函数,在ASCII字符串与网络字节序的二进制值之间转换网际地址。

inet_aton、inet_addr(已经不用了)和inet_ntoa在点分十进制数串(比如“206.168.112.96”)与它长度为32位的网络字节序二进制值间转换IPv4地址。而inet_pton和inet_ntop对于IPv4和IPv6地址都适用。

#include <arpa/inet.h>
int inet_aton(const char *strptr, struct in_addr *addrptr); /*返回:若字符串有效则为1,否则为0*/
char *inet_ntoa(struct in_addr inaddr); /*返回:指向一个点分十进制数串的指针*/

inet_aton将strptr所指字符串转换为一个32位的网络字节序二进制值,并通过指针addrptr来存储。若成功为1,否则为0。inet_ntoa函数将一个32位的网络字节序二进制IPv4地址转换成对应的点分十进制数串,由该函数的返回值所指向的字符串驻留在静态内存中。注意,该函数以一个结构而不是该结构的指针作为其參数。

#include <arpa/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);
const char *inet_ntop(int family, const void *addrptr, char *strptr, size_t len);

两个函数的family參数既能够是AF_INET,也能够是AF_INET6。假设以不被支持的地址族作为family參数。这两个函数就返回一个错误,并将errno置位EAFNOSUPPORT。

第一个函数尝试转换由strptr指针所指的字符串,并通过addrptr指针存放二进制结果。若成功则返回值为1。否则假设对所指定的family而言输入的字符串不是有效的表达格式,返回0。inet_ntop进行相反的转换,从数值格式转换为表达式。



既然上面提到了网络字节序,那么就要说一说网络字节序与主机字节序的差别。

内存中存储两个字节有两种方法:一种是将迪许字节存储在起始地址,称为小端字节序;还有一种是将高端字节存储在起始地址。称为大端字节序。

主机字节序是系统所用的字节序,能够用下列程序測试:

#include <stdio.h>
#include <stdlib.h> int
main(int argc, char **argv)
{
union{
short s;
char c[sizeof(short)];
}un; un.s = 0x0102;
if(sizeof(short) == 2){
if(un.c[0] == 1 && un.c[1] == 2)
printf("big-endian\n");
else if(un.c[0] == 2 && un.c[1] == 1)
printf("little-endian\n");
else
printf("unkonwn\n");
}else
printf("sizeof(short) = %d\n", sizeof(short));
exit(0);
}

网络字节序和主机字节序之间相互转换的函数例如以下:

#include <netinet/in.h>

unit16_t htons(unit16_t host16bitvalue);

unit32_t htons(unit32_t host32bitvalue);/*返回网络字节序的值*/

unit16_t ntohs(unit16_t net16bitvalue); 

unit32_t ntohs(unit32_t net32bitvalue); /*返回主机字节序的值*/

应该把s视为一个16位的值(比如TCP或UDPport号)。把l视为一个32位的值(比如IPv4地址);

除了协议首部中各个字段的字节序问题外,还有网络分组中所含数据的字节序问题。



补充:字节操纵函数

操纵多字节字段的函数有两组:它们既不正确数据做解释,也不如果数据是以空字符结束的字符串。

#include <string.h>

void bzero(void *dest, size_t nbytes);

void bcopy(const void *src, void *dest, size_t bytes);

int bcmp(const void *ptr1, const void *ptr2, size_t nbytes);

#include <string.h>

void *memset(void *dest, int c, size_t len);

void *memcpy(void *dest, const void *src, size_t nbytes);

int memcmp(const void *ptr1, const void *ptr2, size_t nbytes);