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

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

写在前面

这里先介绍下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接口。

相关文章:

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

写在前面 这里先介绍下socket函数&#xff08;Windows版本&#xff09;的函数声明&#xff0c;后续内容均围绕该声明展开&#xff1a; #include <winsock2.h> //af: 指定该套接字的协议族 //type: 指定该套接字的数据传输方式 //protocol: 指定该套接字的最终协议 //返…...

Linux入门操作

pwd 查看当前目录 与 自动补全 文件详情 drwxrwxr-x d代表文件夹 -代表文件 其中rwx rwx r-x r是可读 w是可写 x 执行 第一组&#xff08;前三个&#xff09;指文件拥有者的权限 第二组&#xff08;中三个&#xff09;代表文件拥有的组的权限 第三组&#xff08;后三个&am…...

1。C语言基础知识回顾

学习嵌入式的C基础知识&#xff0c;主要包括几个核心知识点&#xff1a;三大语法结构、常用的数据类型、函数、结构体、指针、文件操作。 一、顺序结构 程序自上而下依次执行、没有分支、代码简单。 常见顺序结构有&#xff1a;四则运算&#xff1a;&#xff0c;-&#xff0…...

学习如何通过构建一个简单的JavaScript颜色游戏来操作DOM

学习如何通过构建一个简单的JavaScript颜色游戏来操作DOM 题目要求 我们将构建一个简单的颜色猜谜游戏。每次游戏启动时&#xff0c;都会选择一个随机的RGB颜色代码。根据游戏模式&#xff0c;我们将在屏幕上提供三个&#xff08;简单&#xff09;或六个&#xff08;困难&…...

【算法学习】—n皇后问题(回溯法)

【算法学习】—n皇后问题(回溯法) 1. 什么是回溯法&#xff1f; 相信"迷宫"是许多人儿时的回忆&#xff0c;大家小时候一定都玩过迷宫游戏。我们从不用别人教&#xff0c;都知道走迷宫的策略是&#xff1a; 当遇到一个岔路口&#xff0c;会有以下两种情况&#xf…...

万亿OTA市场进入新爆发期,2025或迎中国汽车软件付费元年

伴随智能汽车市场规模发展&#xff0c;越来越多的汽车产品具备OTA能力&#xff0c;功能的优化、以及服务的差异化&#xff0c;成为了车企竞争的新战场。 例如&#xff0c;今年初&#xff0c;问界M5 EV迎来了首次OTA升级&#xff0c;升级内容覆盖用户在实际用车中的多个场景&am…...

Android硬件通信之 蓝牙Mesh通信

一&#xff0c;简介 蓝牙4.0以下称为传统蓝牙&#xff0c;4.0以上是低功耗蓝牙&#xff0c;5.0开始主打物联网 5.0协议蓝牙最重要的技术就是Mesh组网&#xff0c;实现1对多&#xff0c;多对多的无线通信。即从点对点传输发展为网络拓扑结构&#xff0c;主要领域如灯光控制等&…...

PG数据库实现bool自动转smallint的方式

删除函数&#xff1a; 语法&#xff1a; DROP FUNCTION IF EXISTS your_schema_name.function_name(arg_type1, arg_type2) CASCADE RESTRICT; 实例&#xff1a; DROP FUNCTION IF EXISTS platformyw.boolean_to_smallint(bool) CASCADE RESTRICT; 查询是否存在函数 语法: SELE…...

易观千帆 | 2023年3月证券APP月活跃用户规模盘点

易观&#xff1a;2023年3月证券服务应用活跃人数14131.58万人&#xff0c;相较上月&#xff0c;环比增长0.61%&#xff0c;同比增长0.60%&#xff1b;2023年3月自营类证券服务应用Top10 活跃人数6221.44万人&#xff0c;环比增长0.08%&#xff1b;2023年3月第三方证券服务应用T…...

2023年江苏专转本成绩查询步骤

2023年江苏专转本成绩查询时间 2023年江苏专转本成绩查询时间预计在5月初&#xff0c;参加考试的考生&#xff0c;可以关注考试院发布的消息。江苏专转本考生可在规定时间内在省教育考试院网&#xff0c;在查询中心页面中输入准考证号和身份证号进行查询&#xff0c;或者拨…...

JavaScript中sort()函数

sort()函数是javascript中自带函数&#xff0c;这个函数的功能是排序。 使用sort()函数时&#xff0c;函数参数如果不设置的话&#xff0c;以默认方式进行排序&#xff0c;就是以字母顺序进行排序&#xff0c;准确的讲就是按照字符编码的顺序进行排序。 var arr [3,2,3,34,1…...

泰克Tektronix DPO5204B混合信号示波器

特征 带宽&#xff1a;2 GHz输入通道&#xff1a;4采样率&#xff1a;1 或 2 个通道上为 5 GS/s、10 GS/s记录长度&#xff1a;所有 4 个通道 25M&#xff0c;50M&#xff1a;1 或 2 个通道上升时间&#xff1a;175 皮秒MultiView zoom™ 记录长度高达 250 兆点>250,000 wf…...

突破传统监测模式:业务状态监控HM的新思路

作者&#xff1a;京东保险 管顺利 一、传统监控系统的盲区&#xff0c;如何打造业务状态监控。 在系统架构设计中非常重要的一环是要做数据监控和数据最终一致性&#xff0c;关于一致性的补偿&#xff0c;已经由算法部的大佬总结过就不在赘述。这里主要讲如何去补偿&#xff…...

0Ω电阻在PCB板中的5大常见作用

在PCB板中&#xff0c;时常见到一些阻值为0Ω的电阻。我们都知道&#xff0c;在电路中&#xff0c;电阻的作用是阻碍电流&#xff0c;而0Ω电阻显然失去了这个作用。那它存在于PCB板中的原因是什么呢&#xff1f;今天我们一探究竟。 1、充当跳线 在电路中&#xff0c;0Ω电阻…...

分布式消息队列Kafka(三)- 服务节点Broker

1.Kafka Broker 工作流程 &#xff08;1&#xff09;zookeeper中存储的kafka信息 ​ 1&#xff09;启动 Zookeeper 客户端。 [zrclasshadoop102 zookeeper-3.5.7]$ bin/zkCli.sh ​ 2&#xff09;通过 ls 命令可以查看 kafka 相关信息。 [zk: localhost:2181(CONNECTED) 2]…...

蠕动泵说明书_RDB

RDB_2T-S蠕 动 泵 概述 蠕动灌装泵是一种高性能、高质量的泵。采用先进的微处理技术及通讯方式做成的控制器和步进电机驱动器&#xff0c;配以诚合最新研制出的泵头&#xff0c;使产品在稳定性、先进性和性价比上达到一个新的高度。适用饮料、保健品、制药、精细化工等诸流量…...

浅谈react如何自定义hooks

react 自定义 hooks 简介 一句话&#xff1a;使用自定义hooks可以将某些组件逻辑提取到可重用的函数中。 自定义hooks是一个从use开始的调用其他hooks的Javascript函数。 下面以一个案例: 新闻发布操作&#xff0c;来简单说一下react 自定义 hooks。 不使用自定义hooks时 …...

如何优雅的写个try catch的方式!

软件开发过程中&#xff0c;不可避免的是需要处理各种异常&#xff0c;就我自己来说&#xff0c;至少有一半以上的时间都是在处理各种异常情况&#xff0c;所以代码中就会出现大量的try {...} catch {...} finally {...} 代码块&#xff0c;不仅有大量的冗余代码&#xff0c;而…...

海尔智家:智慧场景掌握「主动」权,用户体验才有话语权

2023年1月&#xff0c;《福布斯》AI专栏作家Rob Toews发布了年度AI发展预测&#xff0c;指出人工智能的发展将带来涉及各行业、跨学科领域的深远影响。变革将至&#xff0c;全球已掀起生成式AI热&#xff0c;以自然语言处理为代表的人工智能技术在快速进化&#xff0c;积极拥抱…...

基于铜锁,在前端对登录密码进行加密,实现隐私数据保密性

本文将基于 铜锁&#xff08;tongsuo&#xff09;开源基础密码库实现前端对用户登录密码的加密&#xff0c;从而实现前端隐私数据的保密性。 首先&#xff0c;铜锁密码库是一个提供现代密码学算法和安全通信协议的开源基础密码库&#xff0c;在中国商用密码算法&#xff0c;例…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

工业安全零事故的智能守护者:一体化AI智能安防平台

前言&#xff1a; 通过AI视觉技术&#xff0c;为船厂提供全面的安全监控解决方案&#xff0c;涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面&#xff0c;能够实现对应负责人反馈机制&#xff0c;并最终实现数据的统计报表。提升船厂…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

Docker拉取MySQL后数据库连接失败的解决方案

在使用Docker部署MySQL时&#xff0c;拉取并启动容器后&#xff0c;有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致&#xff0c;包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因&#xff0c;并提供解决方案。 一、确认MySQL容器的运行状态 …...