《TCP/IP网络编程》(第三章)地址族和数据序列

ops/2024/10/16 2:28:20/

1.IP地址

IP地址是Internet Protocol(网络协议)的缩写,是为了收发网络数据而分配给计算机的值,分为两类:IPv4IPv6,差别主要是表示IP地址所用的字节数,前者用四字节地址族,后者用16字节地址族。IPv6是为了应对IPv4地址耗尽而提出的标准,2019 年 11 月 25 日已分配完公网 IPv4 地址,但本书还是以IPv4为例子进行展示。
在这里插入图片描述

下图展示IPv4地址族,分为A、B、C、D、E(忽略)等类型:
在这里插入图片描述
可以通过IP地址的第一个字节即可判断网络地址占用的字节数,进而区分网络地址的类型:

  1. A类地址:
    首字节范围:0到127
    首位:0
    网络ID占用:1个字节
    主机ID占用:3个字节
  2. B类地址:
    首字节范围:128到191
    首位:10
    网络ID占用:2个字节
    主机ID占用:2个字节
  3. C类地址:
    首字节范围:192到223
    首位:110
    网络ID占用:3个字节
    主机ID占用:1个字节

例如,如果一个IP地址的首字节是192(二进制为11000000),我们立即知道这是一个C类地址,网络ID占用3个字节,主机ID占用1个字节.

网络ID是用来标识一个特定网络的。它告诉路由器和其他网络设备,数据应该被发送到哪个网络
主机ID是用来标识网络中具体设备的。它必须在网络中是唯一的,以确保数据能够被正确地发送到正确的设备。

2.端口号

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

3.数据传输过程

下面是基于IP地址的数据传输过程图:
在这里插入图片描述

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

4.IPv4的地址结构体表示

结构体定义如下:

struct sockaddr_in ipv4_address {sa_family_t     sin_family;   // 地址族,对于IPv4通常是AF_INETin_port_t       sin_port;     // 端口号,网络字节序struct in_addr  sin_addr;     // IPv4地址,具体结构见下char            sin_zero[8];  // 填充字符,用于与sockaddr结构体兼容
};
//其中的struct 定义如下
struct in_addr {in_addr_t       s_addr; // 存储IPv4地址的32位整数,网络字节序
};

sin_family
表示地址族,对于IPv4地址,它通常被设置为AF_INET,具体分类如下:

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

sin_port
是一个网络字节序的端口号,用于标识特定的服务或应用程序。

sin_addr
是一个32位的无符号整数,用来存储IPv4地址,且必须以网络字节序(大端序)表示。

sin_zero
是一个填充字段,用于确保sockaddr_in结构体的大小与sockaddr结构体兼容,因为某些系统调用可能同时处理IPv4和IPv6地址。

5.网络字节序

字节序: 是指计算机处理器在内存中存储多字节数据类型(如整数、长整型、双精度浮点数等)时所采用的字节排列顺序。分为两种:

  1. 大端序: 高位字节存放到低位地址
    在这里插入图片描述
    0x1234567中,0x12是最高位字节,0x67是最低位字节,大端序中先保存最高位。

  2. 小端序: 高位字节存放到高位地址
    在这里插入图片描述
    0x1234567中,0x12是最高位字节,0x67是最低位字节,小端序中先保存最低位。

网络字节序:
是一种在互联网上进行通信时使用的字节序,它保证了不同计算机架构之间数据的一致性。网络字节序遵循大端序),即最高有效字节(MSB)存储在最低的内存地址处。

6.字节序转换

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

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类型数据从网络字节序转换为主机字节序

7.将字符串信息转化为网络字节序

因为在IPv4的结构体定义中,IPv4地址为32位整数型,所以我们可以通过inet_addr()函数进行转化,而且该函数还可以检测无效的IP地址,代码如下:

in_addr_t inet_addr(const char *cp);
//cp 是一个指向包含 IPv4 地址点分十进制字符串的字符指针,例如 "192.168.1.1"。

Linux系统下运行代码:

#include <arpa/inet.h> // 包含inet_addr函数的头文件
#include <stdio.h>int main() {const char *ip_str = "1.2.3.4"; // 这是要转换的IP地址字符串in_addr_t ip_addr; // 存储转换后的网络字节序整数型// 使用inet_addr函数将字符串转换为网络字节序的32位整数ip_addr = inet_addr(ip_str);// 检查转换是否成功if (ip_addr == INADDR_NONE) {fprintf(stderr, "Invalid IP address\n");return 1;}// 打印转换后的网络字节序整数printf("The IP address in network byte order is: %u\n", (unsigned int)ip_addr);//输出结果为0x4030201return 0;
}

Windows系统下运行代码:
因为inet_addr()是Windows Sockets的一部分,所以需要初始化Winsock

#include <winsock2.h> // 包含Winsock的头文件
#include <stdio.h>
#include <stdlib.h>int main() {// 因为inet_addr()是Windows Sockets的一部分,所以需要初始化WinsockWSADATA wsaData;int result = WSAStartup(MAKEWORD(2, 2), &wsaData);if (result != 0) {fprintf(stderr, "WSAStartup failed: %d\n", result);return 1;}const char *ip_str = "1.2.3.4"; // 要转换的IP地址字符串unsigned long ip_addr; // 存储转换后的网络字节序整数型// 使用inet_addr函数将字符串转换为网络字节序的32位整数ip_addr = inet_addr(ip_str);// 检查转换是否成功if (ip_addr == INADDR_NONE) {fprintf(stderr, "Invalid IP address\n");WSACleanup(); // 清理Winsockreturn 1;}// 打印转换后的网络字节序整数printf("The IP address in network byte order is: %#x\n", ip_addr);//输出结果为0x4030201system("pause");// 清理WinsockWSACleanup();return 0;
}

也可以使用inet_aton()函数,与inet_addr()函数不同,inet_aton()可以将转化后的32位网络字节序自动代入in_addr结构体,所以使用更加频繁

int inet_aton(const char *cp, struct in_addr *inp);
//cp:是一个指向点分十进制IPv4地址字符串的指针。
//inp:是一个指向 in_addr 结构的指针,该结构用于接收转换后的网络字节序的32位整数。

8.将网络字节序转化为字符串信息

使用inet_aton()函数,将网络字节序ip地址转化为字符串形式,语法如下:

char *inet_ntoa(struct in_addr in);
//in 是一个 struct in_addr 类型的变量,它包含了要转换的网络字节序的IPv4地址。

函数返回值是一个指向静态分配的字符数组的指针,该数组包含了转换后的点分十进制IPv4地址字符串。注意, 返回的字符串是一个指向静态存储的指针,这意味着该字符串不应被修改,并且在每次 inet_ntoa 调用后都可能改变,如果你需要保留这个字符串,应该立即复制它到安全的存储位置。

Linux系统下运行代码:

#include <arpa/inet.h>
#include <stdio.h>int main() {struct in_addr ip_addr;// 假设我们有一个网络字节序的32位整数ip_addr.s_addr = htonl(0x4030201);// 将网络字节序的IPv4地址转换为点分十进制的字符串char *ip_str = inet_ntoa(ip_addr);// 打印转换后的IP地址字符串printf("The IP address is: %s\n", ip_str);//输出结果为4.3.2.1return 0;
}

Windows系统下运行代码:
因为inet_aton()是Windows Sockets的一部分,所以需要初始化Winsock

#include <winsock2.h> // 包含Winsock的头文件
#include <stdio.h>
#include <stdlib.h>int main() {// 初始化WinsockWSADATA wsaData;int result = WSAStartup(MAKEWORD(2, 2), &wsaData);if (result != 0) {fprintf(stderr, "WSAStartup failed: %d\n", result);return 1;}// 十六进制数转换为网络字节序的整数struct sockaddr_in addr;addr.sin_addr.s_addr= htonl(0x4030201);// 将网络字节序的整数转换回点分十进制的IPv4地址字符串char *ip_str = inet_ntoa(addr.sin_addr);// 打印转换后的IP地址printf("The IP address is: %s\n", ip_str);system("pause");// 清理WinsockWSACleanup();return 0;
}

Windows系统补充说明: WSAStringToAddress()WSAAddressToString()函数,是Windows特有的函数

  • WSAStringToAddress() :是 Windows Sockets的一部分,它用于将字符串形式的网络地址转换为相应的 socket 地址结构,且自动填充到sockaddr 中
int WSAStringToAddress(const char *AddressString,int AddressFamily,const struct sockaddr *lpProtocolInfo,struct sockaddr *lpSocketAddress,int *lpAddressLength
);
//AddressString:指向包含地址字符串的指针,例如 "192.168.1.1" 或 "example.com"。
//AddressFamily:指定地址族,对于 IPv4 使用 AF_INET,对于 IPv6 使用 AF_INET6。
//lpProtocolInfo:指向协议特定信息的可选指针,默认为 NULL。
//lpSocketAddress:指向接收转换结果的 sockaddr 结构的指针。
//lpAddressLength:在调用时,指向 sockaddr 结构的大小的指针。在函数返回后,它表示实际填充到结构中的数据的大小。
  • WSAAddressToString():是 Windows Sockets API (Winsock) 的一部分,自动读取sockaddr 结构体,将其中的 socket 地址转换为它的字符串表示形式。
int WSAAddressToStringA(LPSOCKADDR lpsaAddress,DWORD dwAddressLength,LPWSAPROTOCOL_INFOW lpProtocolInfo,LPSTR lpszAddressString,LPDWORD lpdwAddressStringLength
);
//lpsaAddress:指向 sockaddr 结构的指针,包含要转换的地址信息。
//dwAddressLength:sockaddr 结构的大小。
//lpProtocolInfo:指向 WSAPROTOCOL_INFOW 结构的指针,可以为 NULL。
//lpszAddressString:指向缓冲区的指针,该缓冲区接收转换后的地址字符串。
//lpdwAddressStringLength:指向一个 DWORD 的指针,该 DWORD 指定 lpszAddressString 缓冲区的长度。函数返回时,它表示实际存储在缓冲区中的字符数,包括空终止符。

9.套接字初始化

当学习之后,我们再来看之前的代码,就会发现分为几个部分,这里展示套接字初始化部分,,Linux系统和Windows系统初始化部分代码几乎完全一致:
PS: 使用INADDR_ANY可以自动获取本地计算机的IP地址。

Linux系统

int serv_sock; // 定义服务器套接字
struct sockaddr_in serv_addr; // 定义服务器地址结构体
char* serv_port="9190";//定义端口号//创建套接字
serv_sock=socket(PF_INET, SOCK_STREAM, 0);//地址信息初始化
memset(&servAddr, 0, sizeof(servAddr));// 清空servAddr结构
//设置结构体
servAddr.sin_family = AF_INET;// 设置地址族为IPv4
servAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 设置监听的IP地址
servAddr.sin_port = htons(atoi(serv_port));// 设置监听端口//把设置好的地址信息分配给套接字,使用bind函数绑定
if (bind(serv_sock, (struct sockadd* )&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)error_handling("bind() error");//绑定失败则返回异常

Windows系统

SOCKET serv_sock; // 定义服务器套接字
struct sockaddr_in serv_addr; // 定义服务器地址结构体
char* serv_port="9190";//定义端口号//创建套接字
serv_sock=socket(PF_INET, SOCK_STREAM, 0);//地址信息初始化
memset(&servAddr, 0, sizeof(servAddr));// 清空servAddr结构
//设置结构体
servAddr.sin_family = AF_INET;// 设置地址族为IPv4
servAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 设置监听的IP地址
servAddr.sin_port = htons(atoi(serv_port));// 设置监听端口//把设置好的地址信息分配给套接字,使用bind函数绑定
if (bind(serv_sock, (struct sockadd* )&serv_addr, sizeof(serv_addr)) == SOCKET_ERROR)error_handling("bind() error");//绑定失败则返回异常

10.回顾运行过程

回顾第一章里面的代码运行部分,Linux平台和Windows平台,我们便可以理解为什么那样运行。

在这里插入图片描述
上图是开启服务器命令,意味着通过9190端口创建服务器套接字并运行程序,这里之所以没有输入IP地址,是因为通过INADDR_ANY已经自动获取了本机的IP地址。
在这里插入图片描述
上图是开启客户端命令,意味着尝试连接IP地址127.0.0.1(这里是本机地址,因为运行程序的服务器和客户端在一台电脑上,正常情况下应该是服务器端IP地址),并连接到服务器端的9190端口


http://www.ppmy.cn/ops/42295.html

相关文章

基于springboot+vue的学生考勤管理系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

前端基础入门三大核心之HTML篇:编织互联网的基石

前端基础入门三大核心之HTML篇&#xff1a;编织互联网的基石 一、HTML&#xff1a;互联网的骨架1.1 HTML基本概念1.2 第一个HTML页面1.3 标签与属性 二、HTML结构深入2.1 文档结构2.2 常用标签与实践 三、HTML5新特性四、实际开发中的应用与技巧4.1 语义化的重要性4.2 CSS与Jav…

前后端编程语言和运行环境的理解

我已重新检查了我的回答,并确保信息的准确性。以下是常用的编程语言,以及它们通常用于前端或后端开发,以及相应的框架和运行环境: 前端开发 JavaScript 框架:React, Angular, Vue.js, Ember.js, Backbone.js运行环境:Web 浏览器HTML (HyperText Markup Language) 不是编…

【数学】泰勒公式

目录 引言 一、泰勒公式 1.泰勒公式及推导 &#xff08;1&#xff09;推导 &#xff08;2&#xff09;公式 2.泰勒中值定理 &#xff08;1&#xff09;定理1&#xff08;佩亚诺余项&#xff09; &#xff08;2&#xff09;定理2&#xff08;拉格朗日余项&#xff09; …

编译Qt5.15.13

Windows 一、下载源代码&#xff0c;解压 二、安装软件 1、编译工具msvc&#xff0c;cmake&#xff0c;python3&#xff0c;jom 2、依赖库&#xff08;用于编译QSSL模块&#xff09; 2.1下载openssl 1.1源码并编译 Win10_64bit源码nmake方式安装OpenSSL_nmake安装-CSDN博…

sam代码简析

Segment Anything&#xff1a;建立了迄今为止最大的分割数据集&#xff0c;在1100万张图像上有超过1亿个掩码&#xff0c;模型的设计和训练是灵活的&#xff0c;其重要的特点是Zero-shot(零样本迁移性)转移到新的图像分布和任务&#xff0c;一个图像分割新的任务、模型和数据集…

轻量音乐网站程序源码,在线音乐免费听歌

这是一个高品质的音乐共享和流媒体平台&#xff0c;用户可以在这个网站上免费在线听歌。这个轻量级的音乐网站程序源码&#xff0c;是您创建自己的音乐流媒体网站的最佳选择&#xff01;它还支持制作插件&#xff0c;并且在更新后&#xff0c;您可以保留您的自定义设置。 下 载…

美国服务器远程桌面连接不上,美国服务器远程桌面连接不上的常见因素有哪些

美国服务器远程桌面连接不上是一个常见的问题&#xff0c;可能涉及多个层面的因素。以下是一些常见的因素&#xff1a; 一、网络层面的问题 1. 本地网络故障&#xff1a;用户的本地网络如果出现中断、不稳定或配置错误&#xff0c;将直接导致无法与远程服务器建立连接。检查本…