Linux网络编程学习笔记 【1】

目录


注意:本文内容源于《Linux高性能服务器编程》 作者:游双

主机字节序和网络字节序

​    字节在内存中的排列顺序即为字节序,字节序分为大端字节序(big endian)和小端字节序(little endian)。大端字节是指一个整数的高位字节(23~31位)存储在内存的低地址,而低位字节(0~7位)存储在内存的高地址。小端字节序与大端字节序相反。下方函数可用于检测本机的字节序

#include <iostream>
using namespace std;


void bytesOrder();

int main() {
    bytesOrder();
    return 0;
}

void bytesOrder(){
    union{
        short value;
        char union_byte[sizeof(short)];
    }test;

    test.value = 0x0102;
    if(test.union_byte[0] == 0x01 && test.union_byte[1] == 0x02){
        cout<<"big endian\n";
    }
    else if(test.union_byte[0] == 0x02 && test.union_byte[1] == 0x01){
        cout<<"little endian\n";
    }
    else{
        cout<<"unknown endian\n";
    }
}

​    现代PC多采用小端字节,因此,小端字节又被称为主机字节。当两台主机彼此通信时,发送端总会把数据转换成大端字节再发送。因此接收端应当将收到的数据转换成本地的字节序,才能收到正确的数据。【大端字节序也被称为网络字节序】

​ 注意:即使是同一台主机的两个进程之间进行通信,也要考虑字节序的问题。

Linux提供了如下4个函数来实现主机字节序和网络字节序之间的转换:

#include <netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);

转换IP地址时常用长整型函数,转换端口时常用短整型函数。

通用socket地址

socket网络编程接口中表示socket地址的是结构体sockaddr,其定义如下:

#include <bits/socket.h>
struct sockaddr
{
    sa_family_t sa_family;
    char sa_data[14];
};

注意:sa可能是socket address的缩写

    sa_family_t是地址族类型的变量,地址族类型通常与协议类型对应,常见的协议族和对应的地址族见下表。

协议族 地址族 描述
PF_UNIX AF_UNIX UNIX本地协议族
PF_INET AF_INET TCP/IPV4协议族
PF_INET6 AF_INET6 TCP/IPV6协议族

​    宏PF_*和AF_*都定义在 bit/socket.h中,且后者与前者的值完全一样,所以二者通常混用。

    sockaddr.sa_data用于存放socket地址值,但是,不同协议族的地址值具有不同的含义和长度,如下表:

协议族 地址值含义和长度
PF_UNIX 文件路径名【长度可达到108字节】
PF_INET 16bit端口号和32bit IPv4地址,共6字节
PF_INET6 16bit端口号,32bit流标识,128bit IPv6地址,32bit范围ID,共26字节

​    由上表可见,14字节的sa_data根本无法容纳多数协议族的地址值,因此,Linux定义了下面的新的通用socket地址结构体

#include <bit/socket.h>
struct sockaddr_storage
{
    sa_family_t sa_family;
    unsigned long int __ss_align;
    char __ss_padding[128-sizeof(__ss_align)];
};

​    此通用socket地址结构体不仅提供了足够大的空间存放地址值,而且是内存对齐的。【__ss_align的对齐作用】

专用socket地址

​    上述的两个通用socket地址结构体难以使用,操作繁琐,因此,Linux为各个协议族提供了专用的socket地址结构体。

​ 注意:sin可能为socket internet的缩写

​ UNIX本地域协议族使用如下socket结构体:

#include <sys/un.h>
struct sockaddr_un
{
    sa_family_t sin_family; /*地址族 AF_UNIX*/    
    char sun_path[108];     /*文件路径名*/
};

​    TCP/IP协议族有sockaddr_in和sockaddr_in6两个专用socket地址结构体,它们分别用于IPV4和IPV6。

struct sockaddr_in
{
    sa_family_t sin_family; /*地址族 AF_INET*/
    u_int16_t sin_port;     /*端口号*/
    struct in_addr sin_addr;    /*IPV4地址结构体*/
};

struct in_addr
{
    u_int32_t s_addr;   /*IPV4地址,应使用网络字节序存储*/    
};

------------------------------------------------------------

struct sockaddr_in6
{
    sa_family_t sin6_family;    /*地址族 AF_INET6*/
    u_int16_t sin6_port;    /*端口号 【要以网络字节序表示】*/
    u_int32_t sin6_flowinfo;    /*流消息,应设置为0*/
    struct in6_addr sin6_addr;  /*IPV6地址*/
    u_int32_t sin6_scope_id;    /*scope ID,尚处于实验阶段*/
};

struct in6_addr
{
    unsigned char sa_addr[16]; /*IPV6地址,应以网络字节序表示*/
};

​    所有专用socket地址类型的变量在使用时都必须转化为通用socket地址类型sockaddr(强制转换即可)。因为所有socket编程接口使用的地址参数的类型都是sockaddr。

IP地址转换函数

​    人们习惯用可读性好的字符串表示IP地址,如使用点分十进制字符串表示IPV4地址,或者用16进制字符串表示IPV6地址。但编程时,我们需要先把它们转换为整数(二进制数)才能使用。而记录日志时正好相反,我们需要把整数表示的IP地址转换为可读的字符串。下列3个函数可用于点分10进制字符串表示的IPV4地址和用网络字节序整数表示的IPV4地址间的转换。

#include <arpa/inet.h>
in_addr_t inet_addr(const char* strptr);
int inet_aton(const char *cp, struct in_addr* inp);
char* inet_ntoa(struct in_addr in);

    inet_addr和inet_aton均可以将点分十进制字符表示的IPV4地址转换为网络字节序的整数表示的IPV4地址。

    inet_ntoa将以网络字节序表示的IPV4地址转换为点分十进制字符串表示的IPV4地址,成功返回1,失败返回0。注意,此函数在内部用一个静态变量存储转换结果,因此,此函数是不可重入的。当前转换的结果会覆盖上一次转换的结果。

#include <arpa/inet.h>
int inet_pton(int af, const char* src, void *dst);
const char* inet_ntop(int af, const void* src, char* dst, socklen_t cnt);

    inet_pton函数用于将字符串表示的IP地址src(用点分十进制字符串表示的IPV4地址或用16进制字符串表示的IPV6地址)转换为用网络字节序整数表示的IP地址,并把转换结果存储于dst指向的内存中。其中,af参数指定地址族,可以是AF_INET或AF_INET6,inet_pton成功时返回1,失败时返回0,并设置errno。
    inet_ntop函数进行相反的转换,前三个参数的含义与inet_pton相同,最后一个参数cnt指定目标存储单元的大小,下面的两个宏定义可以帮助我们指定这个大小【分别用于IPV4, IPV6】
注意:也许pton是指point to network

#include <netinet/in.h>
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46

    inet_ntop成功时返回目标存储单元的地址,失败则返回NULL并设置errno。

猜你喜欢

转载自blog.csdn.net/CreatorGG/article/details/81613233
今日推荐