网络编程--协议、协议族、地址族

news/2024/5/9 0:42:28/文章来源:https://blog.csdn.net/SNAKEpc12138/article/details/130370222

写在前面

这里先介绍下socket函数(Windows版本)的函数声明,后续内容均围绕该声明展开:

#include <winsock2.h>
//af: 指定该套接字的协议族
//type: 指定该套接字的数据传输方式
//protocol: 指定该套接字的最终协议
//返回值:失败返回INVALID_SOCKET,否则为成功
SOCKET socket(int af, int type, int protocol);

协议和协议族

协议:协议就是为了完成数据交换而定好的约定。

协议族: 多个相关协议的集合 。

红烧牛肉面和藤椒牛肉面都属于牛肉面的一种,与之类似,套接字通信中的协议也具有以下几类:

名称协议族
PF_INETIPv4互联网协议族
PF_INET6IPv6互联网协议族
PF_LOCAL本地通信的UNIX协议族
PF_PACKET底层套接字协议族
PF_IPXIPX Novell协议族

套接字中实际采用的最终协议信息是通过socket函数的第三个参数传递的。在第一个参数指定的协议族范围内通过第三个参数决定最终协议。

套接字类型

套接字类型指的是套接字的数据传输方式,通过socket函数的第二个参数传递,只有这样才能决定创建的套接字的数据传输方式。

已通过第一个参数传递了协议族信息,为什么还要决定数据传输方式?

问题就在于,决定了协议族并不能同时决定数据传输方式。换言之,socket函数第一个参数PF_INET协议族中也存在多种数据传输方式。

这里最常见的就是面向连接的TCP(SOCK_STREAM)和面向消息的UDP(SOCK_DGRAM)。

面向连接的套接字特性

**面向连接的套接字的特性如下:**可靠的、按序传递的、基于字节的面向连接的数据传输方式的套接字。

收发数据的套接字内部有缓冲(buffer),简言之就是字节数组。通过套接字传输的数据将保存到该数组。因此,收到数据并不意味着马上调用recv函数。只有不超过数组容量,则有可能在数据填充满缓冲后通过1次recv函数调用读取缓冲中的全部内容。当然也可以分多次recv调用读取。

也就是说,,在面向连接的套接字中,recv函数和send函数的调用次数并无太大意义。所以说面向连接的套接字并不存在数据边界。

缓冲区满了会发生什么?

首先调用recv函数从缓存区读取部分(或全部)数据,因此,缓冲并不总是满的。

但如果recv函数读取速度比接收数据慢,缓冲就有可能满。此时套接字将无法再接收数据,但即使这样也不会发生数据丢失,因为传输端套接字将停止传输。

也就是说,面向连接的套接字会根据接收端的状态传输数据,如果传输出错还会提供重传服务。因此,面向连接的套接字除特殊情况外不会发生数据丢失。

面向消息的套接字特性

面向消息的套接字特性如下:

①强调快速传输而非传输顺序

②传输的数据可能丢失也可能销毁

③传输的数据有数据边界

④限制每次传输的数据大小

即面向消息的套接字比面向连接的套接字具有更快的传输速度,但无法避免数据丢失或损毁。另外,每次传输的数据大小具有一定限制,并存在数据边界。存在数据边界意味着接收数据的次数应和传输次数相同。

面向消息的套接字特性总结如下:不可靠的、不按序传递的、以数据的高速传输为目的的套接字。

协议的最终选择

socket函数的第三个参数决定最终采用的协议。

前面已经通过socket函数的前两个参数传递了协议族信息和数据传输方式,这些信息还不足以决定采用的协议吗?为什么还需要传递第三个参数?

正如各位所想,传递前两个参数即可创建所需套接字。所以大部分情况下可以向第三个参数传递0,除非遇到以下这种情况:

同一协议族中存在多个数据传输方式相同的协议。

协议族相同、传输方式也相同,但协议不同。此时就需要通过第三个参数具体指定协议信息。

这里以PF_INET为例,PF_INET指IPv4网络协议族,SOCK_STREAM是面向连接的数据传输。满足这两个条件的只有IPPROTO_TCP,因此可以省略第三个参数创建面向连接的套接字:

int tcp_socket = socket(PF_INET, SOCK_STREAM, 0);
//或
int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)

SOCK_DGRAM指的是面向消息的数据传输方式,满足上述条件的协议只有IPPROTO_UDP。因此可以通过以下方式创建面向消息的套接字:

int udp_socket = socket(PF_INET, SOCK_DGRAM, 0);
//或
int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

地址族和数据序列

IP地址

IP(Internet Protocol),即网络协议,是为了收发网络数据而分配给计算机的值。端口号并非赋予计算机的值,而是为了区分程序中创建的套接字而分配给套接字的序号。即IP地址是分配给计算机的值,端口号则是分配给计算机中各应用程序的套接字的值。

为使计算机连接到网络必须向其分配IP地址,IP地址分为两类:

IPv4(Internet Protocol version 4) 4字节地址族

IPv6(Internet Protocol version 6) 6字节地址族

二者主要的差别是在IP所用的字节数,目前通用的地址族是IPv4,IPv6是为了应对2010年前后IP地址耗尽的问题而提出的标准。

IPv4标准的4字节IP地址分为网络地址主机地址,其中网络地址又分为A、B、C、D、E等类型。如图:

因此只需通过IP地址的第一个字节即可判断网络地址占用的字节数:

A类地址的首字节范围: 0 ~ 127

B类地址的首字节范围: 128 ~ 191

B类地址的首字节范围: 192 ~ 223

还可以这样表示:

A类地址第一个字节的首位以0开始, 即00000000

B类地址第一个字节的前2位以10开始, 即1000 0000, 128(十进制)

C类地址第一个字节的前3位以110开始, 1100 0000, 192(十进制)

网络地址 和 主机地址

网络地址(网络ID)是为区分网络而设置的一部分IP地址。假设向某一地址www.baidu.com传输数据,该公司内部构建了局域网,把所有计算机连接起来。因此首先应该向www.baidu.com网络传输数据,也就是说,并非一开始就浏览所有4字节IP地址,进而找到目标主机。而是仅浏览4字节IP地址的网络地址,先把数据传到www.baidu.com的网络。然后www.baidu.com网络(构成网络的路由器)接收到数据后,流程传输数据的主机地址(主机ID)并将数据传给目标计算机。

端口号

IP地址用于区分计算机,只要有IP地址就能向目标主机传输数据。但仅凭IP地址无法传输给最终的应用程序,因此需要端口号来对应套接字

端口号就是在同一操作系统内为区分不同的套接字而设置的,因此无法将一个端口分配给不同的套接字。

端口号由16位构成,因此可分配的端口号范围是0 ~ 65535,其中0 ~ 1023这1024个端口是知名端口,一般分配给特定的应用程序,所以应该分配此范围之外的值。

虽然端口不可重复,但TCP套接字和UDP套接字不会共用端口,例:如果某TCP套接字使用9190端口,则其他TCP套接字就无法使用该端口,但UDP套接字可以使用。

总之,数据传输目标地址应同时包含IP地址和端口号,只有这样,数据才会被传输到最终的目的应用程序(应用程序套接字)。

地址信息的表示

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

struct SOCKADDR_IN
{sa_family_t		sin_family;		//地址族(Address Family), unsigned short.uint16_t		sin_port;		//16位TCP/UDP端口号struct in_addr	 sin_addr;		//32位IP地址char			sin_zero[8];	//不使用
};struct in_addr
{in_addr_t	s_addr;	//32位IPv4地址,typedef unsigned int in_addr_t
};

成员sin_family:每种协议族适用的地址族均不同,例IPv4使用4字节地址族,IPv6使用6字节地址族。

成员sin_port:该成员保存16位端口号,它以网络字节顺序保存。

成员sin_addr:该成员保存32位IP地址信息,也以网络字节顺序保存。

成员sin_zero:无特殊含义,只是为使结构体SOCKADDR_IN的大小与SOCKADDR结构体(bind,connect,accept中的第二个参数类型, 记得显示的类型转换)大小保持一致而插入的成员。

SOCKADDR结构体定义如下:

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

此结构体成员sa_data保存的地址信息中需包含IP地址和端口号,剩余部分应填充0,而这对于包含地址信息来讲非常麻烦,继而就有了新的更方便结构体SOCKADDR_IN。因此只需填充SOCKADDR_IN结构体,然后转换成SOCKADDR类型传递给相应函数即可。

网络字节顺序与主机字节顺序

不同CPU中,4字节整数型值1在内存空间中的保存方式是不同的,如下:

第一种保存方式: 00000000 00000000 00000000 00000001

第一种保存方式: 00000001 00000000 00000000 00000000

保存顺序的不同意味着对接收数据的解析顺序也不同,因此CPU的数据解析方式也分为2种:

大端序(Big Endian):高位字节存放到低位地址

小端序(Little Endian):高位字节存放到高位地址

例:在0x20号开始的地址中保存4字节int类型数0x12345678,两种保存方式如图:

如果两台保存方式不同的计算机进行套接字通信的时候,数据解析方式就会不一致,从而导致相关错误。因此,在通过网络传输数据时约定了一种统一方式,这种约定称为网络字节顺序(Network Byte Order),非常简单的统一为大端序

因此,主机在想网络传输数据时应将以主机字节顺序的数据(即使该主机是以大端序保存的)转换成网络字节数据在进行网络传输,

这就是为什么要在填充SOCKADDR_IN结构体前将数据转换成网络字节顺序的原因了。

字节顺序的转换

常用的字节顺序转换的API函数:

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

htons中的h代表主机(host)字节顺序。

htons中的n代表网络(network)字节顺序。

另外s表示short,l表示long,因此htons是 h、to、n、s的组合,可以解释为“把short类型主机字节顺序转换成网络字节顺序”,同理ntohs表示“把short型的网络字节顺序转换成主机字节顺序”。

网络地址的初始化

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

SOCKADDR_IN中保存地址信息的成员是32位整数型的。意味着需将常见的IP地址(201.211.124.36)转换成4字节整数型数据,这要如何转。

好在有相应的函数帮助我们将字符串形式的IP地址转换成32位整数型数据,这些转换函数在转换类型的的同时还会自动进行网络字节顺序的转换。

in_addr_t inet_addr(const char* string);
/成功时返回32位大端序整型数据,失败时返回INADDR_NONE
int inet_aton(const char* string, struct in_addr* addr);
//string: 含有序转换的IP地址信息的字符串地址
//addr: 将保存转换结果的in_addr结构体变量的地址值
//返回值:成功时返回1(true),失败时返回0(false)

与之对应的将32位整数型转换成字符串形式的函数:

char* inet_ntoa(struct in_addr adr);
//成功时返回转换的字符串地址, 失败时返回-1

一般的网络地址初始化如下:

SOCKET srvSock;
struct SOCKADDR_IN addr;
memset(&addr, 0, sizeof(addr));	//初始化该结构体
char* srvIP = "211.217.168.13"; //服务器IP
char* srvPort = "9190";			//服务器端口
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(srvIP);
addr.sin_port = htons(atoi(srvPort));
bind(srvSock, (SOCKADDR*)&SOCKADDR_IN, sizeof(SOCKADDR));

INADDR_ANY

每次创建服务器都要输入IP地址会有些繁琐,因此可以使用INADDR_ANY常量分配服务器端的IP地址,即可自动获取运行服务器端的计算机的IP地址。

总结

通过socket函数声明展开了解协议族、数据传输方式以及最终协议的相关知识,此外还学习了IP的分类规则,知道IP和端口分别标识计算机和套接字,以及初始化时的地址的初始化相关的API说明。

为了统一数据传输时的解析,这里引出了主机字节顺序和网络字节顺序,知道数据统一使用网络字节顺序传输,并介绍了主机字节顺序和网络字节顺序相互转换的API接口。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_103437.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ArcGIS Pro、Python、USLE、INVEST模型等多技术融合的生态系统服务构建生态安全格局

第一章、生态安全评价理论及方法介绍 一、生态安全评价简介 ​ 二、生态服务能力简介 ​ 三、生态安全格局构建研究方法简介 ​ 第二章、平台基础一、ArcGIS Pro介绍1. ArcGIS Pro简介2. ArcGIS Pro基础3. ArcGIS Pro数据编辑4. ArcGIS Pro空间分析5. 模型构建器6. ArcGIS Pro…

论文综述——DORE: Document Ordered Relation Extraction based on Generative Framework

DORE: Document Ordered Relation Extraction based on Generative Framework 文章的主要目标是对文档级的关系抽取。以往的研究主要是基于分类的研究&#xff0c;生成式关系抽取研究较少而且性能不佳。 文档级相比于句子级的关系抽取存在序列长度过长&#xff0c;以及实体定位…

Python base64模块加密解密

一、为何使用base64加密解密 为了安全机制的系统&#xff0c;在用户登录的时候&#xff0c;会采用一系列措施保护用户信息&#xff0c;防止程序被攻击&#xff0c;比如&#xff1a;将用户输入的密码加密处理&#xff0c;在控制台看请求接口看到的密码是加密过的密码&#xff0c…

EventBus源码解析

文章目录 前言一、EventBus使用二、EventBus事件流程分析1.注册订阅者2.发布事件Event3.接收事件Event4.取消注册订阅者 三、发送粘性事件问答EventBus 以及它的优点EventBus原理 EventBus中设计模式为什么要使用 EventBus 来替代广播呢&#xff1f;说下 5 种线程模式的区别Eve…

node(express框架)连接mysql 基础篇

文章目录 电脑安装mysql配置mysql连接mysql 创建表 创建node文件启动node node 连接数据库连接数据库 电脑安装mysql 由于我的是mac 我就安装mac版本的 mysql 如已安装跳过此步骤 mysql官网选择版本安装配置 这里注意选择下面的 next输入mysql密码 点击finish 配置mysql 打…

【EasyPoi实战系列】Spring Boot使用EasyPoi的注解让表格更漂亮以及图片的导出 - 第468篇

历史文章&#xff08;文章累计460&#xff09; 《国内最全的Spring Boot系列之一》 《国内最全的Spring Boot系列之二》 《国内最全的Spring Boot系列之三》 《国内最全的Spring Boot系列之四》 《国内最全的Spring Boot系列之五》 《国内最全的Spring Boot系列之六》 【…

行业分析| 视频监控——AI自动巡检

随着视频监控的普及&#xff0c;现在很多社区、工地、车间、厂区、超市、商铺、酒店、餐馆等场所都安装了视频监控系统。当安装的视频监控出现故障时&#xff0c;我们该如何进行简单的视频故障识别呢&#xff1f;如果只依靠人工对视频故障识别排查&#xff0c;工作量是相当大的…

Pytorch 入门资源(一) annaconda3下安装pytorch2.0.0和python3.11,使用Pycharm编辑器环境配置

一、环境安装 用annaconda3-2023.03-windows_x86_64&#xff0c;安装上python3.11和pytorch2.0.0环境。 下载pycharm community版本&#xff0c;将pycharm环境选择到pytorch&#xff0c;就可以开始上手Pytorch了。 指路几个安装博客&#xff1a; 【ok】Anaconda3的安装配置…

Three.js教程:Face3对象定义Geometry的三角形面

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 其他系列工具&#xff1a; NSDT简石数字孪生 Face3对象定义Geometry的三角形面 几何体Geometry的三角面属性geometry.faces和缓冲类型几何体BufferGeometry顶点索引属性BufferGeometry.index类似都是顶点位置数据的索引值…

TCP 协议的低效实现

包括 Linux kernel 在内的各种 TCP 实现均使用类似 skb 的对象管理一个个 packet&#xff0c;使 TCP 失去了 “流” 特征。应用通过 syscall 每写入一批数据&#xff0c;协议栈都可能生成一个 skb&#xff1a; ​ 仅管理这些 skb 就是一笔大开销。除了 skb 数据结构本身的 cru…

物联仓储系统ZigBee组网原理

在嵌入式项目物联仓储系统中&#xff0c;使用cortexM0模拟仓库&#xff0c;cortex-A9模拟服务器&#xff0c;两块开发板之间使用ZigBee技术实现数据接收和发送&#xff0c;本文就介绍一下ZigBee组网的原理和相关步骤。 1.组网概述 组建一个完整的zigbee网状网络包括两个步骤&am…

《低代码PaaS驱动集团企业数字化创新白皮书》-未来展望

未来展望 低代码PaaS发展的机遇 低代码开发可以帮助企业灵活搭建数字化应用&#xff0c;满足企业各业务部门的个性化、碎片化需求。根据IDC调研结果显示&#xff0c;当前低代码开发主要应用于面向企业内部的应用&#xff0c;占该应用总量的比例达到 11.6%&#xff1b;其次是面…

android studio EditText用法

1.自定义文本框 选中状态&#xff1a; <?xml version"1.0" encoding"utf-8"?> <shape xmlns:android"http://schemas.android.com/apk/res/android"><!--指定形状内部颜色--><solid android:color"#ffffff"&g…

Databend 开源周报第 90 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 元数据优化 最…

Leetcodes刷题之删除链表的倒数N个结点和删除链表的中间的结点

吾心信其可行&#xff0c;则移山填海之难&#xff0c;终有成功之日。 --孙中山 目录 &#x1f349;一.删除链表的倒数N个结点 &#x1f33b;1.双指针 &#x1f341;2.求链表的长度 &#x1f338;二.删除链表的中间的结点 &#x1f349;一.删除链…

2023全球运维大会 | 智能运维中的数据管理和数据探索

GOPS 全球运维大会由高效运维社区&#xff08;GreatOPS&#xff09;、开放运维联盟&#xff08;OOPSA&#xff09;和 DevOps 时代社区联合主办&#xff0c;指导单位为云计算开源产业联盟、FinOps产业推进方阵。迄今为止&#xff0c;GOPS 已经举行了十九次&#xff0c;大会参会嘉…

后代选择器

知识点&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible" content"IEedge"> <meta name"viewport" c…

管理后台项目-06-用户管理角色管理模块

目录 1-路由信息搭建和api文件信息创建 2-用户管理模块 2.1-列表数据获取以及动态渲染 2.2-添加 | 修改 用户 2.3-删除-批量删除用户 2.4-分配角色 3-角色管理 3.1-修改角色 3.2-分配权限 1-路由信息搭建和api文件信息创建 我们先进行路由菜单的配置&#xff0c;在系统…

ambari的kafka服务开启sasl

添加 sasl 配置⽂件 集群部署 Kafka2.2下载地址 http://archive.apache.org/dist/kafka/2.2.1/kafka_2.11-2.2.1.tgz 解压安装包 tar -zxvf kafka_2.11-2.2.1.tgz 部署略 ambari 数据kafka服务 在kafka的conf目录下创建sasl_conf目录,将kafka_client_jaas.conf/kafka_se…

如何保护数据安全?企业该从部署SSL证书开始

数字化时代&#xff0c;大数据开始蔓延到各行各业&#xff0c;影响着生活的方方面面。在犹如“皇帝穿新衣”般透明的大数据时代&#xff0c;数据泄露无孔不入&#xff0c;存在问题层出不穷&#xff0c;未知的漏洞隐患、安全边界的模糊、新的网络攻击手段、个人隐私的无处藏身等…