当前位置: 首页 > news >正文

《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列

《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列

《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列

分配给套接字的IP地址和端口号

IP是为收发网络数据而分配给计算机的值。端口号是为区分程序中创建的套接字而分配给套接字的序号。

网络地址

IP地址有两种表达形式:

  • IPv4:4字节地址族
  • IPv6:16字节地址族

一定要记住IPv4和IPv6不只是在地址长度不同,在其具体的协议实现上是有很大程度的不同的。IPv6出现的主要目的是为了解决由于计算机数量的暴增导致IP地址可能出现不足的问题的,现在IPv6的地址范围可以让地球上任何一个沙子都拥有IP地址。

让我们继续说回到IPv4上,IPv4标准的4字节IP地址分为网络地址和主机(指计算机)地址,且分为A、B、C、D、E等类型。

类型地址范围网络地址位数主机地址位数可分配的网络数量每个网络可分配的主机数量
A1.0.0.0 - 126.255.255.25582412816,777,216
B128.0.0.0 - 191.255.255.255161616,38465,536
C192.0.0.0 - 223.255.255.2552482,097,152256
D224.0.0.0 - 239.255.255.255未分配未分配未分配未分配

网络地址是用来标识一个特定网络的。它告诉路由器和其他网络设备,数据应该被发送到哪个网络。

主机地址是用来标识网络中具体设备的。它必须在网络中是唯一的,以确保数据能够被正确地发送到正确的设备。

现在我来举个例子来具体理解一下网络地址和主机地址的含义。
假设向WWW.SEMI.COM公司传输数据,该公司内部构建了局域网,把所有计算机连接起来。因此,首先应向SEMI.COM网络传输数据,也就是说,并非一开始就浏览所有4字节IP地址,进而找到目标主机;而是仅浏览4字节IP地址的网络地址,先把数据传到SEMI.COM的网络。SEMI.COM网络(构成网络的路由器)接收到数据后,浏览传输数据的主机地址(主机ID)并将数据传给目标计算机。

网络地址分类和主机地址边界

只需通过地址的第一个字节即可判断网络地址占用的字节数,因为我们根据地址的边界区分网络地址,如下所示:

  • A类地址的首字节范围:0~127
  • B类地址的首字节范围:128-191
  • C类地址的首字节范围:192~223

还有如下这种表述方式:

  • A类地址的首位以0开始
  • B类地址的前2位以10开始
  • C类地址的前3位以110开始

正因如此,通过套接字收发数据时,数据传到网络后即可轻松找到正确的主机。

用于区分套接字的端口号

端口号是计算机为了区分程序中创建的不同套接字,而分配给套接字的序号,由16位组成,端口号唯一,可配分的范围在0~ 65535,其中0~10223是知名端口,一般分配给特定应用程序,所以应当分配范围之外的值。

端口号与套接字是一一对应关系,端口号与程序的不同通信功能是一一对应关系。

TCP套接字和UDP套接字不会共用端口号,所以允许重复。

数据传输过程示例

下面是基于IP地址的数据传输过程图:

在这里插入图片描述

主要步骤:

  1. 主机向203.211.217.202和203.211.172.103传输数据。
  2. 其中203.211.217和203.211.172是网络ID,通过网络ID可以把数据传输到指定的网络(路由器或交换机)。
  3. 202和103是主机ID,网络(路由器或交换机)通过主机ID将数据传输到指定的设备上。
  4. 操作系统收到数据后,根据数据包里的端口号,将数据传输到对应的程序上。

地址信息的表示

应用程序中使用的IP地址和端口号以结构体的形式给出了定义。

表示IPv4地址的结构体

此结构体将作为地址信息传递给bind函数。

struct sockaddr_in
{sa_family_t sin_family;  // 地址族uint16_t sin_port;       // 16位TCO/UDP端口号struct in_addr sin_addr; // 32位IP地址char sin_zero[8];        // 不使用
};

该结构体中提到的另一个结构体 in_addr 定义如下,它用来存放32位IP地址。

struct in_addr
{in_addr_t s_addr; // 32位IPv4地址
};

这些数据类型可以参考 POSIX,它是为 UNIX 系列操作系统设立的标准,它定义了一些其他数据类型,如下表所示:

数据类型名称数据类型说明声明的头文件
int8_tsigned 8-bit intsys/types.h
uint8_tunsigned 8-bit int (unsigned char)sys/types.h
int16_tsigned 16-bit intsys/types.h
uint16_tunsigned 16-bit int(unsigned short)sys/types.h
int32_tsigned 32-bit intsys/types.h
uint32_tunsigned 32-bit int(unsigned long)sys/types.h
sa_family_t地址族sys/socket.h
socklen_t长度sys/socket.h
in_addr_tIP地址,声明为uint32_tnetinet/in.h
in_port_t端口号,声明为uint16_tnetinet/in.h

看到这么长的类型表,有人不禁会问,为什么要搞出这么长的类型名呢?

其中一个很大的原因就是移植性的问题,如果适用于一个32位计算机的代码搬到64位的计算机上运行可定会出现由于位数不同导致的int被解释为不同的字节大小,这种问题是万万不可发生的。因此如果使用int32_t类型的数据,就能保证任何时候都占用4字节,即使转到不同字节的计算机上。

结构体sockaddr_in的成员分析

成员sin_family

每种协议族适用的地址族均不同。比如,IPv4使用4字节地址族,IPv6使用16字节地址族。

地址族(Address Family)含义
AF_INETIPv4网络协议中使用的地址族

AF_INET6 |IPv6网络协议中使用的地址族
AF_LOCAL| 本地通信中采用的UNIX协议的地址族

AF_LOCAL是为了说明具有多种地址族而添加的。

成员sin_port

该成员保存16位端口号,重点在于,它以网络字节序保存。

成员sin_addr

该成员保存32位地址信息,且也以网络字节序保存。为理解好该成员,应同时观察结构体in_addr。但结构体in_addr明为uint32_t,因此只需当作32位整数型即可。

成员sin_zero

无特殊含义。只是为使结构体sockaddr_in的大小和sockaddr结构体保持一致而插入的成员。必需填充为0,否则无法得到想要的结果。后面会另外讲解sockaddr。

从之前介绍的代码也可看出,sockaddr_in结构体变量地址值将以如下方式传递给bind函数。稍后将给出关于bind函数的详细说明,希望各位重点关注参数传递和类型转换部分的代码。

struct sockaddr_in serv_addr;if(bind(serv_sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)error_handling("bind() error");

此处重要的是第二个参数的传递。实际上,bind函数的第二个参数期望得到sockaddr结构体变量地址值包括地址族、端口号、IP地址等。

struct sockaddr
{sa_family_t sin_family; // 地址族char sa_data[14];       // 地址信息
};

这个结构体结构相对于sockaddr_in来说,它将后三个成员都放入sa_data之中。而这对于包含地址信息非常麻烦,继而有了新的结构体sockaddr_in。但是最后还是要转换为sockaddr型的结构体变量,再传递给bind函数即可。

网络字节序与地址变换

字节序与网络字节序

CPU内存保存数据有两种方式:

  • 大端序: 高位字节存放到低位地址
  • 小端序: 高位字节存放到高位地址

示例:

在这里插入图片描述

0x1234567中,0x12是最高位字节,0x67是最低位字节,大端序中先保存最高位。

在这里插入图片描述

0x1234567中,0x12是最高位字节,0x67是最低位字节,小端序中先保存最低位。

0x12和0x34构成的大端序系统值与0x34和0x12构成的小端序系统值相同。换言之,只有改变数据保存顺序才能被识别为同一值。

如果大端序系统传输数据0x1234时未考虑字节序问题,而直接以0x12、0x34的顺序发送。结果接收端以小端序方式保存数据,因此小端序接收的数据变成0x3412,而非0x1234,就会出现问题。

正因如此,在通过网络传输数据时约定统一方式,这种约定称为网络字统一节序(Network Byte Order),非常简单——统一为大端序。因此,所有计算机接受数据时应识别该数据时网络字节格式,小端序系统传输数据时应转换为大端序的排列方式。

字节序转换

为了统一标准,在网络传输前,得先把主机数据数组转化为大端序的网络字节序格式,下面是四种转换字节序的函数:

unsigned short htons(unsigned short);
unsigned short ntohs(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);

s指short,l指long,h指主机(host)字节序,n指网络(network)字节序。

因此,htons指,把short类型数据从主机字节序转换为网络字节序;ntohl指,把long类型数据从网络字节序转换为主机字节序。

示例程序:

#include <stdio.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{unsigned short host_port = 0x1234;unsigned short net_port;unsigned long host_addr = 0x12345678;unsigned long net_addr;net_port = htons(host_port);net_addr = htonl(host_addr);printf("Host ordered port: %#x \n", host_port);printf("Network ordered port: %#x \n", net_port);printf("Host ordered address: %#lx \n", host_addr);printf("Network ordered address: %#lx \n", net_addr);return 0;
}

Intel 和 AMD 系列的 CPU 都采用小端序标准。

网络地址的初始化与分配

将字符串信息转换为网络字节序的整数型

sockaddr_in中保存地址信息的成员为32位整数型。因此,为了分配IP地址,需要将其表示为32位整数型数据。

对于IP地址的表示,我们熟悉的是点分十进制表示法(Dotted Decimal Notation),而非整数型数据表示法。幸运的是,有个函数会帮我们将字符串形式的IP地址转换成32位整数型数据。此函数在转换类型的同时进行网络字节序转换。

#include<arpa/inet.h>in_addr_t inet_addr(const char *cp);

成功时返回32位大端序整数型值,失败时返回INADDR_NONE。

参数 cp 是一个指向包含 IPv4 地址点分十进制字符串的字符指针,例如 “192.168.1.1”。inet_addr函数会将其转换为32位整数型数据并返回。当然,该整数型值满足网络字节序。另外,该函数的返回值类型in_addr_t在内部声明为32位整数型。下列示例表示该函数的调用过程。

#include <stdio.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{char *addr1 = "1.2.3.4";char *addr2 = "1.2.3.256";unsigned long conv_addr = inet_addr(addr1);if (conv_addr == INADDR_NONE)printf("Error occurred! \n");elseprintf("Network ordered integer addr: %#lx \n", conv_addr);conv_addr = inet_addr(addr2);if (conv_addr == INADDR_NONE)printf("Error occurred! \n");elseprintf("Network ordered integer addr: %#lx \n\n", conv_addr);return 0;
}

从运行结果可以看出,inet_addr函数不仅可以把IP地址转成32位整数型,而且可以检测无效的IP地址。另外,从输出结果可以验证确实转换为网络字节序。

inet_aton函数与inet_addr函数在功能上完全相同,也将字符串形式IP地址转换为32位网络字节序整数并返回。只不过该函数利用了in_addr结构体,且其使用频率更高。

#include <arpa/inet.h>int inet_aton(const char * string, struct in_addr * addr);

成功时返回1(true),失败时返回0(false)。

参数:

  • string:需转换的IP地址信息的字符串指针
  • 将保存转换结果的in_addr结构体变量的指针

通过以下示例来了解inet_aton函数调用过程。

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>void error _handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[])
{char *addr = "127.232.124.79";struct sockaddr_in addr_inet;if (!inet_aton(addr, &addr_inet.sin_addr))error _handling("Conversion error");elseprintf("Network ordered integer addr: %#x \n", addr_inet.sin_addr.s_addr);return 0;
}

下面再介绍一个做的功能完全和上述函数相反的函数:

#include <arpa/inet.h>char * inet_ntoa(struct in_addr adr);

成功时返回转换的字符串地址值,失败时返回-1。

该函数将通过参数传入的整数型IP地址转换为字符串格式并返回。但调用时需小心,返回值类型为char指针。返回字符串地址意味着字符串已保存到内存空间,但该函数未向程序员要求分配内存,而是在内部申请了内存并保存了字符串。也就是说,调用完该函数后,应立即将字符串信息复制到其他内存空间。因为,若再次调用inet_ntoa函数,则有可能覆盖之前保存的字符串信息。总之,再次调用inet_ntoa函数前返回的字符建地此值是有效的。若需要长期保存,则应将字符串复制到其他内存空间。

下面给出上述函数调用示例:

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>int main(int argc, char *argv[])
{struct sockaddr_in addr1, addr2;char *str_ptr;char str_arr[20];addr1.sin_addr.s_addr = htonl(0x1020304);addr2.sin_addr.s_addr = htonl(0x1010101);str_ptr = inet_ntoa(addr1.sin_addr);strcpy(str_arr, str_ptr);printf("Dotted-Decimal notation1: %s \n", str_ptr);inet_ntoa(addr2.sin_addr);printf("Dotted-Decimal notation2: %s \n", str_ptr);printf("Dotted-Decimal notation3: %s \n", str_arr);return 0;
}

网络地址初始化

常用的初始化方法:

    struct sockaddr_in addr;char *serv_ip = "211.217.168.13";char *serv_port = "9190";memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr(serv_ip);addr.sin_port = htons(atoi(serv_port));

客户端地址信息初始化

客户端声明sockaddr_in结构体,初始化为要与之连接的服务器端套接字的IP和端口号,然后调用connect函数。

INADDR_ANY

每次创建服务器端套接字都要输入IP地址会有些繁琐,此时可如下初始化地址信息。

    struct sockaddr_in addr;// char *serv_ip = "211.217.168.13";char *serv_port = "9190";memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = htonl(INADDR_ANY);addr.sin_port = htons(atoi(serv_port));

与之前方式最大的区别在于,利用常数INADDR_ANY分配服务器端的IP地址。若采用这种方式,则可自动获取运行服务器端的计算机IP地址,不必亲自输入。而且,若同一计算机中已分配多个IP地址(多宿主(Multi-homed)计算机,一般路由器属于这一类),则只要端口号一致就可以从不同IP地址接收数据。因此,服务器端中优先考虑这种方式。而客户端中除非带有一部分服务器端功能,否则不会采用。

向套接字分配网络地址

既然已讨论了sockaddr_in结构体的初始化方法,接下来就把初始化的地址信息分配给套接字。bind函数负责这项操作。

#include <sys/socket.h>int bind(int sockfd, struct sockaddr * myaddr, socklen_t addrlen);

成功时返回0,失败时返回-1。

参数:

  • sockfd:要分配地址信息(IP地址和端口号)的套接字文件描述符。
  • myaddr:存有地址信息的结构体变量地址值。
  • addrlen:第二个结构体变量的长度。

如果此函数调用成功,则将第二个参数指定的地址信息分配给第一个参数中的相应套接字。

基于Windows的实现

函数htons、htonl在Windows中的使用

这些函数的用法与Linux平台下的使用没有区别。

#include <stdio.h>
#include <winsock2.h>void ErrorHanding(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;unsigned short host_port = 0x1234;unsigned short net_port;unsigned long host_addr = 0x12345678;unsigned long net_addr;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHanding("WSAStartup() error!");net_port = htons(host_port);net_addr = htonl(host_addr);printf("Host ordered port: %#x \n", host_port);printf("Network ordered port: %#x \n", net_port);printf("Host ordered address: %#lx \n", host_addr);printf("Network ordered address: %#lx \n", net_addr);WSACleanup();return 0;
}

运行结果:

C:\Users\81228\Documents\Program\TCP IP Project\Chapter 3>gcc endian_conv_win.c -lwsock32 -o endian_convC:\Users\81228\Documents\Program\TCP IP Project\Chapter 3>endian_conv
Host ordered port: 0x1234
Network ordered port: 0x3412
Host ordered address: 0x12345678
Network ordered address: 0x78563412

函数inet_addr、inet_ntoa在Windows中的使用

测试程序:

#include <stdio.h>
#include <string.h>
#include <winsock2.h>void ErrorHanding(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHanding("WSAStartup() error!");/* inet_addr 函数调用示例 */{char *addr = "127.212.124.78";unsigned long conv_addr = inet_addr(addr);if (conv_addr == INADDR_NONE)printf("Error occurred! \n");elseprintf("Network ordered integer addr: %#lx \n", conv_addr);}/* inet_ntoa 函数调用示例 */{struct sockaddr_in addr;char *strPtr;char strArr[20];addr.sin_addr.s_addr = htonl(0x1020304);strPtr = inet_ntoa(addr.sin_addr);strcpy(strArr, strPtr);printf("Dotted-Decimal notation3 %s\n", strArr);}WSACleanup();return 0;
}

Windows中不存在inet_aton函数。

运行结果:

C:\Users\81228\Documents\Program\TCP IP Project\Chapter 3>gcc inet_adrconv_win.c -lwsock32 -o inet_adrconvC:\Users\81228\Documents\Program\TCP IP Project\Chapter 3>inet_adrconv
Network ordered integer addr: 0x4e7cd47f
Dotted-Decimal notation3 1.2.3.4

与Linux完全相同。下面是示例程序:

SOCKET hSocket;
SOCKADDR_IN serverAddr;
char *serverPort = "9190";// 创建服务端套接字
hSocket = socket(PF_INET, SOCK_STREAM, 0);// 地址信息初始化
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr(INADDR_ANY);
serverAddr.sin_port = htons(atoi(serverPort));// 分配地址信息
bind(hServerSock, (struct sockaddr *)&serverAddr, sizeof(serverAddr))

WSAStringToAddress & WSAAddressToString

下面介绍Winsock2中增加的2个转换函数。它们在功能上和inet_ntoa和inet_addr全相同,但优点在于只持多种协议,在IPv4和IPv6中均可适用。当然它们也有缺点,使用inet_ntoa、inet_addr可以很容易地在Linux和Windows之间切换程序。而将要介绍的这2个函数则依赖于特定平台,会降低兼容性。

#include <winsock2.h>INT WSAStringToAddress(LPTSTR AddressString, INT AddressFamily, LPWSAPROTOCOL_INFO lpProtocolInfo,LPSOCKADDR lpAddress, LPINT lpAddressLength
);

成功时返回0,失败时返回SOCKET_ERROR。

参数:

  • AddressString:含有IP和端口号的字符串地址值
  • AddressFamily:第一个参数中地址所属的地址族信息
  • lpProtocolInfo:设置协议提供者(Provider),默认为NULL
  • lpAddress:保存地址信息的结构体变量地址值
  • lpAddressLength:第四个参数中传递的结构体长度所在的变量地址值

上述函数中新出现的各种类型几乎都是针对默认数据类型的typedef声明。

WSAAddressToString与WSAStringToAddress在功能上正好相反,它将结构体中的地址信息转换成字符串形式。

#include <winsock2.h>INT WSAAddressToString(LPSOCKADDR lpsaAddress, DWORD dwAddressLength,LPWSAPROTOCOL_INFO lpProtocolInfo, LPSTR lpszAddressString,LPDWORD lpdwAddressStringLength);

成功时返回0,失败时返回 SOCKET_ERROR。

参数:

  • lpsaAddress:需要转换的地址信息结构体变量地址值
  • dwAddressLength:第一个参数中结构体的长度
  • lpProtocolInfo:设置协议提供者,默认为NULL
  • lpszAddressString:保存转换结果的字符串地址值
  • lpdwAddressStringLength:第四个参数中存有地址信息的字符串长度

以下是这两个函数的示例:

#undef UNICODE
#undef _UNICODE
#include <stdio.h>
#include <winsock2.h>int main(int argc, char *argv[])
{char *strAddr = "203.211.218.102:9190";char strAddrBuf[50];SOCKADDR_IN servAddr;int size;WSADATA wsaData;WSAStartup(MAKEWORD(2, 2), &wsaData);size = sizeof(servAddr);WSAStringToAddress(strAddr, AF_INET, NULL, (SOCKADDR *)&servAddr, &size);size = sizeof(strAddrBuf);memset(strAddrBuf, 0, sizeof(strAddrBuf));WSAAddressToString((SOCKADDR *)&servAddr, sizeof(servAddr), NULL, strAddrBuf, (LPDWORD)&size);printf("Second conv result: %s \n", strAddrBuf);WSACleanup();return 0;
}

注意:WSAAddressToString API 的最后一个参数是 unsigned long 类型的指针,要显式转换。

用命令行编译程序:gcc conv_addr_win.c -lwsock32 -o conv_addr

遇到报错:

C:\WINDOWS\TEMP\ccjFwpSG.o:conv_addr_win.c:(.text+0x7a): undefined reference to `__imp_WSAStringToAddressA'
C:\WINDOWS\TEMP\ccjFwpSG.o:conv_addr_win.c:(.text+0xd2): undefined reference to `__imp_WSAAddressToStringA'
collect2.exe: error: ld returned 1 exit status

错误解释:

这个错误表示链接器无法找到__imp_WSAStringToAddressA函数的定义。WSAStringToAddressA是Windows Sockets API中的一个函数,用于将一个IP地址字符串转换为一个sockaddr结构。在使用了WSAStringToAddressA函数的程序中,如果没有正确地链接Winsock库,就会出现这个错误。

解决方法:

确保你的程序链接了正确的Winsock库。在使用GCC或类似的编译器时,可以通过在编译命令中加入-lws2_32来链接Winsock库。

运行结果:

C:\Users\81228\Documents\Program\TCP IP Project\Chapter 3>gcc conv_addr_win.c -o conv_addr -lws2_32C:\Users\81228\Documents\Program\TCP IP Project\Chapter 3>conv_addr
Second conv result: 203.211.218.102:9190

习题

(1)IP地址族IPv4和IPv6有何区别?在何种背景下诞生了IPv6?

IPV4是4字节地址族,IPV6是16字节地址族。

IPV6的诞生是为了应对2010年前后IP地址耗尽的问题而提出的标准。

(2)通过IPV4网络ID、主机ID及路由器的关系说明向公司局域网中的计算机传输数据的过程。

向目标主机传输数据,首先向目标IP所属的网络传输数据。此时使用的是IP地址中的网络ID。数据传到路由器,路由器将参照IP地址的主机号查找路由表,从对应的网口发送数据,数据经过路由器转发最终送到目标主机。

(3)套接字地址分为IP地址和端口号。为什么需要IP地址和端口号?或者说,通过IP可以区分哪些对象?通过端口号可以区分哪些对象?

IP地址是为了区分网络上的主机。端口号是区分同一主机下的不同的socket,以确保程序进程都能准确收发数据。

(4)请说明IP地址分类方法,并说出下面这些IP地址的分类。

214.121.212.102 - C类

120.101.122.89 - A类

129.78.102.211 - B类

在这里插入图片描述

(5)计算机通过路由器或交换机连接到互联网。请说出路由器和交换机的作用。

路由器:是连接两个或多个网络的硬件设备,在网络间起网关的作用,是读取每一个数据包中的地址然后决定如何传送的专用智能性的网络设备。(作用于网络层,寻址,转发(依靠 IP 地址))

交换机:是一种用于电(光)信号转发的网络设备。(一般作用于链路层,过滤,转发(依靠 MAC 地址))

(6)什么是知名端口?其范围是多少?知名端口中具有代表性的HTTP和FTP端口号各是多少?

知名端口(Well-known PROT)是指预定分配给特定操作的端口。其范围是0~1023。

HTTP:80。

FTP:21。

(7)bind函数

bind函数原型:

int bind(SOCKET s, const struct sockaddr *name, int namelen);

调用:

bind(hServerSock, (struct sockaddr *)&serverAddr, sizeof(serverAddr))

为什么bind中第二个参数是sockaddr,但是传入的是sockaddr_in?

答:

bind函数第二个参数类型sockaddr结构体,很难分配IP地址和端口号的空间大小(因为结构体sockaddr并非只为IPv4设计,所以要兼容),所以sockaddr结构体内将IP地址和端口号合并到一起,用一个成员 sa_data 表示。因此对于不同类型网络的IP地址和PORT号的分配是可先通过sockaddr_in完成,之后再强制转换成sockaddr类型(因为两个结构体的长度相同,都是16字节,强制转换类型时不会丢失字节,也没有多余的字节)。

(8)请解释大端序、小端序、网络字节序,并说明为何需要网络字节序。

小端序是把高位字节存储到高位地址上;大端序是把高位字节存储到低位地址上。因为保存栈的方式有差异,所以对网络传输数据的过程制定了标准,这就是“网路字节序”。而且,在网络字节序中,数据传输的标准是“大端序”。

(9)大端计算机希望将4字节整型数据12传到小端序计算机。请说出数据传输过程中发生的字节序变换过程。

因为网络字节序的顺序标准是“大端序”,所以大端序的计算机在网络传输中不需要先转换字节顺序,直接传输。但是接受数据的是小端序计算机,因此,要经过网络转本地序的过程,再保存到存储设备上。

(10)怎么表示回送地址?其含义是什么?如果向回送地址传输数据将会发生什么情况?

127.0.0.1 / localhost。

回送地址表示计算机本身。

因此,如果将数据传送到IP地址127.0.0.1,数据不进行网络传输而是直接返回。

相关文章:

《TCP/IP网络编程》学习笔记 | Chapter 3:地址族与数据序列

《TCP/IP网络编程》学习笔记 | Chapter 3&#xff1a;地址族与数据序列 《TCP/IP网络编程》学习笔记 | Chapter 3&#xff1a;地址族与数据序列分配给套接字的IP地址和端口号网络地址网络地址分类和主机地址边界用于区分套接字的端口号数据传输过程示例 地址信息的表示表示IPv4…...

C++ | Leetcode C++题解之第546题移除盒子

题目&#xff1a; 题解&#xff1a; class Solution { public:int dp[100][100][100];int removeBoxes(vector<int>& boxes) {memset(dp, 0, sizeof dp);return calculatePoints(boxes, 0, boxes.size() - 1, 0);}int calculatePoints(vector<int>& boxes…...

day05(单片机)SPI+数码管

目录 SPI数码管 SPI通信 SPI总线介绍 字节交换原理 时序单元 ​​​​​​​SPI模式 模式0 模式1 模式2 模式3 数码管 介绍 74HC595芯片分析 ​​​​​​​原理图分析 ​​​​​​​cubeMX配置​​​​​​​ 程序编写 硬件SPI ​​​​​​​软件SPI 作业&#xff1a; SPI数…...

Android Framework AMS(13)广播组件分析-4(LocalBroadcastManager注册/注销/广播发送处理流程解读)

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要解读广播组件的广播发送过程。关注思维导图中左上侧部分即可。 有了前面普通广播组件 注册/注销程/广播组件的发送广播流程分析的基础&…...

模糊理论与模糊集概述

1. 模糊集 1️⃣ μ A : U → [ 0 , 1 ] \mu_A:U\to{[0,1]} μA​:U→[0,1]&#xff0c;将任意 u ∈ U u\in{}U u∈U映射到 [ 0 , 1 ] [0,1] [0,1]上的某个函数 模糊集&#xff1a; A { μ A ( u ) , u ∈ U } A\{\mu_A(u),u\in{}U\} A{μA​(u),u∈U}称为 U U U上的一个模糊集…...

基于STM32的实时时钟(RTC)教学

引言 实时时钟&#xff08;RTC&#xff09;是微控制器中的一种重要功能&#xff0c;能够持续跟踪当前时间和日期。在许多应用中&#xff0c;RTC用于记录时间戳、定时操作等。本文将指导您如何使用STM32开发板实现RTC功能&#xff0c;通过示例代码实现当前时间的读取和显示。 环…...

Caffeine Cache解析(三):BoundedBuffer 与 MpscGrowableArrayQueue 源码浅析

接续 Caffeine Cache解析(一)&#xff1a;接口设计与TinyLFU 接续 Caffeine Cache解析(二)&#xff1a;drainStatus多线程状态流转 BoundedBuffer 与 MpscGrowableArrayQueue multiple-producer / single-consumer 这里multiple和single指的是并发数量 BoundedBuffer: Caf…...

全双工通信协议WebSocket——使用WebSocket实现智能学习助手/聊天室功能

一.什么是WebSocket&#xff1f; WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器的全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c;并进行双向数据传输 HTTP 协议是一种无状态的、无连接的、单向的应用…...

Rust-Trait 特征编程

昨夜江边春水生&#xff0c;艨艟巨舰一毛轻。 向来枉费推移力&#xff0c;此日中流自在行。 ——《活水亭观书有感二首其二》宋朱熹 【哲理】往日舟大水浅&#xff0c;众人使劲推船&#xff0c;也是白费力气&#xff0c;而此时春水猛涨&#xff0c;巨舰却自由自在地飘行在水流中…...

彻底理解哈希表(HashTable)结构

目录 介绍优缺点概念哈希函数快速的计算键类型键转索引霍纳法则 均匀的分布 哈希冲突链地址法开放地址法线性探测二次探测再哈希法 扩容/缩容实现哈希创建哈希表质数判断哈希函数插入&修改获取数据删除数据扩容/缩容函数全部代码 哈希表&#xff08;Hash Table&#xff09;…...

微信小程序的汽车维修预约管理系统

文章目录 项目介绍具体实现截图技术介绍mvc设计模式小程序框架以及目录结构介绍错误处理和异常处理java类核心代码部分展示详细视频演示源码获取 项目介绍 系统功能简述 前台用于实现用户在页面上的各种操作&#xff0c;同时在个人中心显示各种操作所产生的记录&#xff1a;后…...

LeetCode:3255. 长度为 K 的子数组的能量值 II(模拟 Java)

目录 3255. 长度为 K 的子数组的能量值 II 题目描述&#xff1a; 实现代码与解析&#xff1a; 模拟 原理思路&#xff1a; 3255. 长度为 K 的子数组的能量值 II 题目描述&#xff1a; 给你一个长度为 n 的整数数组 nums 和一个正整数 k 。 一个数组的 能量值 定义为&am…...

深入了解逻辑回归:机器学习中的经典算法

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

软件测试基础十三(python 函数)

函数 1. 函数的意义 代码复用 提高效率&#xff1a;Python中的函数允许将一段可重复使用的代码封装起来。例如&#xff0c;在一个数据分析项目中&#xff0c;可能需要多次计算一组数据的平均值。可以将计算平均值的代码定义为一个函数&#xff1a; def calculate_average(nu…...

计算机网络——HTTP篇

基础篇 IOS七层网络模型 TCP/IP四层模型&#xff1f; 应⽤层&#xff1a;位于传输层之上&#xff0c;主要提供两个终端设备上的应⽤程序之间的通信&#xff0c;它定义了信息交换的格式&#xff0c;消息会交给下⼀层传输层来传输。 传输层的主要任务就是负责向两台设备进程之间…...

信息化运维方案,实施方案,开发方案,信息中心安全运维资料(软件资料word)

1 编制目的 2 系统运行维护 2.1 系统运维内容 2.2 日常运行维护方案 2.2.1 日常巡检 2.2.2 状态监控 2.2.3 系统优化 2.2.4 软件系统问题处理及升级 2.2.5 系统数据库管理维护 2.2.6 灾难恢复 2.3 应急运行维护方案 2.3.1 启动应急流程 2.3.2 成立应急小组 2.3.3 应急处理过程 …...

自动化工具 Gulp

自动化工具 gulp 摘要 概念&#xff1a;gulp用于自动化开发流程。 理解&#xff1a;我们只需要编写任务&#xff0c;然后gulp帮我们执行 核心概念&#xff1a; 任务&#xff1a;通过定义不同的任务来组织你的构建流程。 管道&#xff1a;通过管道方式将文件从一个插件传递…...

css实现div被图片撑开

固定好盒子的宽度&#xff0c;高度随传过来的图片大小决定 <div class"tab-con"> <img:src"concertInfo.detail"alt""> </div>.tab-con {margin-bottom: 20px;width: 700px;img {width: 700px;height: auto;object-fit: cont…...

Power Pivot、Power BI 和 SQL Server Analysis Services 的公式语言:DAX(数据分析表达式)

DAX&#xff08;Data Analysis Expressions&#xff09;是一种用于 Power Pivot、Power BI 和 SQL Server Analysis Services 的公式语言&#xff0c;旨在帮助用户进行数据建模和复杂计算。DAX 的设计初衷是使数据分析变得简单而高效&#xff0c;特别是在处理数据模型中的表关系…...

大模型应用编排工具Dify二开之工具和模型页面改造

1.前言 简要介绍下 dify&#xff1a; ​ 一款可以对接市面上主流大模型的任务编排工具&#xff0c;可以通过拖拽形式进行编排形成解决某些业务场景的大模型应用。 背景信息&#xff1a; ​ 环境&#xff1a;dify-0.8.3、docker-21 ​ 最近笔者在做 dify的私有化部署和二次…...

Pytorch用BERT对CoLA、新闻组文本数据集自然语言处理NLP:主题分类建模微调可视化分析...

原文链接&#xff1a;https://tecdat.cn/?p38181 自然语言处理&#xff08;NLP&#xff09;领域在近年来发展迅猛&#xff0c;尤其是预训练模型的出现带来了重大变革。其中&#xff0c;BERT 模型凭借其卓越性能备受瞩目。然而&#xff0c;对于许多研究者而言&#xff0c;如何高…...

LightGBM-GPU不能装在WSL,能装在windows上

这是一篇经验总结文章&#xff0c;注重思路&#xff0c;忽略细节。 1.起因 用多个机器学习方法训练模型&#xff0c;比较性能&#xff0c;发现Light GBM方法获得的性能明显更高&#xff0c;但问题是在CPU上训练的速度特别特别慢&#xff0c;需要用GPU训练。 2.开始装LightGB…...

工业相机常用功能之白平衡及C++代码分享

目录 1、白平衡的概念解析 2、相机白平衡参数及操作 2.1 相机白平衡参数 2.2 自动白平衡操作 2.3 手动白平衡操作流程 3、C++ 代码从XML读取参数及设置相机参数 3.1 读取XML 3.2 C++代码,从XML读取参数 3.3 给相机设置参数 1、白平衡的概念解析 白平衡(White Balance)…...

Foundry 单元测试

安装 Foundry 如果你还没有安装 Foundry&#xff0c;请按照此处的说明进行操作&#xff1a;Foundry 安装 Foundry Hello World 只需运行以下命令&#xff0c;它将为你设置环境&#xff0c;创建测试并运行它们。&#xff08;当然&#xff0c;这假设你已经安装了 Foundry&…...

idea database连接数据库后看不到表解决方法、格式化sql快捷键

最下面那个勾选上就可以了 或 格式化sql快捷键&#xff1a; 先选中&#xff0c; 使用快捷键格式化 SQL&#xff1a; Windows/Linux: Ctrl Alt L macOS: Cmd Alt L...

【数学二】线性代数-向量-向量组的秩、矩阵得秩

考试要求 1、理解 n n n维向量、向量的线性组合与线性表示的概念. 2、理解向量组线性相关、线性无关的概念,掌握向量组线性相关、线性无关的有关性质及判别法. 3、了解向量组的极大线性无关组和向量组的秩的概念,会求向量组的极大线性无关组及秩. 4、了解向量组等价的概念,…...

ABAP开发-内存管理

系列文章目录 文章目录 系列文章目录前言一、概述二、程序间调用三、外部会话和内部会话四、SAP内存与ABAP内存五、实例总结 前言 一、概述 内存是程序之间为了传递数据而使用的共享存储空间&#xff0c;在每个程序里使用的内存有SAP内存和ABAP内存 SAP内存分类 SAP内存 主会…...

【Ajax】跨域

文章目录 1 同源策略1.1 index.html1.2 server.js 2 如何解决跨域2.1 JSONP2.1.1 JSONP 原理2.1.2 JSONP 实践2.1.3 jQuery 中的 JSONP 2.2 CORS2.2.1 CORS实践 3 server.js 1 同源策略 同源策略(Same-Origin Policy)最早由 Netscape 公司提出&#xff0c;是浏览器的一种安全策…...

yii 常用一些调用

yii 常用一些调用 (增加中) 调用YII框架中 jquery&#xff1a;Yii::app()->clientScript->registerCoreScript(‘jquery’); framework/web/js/source的js,其中registerCoreScript key调用的文件在framework/web/js/packages.php列表中可以查看 在view中得到当前contro…...

网页版五子棋——用户模块(服务器开发)

前一篇文章&#xff1a;网页版五子棋—— WebSocket 协议-CSDN博客 目录 前言 一、编写数据库代码 1.数据库设计 2.配置 MyBatis 3.创建实体类 4.创建 UserMapper 二、前后端交互接口 1.登录接口 2.注册接口 3.获取用户信息 三、服务器开发 1.代码编写 2.测试后端…...