学习网络编程No.4【socket编程实战】

引言

北京时间:2023/8/19/23:01,耍了好几天,主要归咎于《我欲封天》这本小说,听了几个晚上之后逐渐入门,在闲暇时间又看了一下,小高潮直接来临,最终在三个昼夜下追完了,哈哈哈!没办法呀,哎!末200章有些些烂尾,结局合乎情理,总的来说优秀,毕竟耳根的名号摆在哪里。过度了两天,辅导员发来了开学通知,时间不允许我们摆烂啦!不然我肯定要把《一念永恒》给追完,哈哈哈!都说《求魔》才是耳根的巅峰,但是我看评论好像《一念永恒》才是经典,具体没看过,有待商榷,不过主要是这种类型的网文有一个缺点,就是容易上头,所以我打算找一个节奏慢的,像《剑来》这种应该就挺适合用听,具体有待尝试。前天晚上睡觉的时候感觉今天的引言有非常多的内容可以写,现在莫名想不起来要说什么了,那就算了,正式进入该篇博客的主题,socket编程实战,使用socket接口简单实现udp客户端/服务器。

在这里插入图片描述

正式学习套接字编程

在上篇博客中,我们重点对有关IP地址和端口号的知识进行了讲解,并且对于数据传输过程以及套接字有了更深的了解,并且对于什么网络字节序和套接字编程相关的接口有了认识,最终因为时间原因只是简单的看了一下对应的socket接口,并没有重点理解这些接口的使用,以及这些接口的功能,所以该篇博客就将重点讲解有关套接字接口的使用以及作用,当然也就是有关socket编程的具体实现,以socket编程实现UDP版本的网通通信为例。

认识sockaddr结构体

在学习socket编程,也就是socket常见的API(接口)之前,我们有必要先进行一个基础概念的梳理,当然此时的这个概念和上篇博客理解IP地址、端口号以及网络字节序不同,此时的这个基础概念是更加宏观的一个概念,本质也可以理解成为什么需要认识sockaddr结构体,sockaddr结构体是什么的问题。

上篇博客中我们反复强调网络通信的本质就是进程间通信的概念,而如果谈到进程间通信,那么我们此时就不得不回忆起进程间通信的两大标准:System V和POSIX,这两大进程间通信标准我们并不陌生,在之前学习多线程之信号量时,重点理解过,并且在上篇博客中,我们讲过socket不仅可以实现本地进程通信(本地套接字),也可以实现网络通信(网络套接字),所以此时两大通信标准的区别就诞生了。什么意思呢?也就是System V进程间通信标准只支持本地进程间通信,不支持网络通信,而因为socket(套接字)中所有的API都是POSIX标准提供和设计的,所以POSIX天然的就是一个既支持本地进程间通信,也支持网络间进程通信的标准,这也就是为什么目前大部分操作系统的内核都是使用POSIX标准的原因。明白了这点之后,此时我可以明白sockaddr本质应该就是POSIX标准下进行的结构体设计,具体用来做什么下述理解,此时我们对socket(套接字)再来一个明确的概念理解,明白socket本质就是一种网络通信技术,它会提供一套API供给我们使用,而使用这套API接口进行编程我们就称为socket网络编程,而常说的socket网络通信本质就是使用socket提供的API进行的进程间通信编码。

明白了上述知识之后,此时对于sockaddr的背景知识我们就了解了,所以此时基于该背景知识,我们就可以很好的进入该话题的理解,也就是因为POSIX标准既支持本地通信,也支持网络通信的特点,所以当时设计POSXI标准的时候,那些顶级的工程师为了实现本地进程通信和网络通信的套接字API(接口)兼容时,他们就提出了sockaddr结构体的方法,因为如果想要实现本地和网络之间的进程通信兼容同一套接口,那么必然就需要通过对接口中参数的改变来实现,所以最后为了实现接口中参数类型不同,而又能传参成功,此时就有了sockaddr_in和sockaddr_un被sockaddr强制类型转换的概念,所以此时我们就明白sockaddr本质是一个共用类型的结构体,而sockaddr_in(网络)和sockaddr_un(本地)才是不同场景下被使用的真正结构体类型,当然具体在POSIX标准下进程通信场景不止这两种,如还有IPV6(sockaddr_in6)等…,如下图所示:

在这里插入图片描述
如上图所示,就是sockaddr、sockaddr_in和sockaddr_un三种不同结构体的地址格式,不同的地址格式也就表示不同结构体对应存储的数据大不相同,从而最终决定什么结构体用于什么场景下的通信,当然这也就是为什么POSIX标准可以实现同一接口在不同场景下使用的重要原因,以sockaddr为公共结构体,然后根据不同场景下的数据去构造不同场景下的不同结构体。并且因为今天我们学习的是网络套接字,所以我们重点学习的就是sockaddr_in结构体,如下图所示,就是sockaddr_in结构体中的变量:

在这里插入图片描述

此时从图中,我们可以非常清晰的看出sockaddr_in结构体中有哪些数据,并且从上篇博客讲的有关IP地址和端口号的知识,此时我们可以很好的理解为什么sockaddr_in表示的是网络套接字通信(IP地址+端口号),但是,此时我们在该结构体中还发现了一个奇怪的数据:__SOCKADDR_COMMON (sin_); 这个玩意是个什么东西呢?从图中我们能看出,它对应的应该是一个16位地址类型AF_INET,那么它们之间存在什么关系呢?具体表示的又是什么意思呢?从上述有关sockaddr_in和sockaddr_un表示两种不同场景下的通信方式,然后在传参的时候需要使用sockaddr类型进行强制类型转换,我们就可以理解:强制类型转换的目的是为了让该目标结构体类型可以被对应的接口识别,那么此时就明白,目标接口本质识别到的一直都是sockaddr类型,那么具体如何去区分传过去的是sockaddr_in,还是sockaddr_un呢?所以为了解决这一问题,此时就设计出了AF_INET和AF_UNIX这两个字段去区分sockaddr_in/sockaddr_un,当然本质就是区分网络通信和本地通信,所以上述所说的__SOCKADDR_COMMON (sin_); 本质就是用来识别对应sockaddr结构体中第一个字段是AF_INET、AF_UNIX还是其它,具体识别方法如下:
在这里插入图片描述
如上图所示,此时我们就发现不同的sockaddr结构体,它们的__SOCKADDR_COMMON 宏定义对应的参数是不一样的,并且此时结合宏定义的具体实现,我们可以发现它使用了##来实现(注:##在C语言中起到一个将两边字符合成一个字符的功能),所以此时对不同结构体宏定义参数的不同以及##的分析,我们就能明白当时在设计sockaddr结构体时,为什么需要这么设计,本质就是为了让不同的结构体类型最终在初始化的时候,有不同的变量可以接收不同的值,也就是最终在sockaddr_in结构体中可以用sin_family变量来接收AF_INET字段,在sockaddr_un结构体中用sun_family变量来接收AF_UNIX字段,在sockaddr_in6中用sin6_family变量接收AF_INET6字段,最终实现使用同一个宏来区分不同的字段,当然本质还是##在起作用,所以此时使用##的好处就在于可以提高代码的复用性、灵活性、可维护性,让我们呢能够根据不同的情况来使用不同的变量,当然这种特性也就是多态的特性,这也就是为什么sockaddr_in、sockaddr_un和sockaddr之间存在多态的根本原因。

把对应结构体中的所有变量搞懂之后,此时我们对sockaddr_in的理解可以说是已臻化境,本质就是在我们初始化sockaddr_in结构体时,需要传入一个标识符来标识该结构体是sockaddr_in,标识我们想要进行的是网络通信,而为了区分各种通信场景,并且实现多态的特性,此时我们就使用了##来区分不同的变量,最后实现不同场景的结构体中标识符变量不同,最终让对应接口在识别是那种结构体,也就是那种标识符时,可以根据不同的变量来识别,如下图代码所示:

在这里插入图片描述
我们以套接字API中最为经典的bind接口为例,先简单来看一看对应接口内部是如何去处理不同场景下的结构体类型,如何识别该结构体内部的第一个字段,具体如上图所示,当然具体bind接口如何使用,下述我们详细介绍。

所以具体有关sockaddr结构体相关的知识我们就讲到这里,下述我们正式进入套接字API的理解,当然先理解sockaddr结构体,再学套接字接口肯定是有原因的,因为套接字接口依赖的就是sockaddr_in结构体,总而言之:在POSIX标准中对sockaddr结构体进行如此设计,此时就可以在不同的协议族(UDP/TCP)、地址类型(系统)和地址长度(IPV4/IPV6)之间进行通信,而不需要改变接口。

学习socket编程有关接口

  • int socket(int domain, int type, int protocol); 功能:创建套接字(本质就是打开一份网络文件),第一个参数domain:表示该套接字是进行网络通信(AF_INET)还是本地通信(AF_UNIX),第二个参数type:表示套接字种类,该套接字是TCP类型还是UDP类型,若传SOCK_STREAM则表示TCP类型(可靠传输、连接、面向字节流),若传SOCK_DGRAM则表示UDP类型(不可靠传输、无连接、面向数据报),第三个参数protocol:默认是0,表示默认让系统根据前两个参数选择使用TCP协议还是UDP协议,返回值int:表示如果创建套接字成功,则返回系统默认为套接字打开文件的文件描述符,失败返回-1,并设置错误码。

  • int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen); 功能:将sockaddr_in结构体从本地绑定到内核套接字中,第一个参数sockfd:表示创建套接字时返回的文件描述符(存在该参数也就是表示需要将sockaddr_in结构体中的数据写入到该文件描述符对应的文件中),第二个参数addr:是一个sockaddr结构体指针类型,也就是需要把我们初始化完成的sockaddr_in结构体传过去,第三个参数addrlen:本质就是sockaddr_in的长度(大小),返回值int:绑定成功返回0,失败返回-1。

  • ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen); 功能:接收其它主机中套接字发送的数据(网络通信),并记录发送方主机的IP地址和端口号(sockaddr_in) ,第一个参数sockfd:同理表示此时套接字对应返回文件的文件描述符(存在该参数可以明白最后接收的数据肯定就是该文件描述符对应文件中获取的),第二个参数buffer:自己定义的一个存储数据的缓冲区(本质就是用来存储接收到的数据),第三个参数len:定义缓冲区的大小(用来标明你最多可以接收多少数据),第四个参数flags:标示接收数据的方式(阻塞式/非阻塞式),默认设置为0,第五个参数src_addr:同理是一个结构体指针类型,只不过此时的这个指针类型与bind接口中的指针类型不同,它代表的是一个输出型参数,此时传的不再是初始化完成的sockaddr_in结构体,而是一个未初始化的sockaddr_in结构体,本质就是为了作为输出型参数,接收数据发送方的sockaddr_in结构体中的信息(IP地址+端口号),第六个参数addrlen:类型同理是一个指针类型,作为输出型参数,在该接口调用完毕之后,它就会将实际发送方sockaddr_in的大小更新(输出型参数的好处)。返回值ssize_t:最终调用完毕之后,该接口就会返回实际接收数据的字节数。

  • ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen); 功能:发送数据到其它主机的套接字中,第一个参数sockfd:同理,套接字对应文件的文件描述符(存在该参数表明最后数据肯定是被发送到了该文件描述符对应的文件中),第二个参数buffer:存储待发送的数据,第三个参数len:待发送数据的大小,第四个参数flags:同理默认为0,第五个参数dest_addr:明白此时发数据一定需要知道目标套接字的sockaddr_in数据,也就是需要知道向谁发数据,所以此时该参数表示的就是目标套接字的sockaddr_in信息(IP地址+端口号),第六个参数addrlen:同理目标套接字sockaddr_in结构体的大小。返回值ssize_t:同理,表示实际发送数据的字节数。

  • uint16_t ntohs(uint16_t netshort); 该接口同理上篇博客所说有关网络字节序相关知识,功能:将数据从网络序列转化为主机序列,一般在从网络中接收数据时使用,参数:16位的网络序列数据,返回值:返回从网络序列转化成主机序列之后的16位主机序列。

  • uint16_t htons(uint16_t hostshort); 同理,功能:将数据从主机序列转化为网络序列,用在需要被网络转述的数据(IP地址和端口号等),参数:16位的主机序列,返回值:返回转化后的16位网络序列。

  • in_addr_t inet_addr(const char *cp); 功能:将IP地址从点分十进制转换为32位无符号整数,并且自带主机序列转网络序列的功能,本质也就是可以把我们日常见到的IP地址转换为允许在网络中传输的数据,参数:需要被转换的字符串类型IP地址,返回值:成功返回转换之后的32位无符号整形,失败则返回INADDR_NONE。

  • char *inet_ntoa(struct in_addr in); 功能:同理,将IP地址从32位无符号整数转化为点分十进制的形式,并自带将网络序列转化为主机序列,参数:一个32位无符号整形的IP地址,返回值:转换之后的点分十进制形式的IP地址。

正式开始UDP客户端/服务器代码编写

搞定了上述常见套接字接口之后,此时实现一份网络通信形式的代码对于我们来说可以说是不费吹灰之力,本质和我们之前学习进程间通信,使用管道或者共享内存实现一份本地进程间通信的代码一样,只需要把步骤和需要初始化的数据搞定,最后直接使用socket为我们提供的数据发送和数据接收接口,很轻松就能将整份代码搞定。所以下面我们就分为三个场景来实现我们的服务端和客户端,由浅入深将我们的客户端和服务端一步一步完善。

首先是客户端的代码实现

此时因为我们想要实现的是三种不同场景下的服务端代码,而服务端代码的改变并不改变客户端,所以此时明白服务端代码有三份,而客户端的代码大致都是相似的,主要起的功能就是向服务端发送数据,接收服务端传回来的数据,所以如下图所示,就是使用socket编程实现的UDP客户端:

在这里插入图片描述
明白,此时对于客户端而言,还有一个问题需要我们解决,本质也就是理解为什么服务端关心的是自己的IP地址和端口号,而客户端关心的也是服务器的IP地址和端口号, 对比服务端和客户端的代码我们发现,在进行sockaddr_in初始化时初始化的都是服务器的IP地址和端口号,那么第一个问题就来了,为什么客户端不需要设置自己的IP地址和端口号呢?原因是因为:在我们的电脑主机上存在非常多的客户端,如果让客户端像服务端一样自己设置端口号,那么就有可能会导致两个客户端设置的端口号相同,那么此时就会导致客户端启动冲突,因为socket网络通信的本质就是 [client_ip、client_port和server_ip、server_port] 两个进程之间的通信,所以对于客户端而言,它的端口号和IP地址都是操作系统统一分配的,在发送数据时操作系统同理会将对应的端口号和IP地址绑定到该主机的套接字,然后发送给服务端套接字,从而让服务端最终也能通过recvfrom接口顺利拿到客户端的sockaddr_in数据,这也就是为什么无论是服务端还是客户端它们关心的都是服务端的IP地址和端口号了,换一个角度来看,本质也就是取决于服务端是先收后发,而客户端是先发后收的原理,再本质一点来看也就是套接字的使用规则和网络通信的基本原理决定的。

其次是服务端代码的编写

搞定了有关客户端的知识,此时我们正式进入三个不同场景服务端代码的编写,本就就是一个逐渐深入,代码逐渐丰富,逐渐变化的一个过程,如下场景一、场景二、场景三所示。

场景一:客户端发消息给服务端,服务端将消息进行回显

此时该场景是三种场景下最简单的一种,也是socket编程最为简单的一种运用,总体socket的使用规则已包含在其中,当然对于现在的我们(搞懂sockaddr_in和API),这种场景还是非常基础和实用的,下述两种场景本质万变不离其宗,更多的是对代码能力和C++语法的熟练使用而已,现在就让我们一起来看看如何实现socket最基础的使用吧!如下代码所示:

在这里插入图片描述

场景二:客户端发消息给服务端,服务端对消息进行处理,返回处理后的数据

该场景相比于场景一,有关使用套接字实现网络通信以及数据接收和转发本质都没有任何区别,因为这一套就是套接字编程的使用规则,最大的区别就是我们在服务器中增加了一个实现数据处理的函数指针,然后我们对其进行了数据处理函数的初始化,让其具有了处理数据的功能,并且此时我们使用到了一个popen接口,所以此时我们的服务器就具备了处理指令的能力,具体如图示,下面就是代码实现:

在这里插入图片描述


处理指令之后的结果,如图所示:
在这里插入图片描述

所以如上图所示,此时我们就可以实现将本地客户端中输入相关的指令,然后通过网络的形式传到某个专门处理或者说可以处理该指令的服务端上,然后该服务端处理该指令,最后将指令处理完成之后的数据返回给我们,所以根据这一原理,这不就是我们使用云服务器构建xshell机器的过程吗?让xshell将我们本地输入的指令发送到远端服务器上,然后通过服务器发送到xshell的服务端上,然后xshell根据我们在云服务器上的数据和输入的指令,进行处理,最后返回处理结果,这样我们就获取到了对应指令产生的功能,这不就是最经典的网络通信过程吗?哈哈哈,具体有待深入学习,这里先简单理解一下就行。

场景三:客户端发消息给服务端,服务端将收到的消息全部返回给客户端

这个功能不能说是上述场景二的进阶,只能说是一个相辅相成的存在,因为一个服务端的代码设计和逻辑肯定是非常复杂的,需要实现的功能和完成的效果非常多,但是因为该代码设计多线程相关知识,所以我们将其称为场景三,本质就是实现一个群聊效果,让服务端中一个线程负责接收数据,一个线程负责发送数据,然后每次都会将所有数据发送给所有在线用户,所以因为该部分代码改动较大,客户端也需要通过多线程来控制,所以客户端代码也会展示,如下代码所示:

首先是服务端代码实现

在这里插入图片描述


其次是客户端代码实现

在这里插入图片描述
上述就是有关实现网络群聊效果的UDP服务器和客户端,当然代码只是基础实现,但是麻雀虽小五脏俱全,我们想要实现的预期效果是有的,本质逻辑就是让服务器保存所有发送数据主机的IP地址和端口号,然后将服务器接收到的任意消息广播给所有在线用户,以这一逻辑实现方法有很多,上述只是其中一种,所有有关UDP实现网络通信服务器和客户端的知识我们就理解到这里。

总结:有关使用socket编程实现UDP版本的网络通信知识我们就了解到这,下篇博客我们再来看看如何使用socket编程实现TCP版本的网络通信吧!See you!

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

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

相关文章

开放网关架构演进

作者:庄文弘(弘智) 淘宝开放平台是阿里与外部生态互联互通的重要开放途径,通过开放的产品技术把阿里经济体一系列基础服务,像水、电、煤一样输送给我们的商家、开发者、社区媒体以及其他合作伙伴,推动行业的…

如何利用SFTP如何实现更安全的远程文件传输 ——【内网穿透】

🎬 鸽芷咕:个人主页 🔥 个人专栏: 《高效编程技巧》《cpolar》 ⛺️生活的理想,就是为了理想的生活! 文章目录 1. 安装openSSH1.1 安装SSH1.2 启动ssh 2. 安装cpolar2.1 配置termux服务 3. 远程SFTP连接配置3.1 查看生成的随机公…

openGauss学习笔记-46 openGauss 高级数据管理-子查询

文章目录 openGauss学习笔记-46 openGauss 高级数据管理-子查询46.1 SELECT语句中的子查询使用46.2 INSERT语句中的子查询使用46.3 UPDATE语句中的子查询使用46.4 DELETE语句中的子查询使用 openGauss学习笔记-46 openGauss 高级数据管理-子查询 子查询或称为内部查询&#xf…

H3C 无线网络vlan pool架构案例三层组网web配置

实验的是目标就是要实现华为vlan pool那种应用, 整个园区发一种ssid信号,但是连接的客户端可以随机连上后进入不同的vlan,在这大型园区网非常有用。 这种方法也适合同一个ssid情况下,在不同的位置关联不同的vlan 开启自动固化、…

【业务功能篇78】微服务-前端后端校验- 统一异常处理-JSR-303-validation注解

5. 前端校验 我们在前端提交的表单数据,我们也是需要对提交的数据做相关的校验的 Form 组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,并将 Form-Item 的 prop 属性设置为需校验的字段名即可 校验的页面效果 前端数据…

建议收藏|软考机构推荐看这一篇就够了

需要最近因为软考改革成机考,大家都在问还有没有必要找机构学?本来已经进入自学阶段的考生,也纷纷开始慌张机考改革会不会影响考试难度?今天胖圆给大家总结一下软考要不要报机构?市面上的软考培训机构如何选择&#xf…

SAP ABAPG开发屏幕自动生成日期的搜索帮助

代码如下: REPORT z_jason_test_f4 . TABLES: s031. PARAMETER p_spmon TYPE spmon DEFAULT sy-datum0(6) OBLIGATORY. SELECT-OPTIONS s_spmon FOR s031-spmon DEFAULT sy-datum0(6) OBLIGATORY. AT SELECTION-SCREEN ON VALUE-REQUEST…

gdb call 函数调用

在 gdb 里可以用 call 命令来调用函数,跟代码里调用效果是一样的。在任一断点处都可以用 call 来调用函数,但不知是否需要debug 信息来支持,我这里实际操作的都是带debug信息的程序。首先打一个断点,如: 当断点触发时&…

【Git】代码误推送还原(真实项目环境,非纸上谈兵)

背景 RT, 我今天眼睛花了,不小心把工作分支【合并】到了一个不相干的功能分支上,并且代码已经推送到远程仓库了。于是,只能尝试还原到上一次提交中。 【合并】分支有一个点我们是不可避免的,文字很难描述,…

Postgresql部署及简单操作

目录 1、介绍 2、什么是PostgreSQL 3、PostgreSQL 的特点 4、数据库定为 5、环境准备 6、编译安装 6.1 安装依赖包 6.2 下载安装包 6.3 创建用户 6.4 创建 postgresql数据目录并授权 6.5 上传压缩包并解压 6.6 编译postgresql源码 6.7 配置环境变量 6.8 初始化数…

数据结构:二叉树及相关操作

文章目录 前言一、树的概念及结构1.什么是树2. 树的相关概念3.树的表示 二、二叉树概念及结构1.二叉树概念2.特殊的二叉树3.二叉树的性质4.二叉树的存储结构 三、平衡二叉树实现1.创建树和树的前中后遍历1.前中后遍历2.创建树且打印前中后遍历 2.转换为平衡二叉树和相关操作1.转…

搭建Tomcat HTTP服务:在Windows上实现外网远程访问的详细配置与设置教程

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个轻量级的服务器,不仅名字很有趣&#xff0…

【五子棋】

五子棋 文章目录 五子棋前言一、登录功能二.哈希表管理用户的会话和房间三.基于Websocket连接开发的功能1.匹配功能2.游戏房间3.挑战功能4.人机对战5.聊天功能 前言 这篇博客主要详细介绍我的五子棋项目的核心功能的实现细节,也就是详细介绍五子棋各个功能是如何实…

开悟Optimization guide for intermediate tracks

目录 认识模型 参考方案(按模块拆解) 认识模型 模型控制1名英雄进行镜像1 v 1对战 Actor集群资源为64核CPU 问题特点:单一公平对抗场景(同英雄镜像对赛),单位时间样本产能低,累计训练资源相…

leetcode 718. 最长重复子数组

2023.8.24 本题求得子数组,其实就是连续的序列。定义一个二维dp数组,dp[i][j]的含义为:以下标i为结尾的nums1和以下标j为结尾的nums2之间的公共最长子数组。 易得:递推公式为dp[i][j] dp[i-1][j-1] 1; 由此可以看出当…

stm32 之20.HC-06蓝牙模块

原理图显示使用usart3串口使用的是PB10和PB11引脚 直接配置usart3串口协议 void usart3_init(uint32_t baud) {GPIO_InitTypeDef GPIO_InitStructureure;USART_InitTypeDef USART_InitStructure;NVIC_InitTypeDef NVIC_InitStructure;//端口B硬件时钟打开RCC_AHB1PeriphClockC…

【洛谷】P1678 烦恼的高考志愿

原题链接:https://www.luogu.com.cn/problem/P1678 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 将每个学校的分数线用sort()升序排序,再二分查找每个学校的分数线,通过二分找到每个同学估分附近的分数线。 最后…

键入网址到网页显示,期间发生了什么?

目录 1.DNS2.可靠传输 —— TCP3.远程定位 —— IP4.两点传输 —— MAC5.出口 —— 网卡6.送别者 —— 交换机(可省略)7.出境大门 —— 路由器8.数据包抵达服务器后9.响应过程:带有MAC、IP、TCP头部的完整HTTP报文: 1.DNS 客户端…

Android---- 一个完整的小项目(消防app)

前言: 针对不同群体的需求,想着应该拓展写方向。医疗app很受大家喜欢,就打算顺手写个消防app,里面基础框架还是挺简洁 规整的。登陆注册和本地数据库写的便于大家理解。是广大学子的毕设首选啊! 此app主要为了传递 消防…

【HCIP】08.ISIS中间系统

链路状态协议,传递LSA信息ISIS基于数据链路层封装在OSI时,也有自己的网络层地址和自己的路由协议,即ISIS。之前的ISIS支持OSI的网络层地址,是为OSI中的CLNP(无连接网络协议)网络设计的路由协议,…