搬砖14、Python网络编程入门

网络编程入门

计算机网络基础

计算机网络是独立自主的计算机互联而成的系统的总称,组建计算机网络最主要的目的是实现多台计算机之间的通信和资源共享。今天计算机网络中的设备和计算机网络的用户已经多得不可计数,而计算机网络也可以称得上是一个“复杂巨系统”,对于这样的系统,我们不可能用一两篇文章把它讲清楚,有兴趣的读者可以自行阅读Andrew S.Tanenbaum老师的经典之作《计算机网络》或Kurose和Ross老师合著的《计算机网络:自顶向下方法》来了解计算机网络的相关知识。

计算机网络发展史
  1. 1960s - 美国国防部ARPANET项目问世,奠定了分组交换网络的基础。

  2. 1980s - 国际标准化组织(ISO)发布OSI/RM,奠定了网络技术标准化的基础。

  3. 1990s - 英国人蒂姆·伯纳斯-李发明了图形化的浏览器,浏览器的简单易用性使得计算机网络迅速被普及。

TCP/IP模型

实现网络通信的基础是网络通信协议,这些协议通常是由互联网工程任务组 (IETF)制定的。所谓“协议”就是通信计算机双方必须共同遵从的一组约定,例如怎样建立连接、怎样互相识别等,网络协议的三要素是:语法、语义和时序。构成我们今天使用的Internet的基础的是TCP/IP协议族,所谓协议族就是一系列的协议及其构成的通信模型,我们通常也把这套东西称为TCP/IP模型。与国际标准化组织发布的OSI/RM这个七层模型不同,TCP/IP是一个四层模型,也就是说,该模型将我们使用的网络从逻辑上分解为四个层次,自底向上依次是:网络接口层、网络层、传输层和应用层。

IP通常被翻译为网际协议,它服务于网络层,主要实现了寻址和路由的功能。接入网络的每一台主机都需要有自己的IP地址,IP地址就是主机在计算机网络上的身份标识。当然由于IPv4地址的匮乏,我们平常在家里、办公室以及其他可以接入网络的公共区域上网时获得的IP地址并不是全球唯一的IP地址,而是一个局域网(LAN)中的内部IP地址,通过网络地址转换(NAT)服务我们也可以实现对网络的访问。计算机网络上有大量的被我们称为“路由器”的网络中继设备,它们会存储转发我们发送到网络上的数据分组,让从源头发出的数据最终能够找到传送到目的地通路,这项功能就是所谓的路由。

TCP全称传输控制协议,它是基于IP提供的寻址和路由服务而建立起来的负责实现端到端可靠传输的协议,之所以将TCP称为可靠的传输协议是因为TCP向调用者承诺了三件事情:

  1. 数据不传丢不传错(利用握手、校验和重传机制可以实现)。
  2. 流量控制(通过滑动窗口匹配数据发送者和接收者之间的传输速度)。
  3. 拥塞控制(通过RTT时间以及对滑动窗口的控制缓解网络拥堵)。
网络应用模式
  1. C/S模式和B/S模式。这里的C指的是Client(客户端),通常是一个需要安装到某个宿主操作系统上的应用程序;而B指的是Browser(浏览器),它几乎是所有图形化操作系统都默认安装了的一个应用软件;通过C或B都可以实现对S(服务器)的访问。关于二者的比较和讨论在网络上有一大堆的文章,在此我们就不再浪费笔墨了。
  2. 去中心化的网络应用模式。不管是B/S还是C/S都需要服务器的存在,服务器就是整个应用模式的中心,而去中心化的网络应用通常没有固定的服务器或者固定的客户端,所有应用的使用者既可以作为资源的提供者也可以作为资源的访问者。

基于HTTP协议的网络资源访问

HTTP(超文本传输协议)

HTTP是超文本传输协议(Hyper-Text Transfer Proctol)的简称,维基百科上对HTTP的解释是:超文本传输协议是一种用于分布式、协作式和超媒体信息系统的应用层协议,它是万维网数据通信的基础,设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法,通过HTTP或者HTTPS(超文本传输安全协议)请求的资源由URI(统一资源标识符)来标识。关于HTTP的更多内容,我们推荐阅读阮一峰老师的《HTTP 协议入门》,简单的说,通过HTTP我们可以获取网络上的(基于字符的)资源,开发中经常会用到的网络API(有的地方也称之为网络数据接口)就是基于HTTP来实现数据传输的。

JSON格式

JSONJavaScript Object Notation)是一种轻量级的数据交换语言,该语言以易于让人阅读的文字(纯文本)为基础,用来传输由属性值或者序列性的值组成的数据对象。尽管JSON是最初只是Javascript中一种创建对象的字面量语法,但它在当下更是一种独立于语言的数据格式,很多编程语言都支持JSON格式数据的生成和解析,Python内置的json模块也提供了这方面的功能。由于JSON是纯文本,它和XML一样都适用于异构系统之间的数据交换,而相较于XML,JSON显得更加的轻便和优雅。下面是表达同样信息的XML和JSON,而JSON的优势是相当直观的。

XML的例子:

<?xml version="1.0" encoding="UTF-8"?>
<message><from>Alice</from><to>Bob</to><content>Will you marry me?</content>
</message>

JSON的例子:

{"from": "Alice","to": "Bob","content": "Will you marry me?"
}
requests库

requests是一个基于HTTP协议来使用网络的第三库,其官方网站有这样的一句介绍它的话:“Requests是唯一的一个非转基因的Python HTTP库,人类可以安全享用。”简单的说,使用requests库可以非常方便的使用HTTP,避免安全缺陷、冗余代码以及“重复发明轮子”(行业黑话,通常用在软件工程领域表示重新创造一个已有的或是早已被优化過的基本方法)。前面的文章中我们已经使用过这个库,下面我们还是通过requests来实现一个访问网络数据接口并从中获取美女图片下载链接然后下载美女图片到本地的例子程序,程序中使用了天行数据提供的网络API。

我们可以先通过pip安装requests及其依赖库。

pip install requests

如果使用PyCharm作为开发工具,可以直接在代码中书写import requests,然后通过代码修复功能来自动下载安装requests。

from time import time
from threading import Threadimport requests# 继承Thread类创建自定义的线程类
class DownloadHanlder(Thread):def __init__(self, url):super().__init__()self.url = urldef run(self):filename = self.url[self.url.rfind('/') + 1:]resp = requests.get(self.url)with open('/Users/Hao/' + filename, 'wb') as f:f.write(resp.content)def main():# 通过requests模块的get函数获取网络资源# 下面的代码中使用了天行数据接口提供的网络API# 要使用该数据接口需要在天行数据的网站上注册# 然后用自己的Key替换掉下面代码的中APIKey即可resp = requests.get('http://api.tianapi.com/meinv/?key=APIKey&num=10')# 将服务器返回的JSON格式的数据解析为字典data_model = resp.json()for mm_dict in data_model['newslist']:url = mm_dict['picUrl']# 通过多线程的方式实现图片下载DownloadHanlder(url).start()if __name__ == '__main__':main()

基于传输层协议的套接字编程

套接字这个词对很多不了解网络编程的人来说显得非常晦涩和陌生,其实说得通俗点,套接字就是一套用C语言写成的应用程序开发库,主要用于实现进程间通信和网络编程,在网络应用开发中被广泛使用。在Python中也可以基于套接字来使用传输层提供的传输服务,并基于此开发自己的网络应用。实际开发中使用的套接字可以分为三类:流套接字(TCP套接字)、数据报套接字和原始套接字。

TCP套接字

所谓TCP套接字就是使用TCP协议提供的传输服务来实现网络通信的编程接口。在Python中可以通过创建socket对象并指定type属性为SOCK_STREAM来使用TCP套接字。由于一台主机可能拥有多个IP地址,而且很有可能会配置多个不同的服务,所以作为服务器端的程序,需要在创建套接字对象后将其绑定到指定的IP地址和端口上。这里的端口并不是物理设备而是对IP地址的扩展,用于区分不同的服务,例如我们通常将HTTP服务跟80端口绑定,而MySQL数据库服务默认绑定在3306端口,这样当服务器收到用户请求时就可以根据端口号来确定到底用户请求的是HTTP服务器还是数据库服务器提供的服务。端口的取值范围是0~65535,而1024以下的端口我们通常称之为“著名端口”(留给像FTP、HTTP、SMTP等“著名服务”使用的端口,有的地方也称之为“周知端口”),自定义的服务通常不使用这些端口,除非自定义的是HTTP或FTP这样的著名服务。

下面的代码实现了一个提供时间日期的服务器。

from socket import socket, SOCK_STREAM, AF_INET
from datetime import datetimedef main():# 1.创建套接字对象并指定使用哪种传输服务# family=AF_INET - IPv4地址# family=AF_INET6 - IPv6地址# type=SOCK_STREAM - TCP套接字# type=SOCK_DGRAM - UDP套接字# type=SOCK_RAW - 原始套接字server = socket(family=AF_INET, type=SOCK_STREAM)# 2.绑定IP地址和端口(端口用于区分不同的服务)# 同一时间在同一个端口上只能绑定一个服务否则报错server.bind(('192.168.1.2', 6789))# 3.开启监听 - 监听客户端连接到服务器# 参数512可以理解为连接队列的大小server.listen(512)print('服务器启动开始监听...')while True:# 4.通过循环接收客户端的连接并作出相应的处理(提供服务)# accept方法是一个阻塞方法如果没有客户端连接到服务器代码不会向下执行# accept方法返回一个元组其中的第一个元素是客户端对象# 第二个元素是连接到服务器的客户端的地址(由IP和端口两部分构成)client, addr = server.accept()print(str(addr) + '连接到了服务器.')# 5.发送数据client.send(str(datetime.now()).encode('utf-8'))# 6.断开连接client.close()if __name__ == '__main__':main()

运行服务器程序后我们可以通过Windows系统的telnet来访问该服务器。

telnet 192.168.1.2 6789

当然我们也可以通过Python的程序来实现TCP客户端的功能,相较于实现服务器程序,实现客户端程序就简单多了,代码如下所示。

from socket import socketdef main():# 1.创建套接字对象默认使用IPv4和TCP协议client = socket()# 2.连接到服务器(需要指定IP地址和端口)client.connect(('192.168.1.2', 6789))# 3.从服务器接收数据print(client.recv(1024).decode('utf-8'))client.close()if __name__ == '__main__':main()

需要注意的是,上面的服务器并没有使用多线程或者异步I/O的处理方式,这也就意味着当服务器与一个客户端处于通信状态时,其他的客户端只能排队等待。很显然,这样的服务器并不能满足我们的需求,我们需要的服务器是能够同时接纳和处理多个用户请求的。下面我们来设计一个使用多线程技术处理多个用户请求的服务器,该服务器会向连接到服务器的客户端发送一张图片。

服务器端代码:

from socket import socket, SOCK_STREAM, AF_INET
from base64 import b64encode
from json import dumps
from threading import Threaddef main():# 自定义线程类class FileTransferHandler(Thread):def __init__(self, cclient):super().__init__()self.cclient = cclientdef run(self):my_dict = {}my_dict['filename'] = 'guido.jpg'# JSON是纯文本不能携带二进制数据# 所以图片的二进制数据要处理成base64编码my_dict['filedata'] = data# 通过dumps函数将字典处理成JSON字符串json_str = dumps(my_dict)# 发送JSON字符串self.cclient.send(json_str.encode('utf-8'))self.cclient.close()# 1.创建套接字对象并指定使用哪种传输服务server = socket()# 2.绑定IP地址和端口(区分不同的服务)server.bind(('192.168.1.2', 5566))# 3.开启监听 - 监听客户端连接到服务器server.listen(512)print('服务器启动开始监听...')with open('guido.jpg', 'rb') as f:# 将二进制数据处理成base64再解码成字符串data = b64encode(f.read()).decode('utf-8')while True:client, addr = server.accept()# 启动一个线程来处理客户端的请求FileTransferHandler(client).start()if __name__ == '__main__':main()

客户端代码:

from socket import socket
from json import loads
from base64 import b64decodedef main():client = socket()client.connect(('192.168.1.2', 5566))# 定义一个保存二进制数据的对象in_data = bytes()# 由于不知道服务器发送的数据有多大每次接收1024字节data = client.recv(1024)while data:# 将收到的数据拼接起来in_data += datadata = client.recv(1024)# 将收到的二进制数据解码成JSON字符串并转换成字典# loads函数的作用就是将JSON字符串转成字典对象my_dict = loads(in_data.decode('utf-8'))filename = my_dict['filename']filedata = my_dict['filedata'].encode('utf-8')with open('/Users/Hao/' + filename, 'wb') as f:# 将base64格式的数据解码成二进制数据并写入文件f.write(b64decode(filedata))print('图片已保存.')if __name__ == '__main__':main()

在这个案例中,我们使用了JSON作为数据传输的格式(通过JSON格式对传输的数据进行了序列化和反序列化的操作),但是JSON并不能携带二进制数据,因此对图片的二进制数据进行了Base64编码的处理。Base64是一种用64个字符表示所有二进制数据的编码方式,通过将二进制数据每6位一组的方式重新组织,刚好可以使用0~9的数字、大小写字母以及“+”和“/”总共64个字符表示从000000111111的64种状态。维基百科上有关于Base64编码的详细讲解,不熟悉Base64的读者可以自行阅读。

说明: 上面的代码主要为了讲解网络编程的相关内容因此并没有对异常状况进行处理,请读者自行添加异常处理代码来增强程序的健壮性。

UDP套接字

传输层除了有可靠的传输协议TCP之外,还有一种非常轻便的传输协议叫做用户数据报协议,简称UDP。TCP和UDP都是提供端到端传输服务的协议,二者的差别就如同打电话和发短信的区别,后者不对传输的可靠性和可达性做出任何承诺从而避免了TCP中握手和重传的开销,所以在强调性能和而不是数据完整性的场景中(例如传输网络音视频数据),UDP可能是更好的选择。可能大家会注意到一个现象,就是在观看网络视频时,有时会出现卡顿,有时会出现花屏,这无非就是部分数据传丢或传错造成的。在Python中也可以使用UDP套接字来创建网络应用,对此我们不进行赘述,有兴趣的读者可以自行研究。

网络应用开发

发送电子邮件

在即时通信软件如此发达的今天,电子邮件仍然是互联网上使用最为广泛的应用之一,公司向应聘者发出录用通知、网站向用户发送一个激活账号的链接、银行向客户推广它们的理财产品等几乎都是通过电子邮件来完成的,而这些任务应该都是由程序自动完成的。

就像我们可以用HTTP(超文本传输协议)来访问一个网站一样,发送邮件要使用SMTP(简单邮件传输协议),SMTP也是一个建立在TCP(传输控制协议)提供的可靠数据传输服务的基础上的应用级协议,它规定了邮件的发送者如何跟发送邮件的服务器进行通信的细节,而Python中的smtplib模块将这些操作简化成了几个简单的函数。

下面的代码演示了如何在Python发送邮件。

from smtplib import SMTP
from email.header import Header
from email.mime.text import MIMETextdef main():# 请自行修改下面的邮件发送者和接收者sender = 'abcdefg@126.com'receivers = ['uvwxyz@qq.com', 'uvwxyz@126.com']message = MIMEText('用Python发送邮件的示例代码.', 'plain', 'utf-8')message['From'] = Header('王大锤', 'utf-8')message['To'] = Header('骆昊', 'utf-8')message['Subject'] = Header('示例代码实验邮件', 'utf-8')smtper = SMTP('smtp.126.com')# 请自行修改下面的登录口令smtper.login(sender, 'secretpass')smtper.sendmail(sender, receivers, message.as_string())print('邮件发送完成!')if __name__ == '__main__':main()

如果要发送带有附件的邮件,那么可以按照下面的方式进行操作。

from smtplib import SMTP
from email.header import Header
from email.mime.text import MIMEText
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipartimport urllibdef main():# 创建一个带附件的邮件消息对象message = MIMEMultipart()# 创建文本内容text_content = MIMEText('附件中有本月数据请查收', 'plain', 'utf-8')message['Subject'] = Header('本月数据', 'utf-8')# 将文本内容添加到邮件消息对象中message.attach(text_content)# 读取文件并将文件作为附件添加到邮件消息对象中with open('/Users/Hao/Desktop/hello.txt', 'rb') as f:txt = MIMEText(f.read(), 'base64', 'utf-8')txt['Content-Type'] = 'text/plain'txt['Content-Disposition'] = 'attachment; filename=hello.txt'message.attach(txt)# 读取文件并将文件作为附件添加到邮件消息对象中with open('/Users/Hao/Desktop/汇总数据.xlsx', 'rb') as f:xls = MIMEText(f.read(), 'base64', 'utf-8')xls['Content-Type'] = 'application/vnd.ms-excel'xls['Content-Disposition'] = 'attachment; filename=month-data.xlsx'message.attach(xls)# 创建SMTP对象smtper = SMTP('smtp.126.com')# 开启安全连接# smtper.starttls()sender = 'abcdefg@126.com'receivers = ['uvwxyz@qq.com']# 登录到SMTP服务器# 请注意此处不是使用密码而是邮件客户端授权码进行登录# 对此有疑问的读者可以联系自己使用的邮件服务器客服smtper.login(sender, 'secretpass')# 发送邮件smtper.sendmail(sender, receivers, message.as_string())# 与邮件服务器断开连接smtper.quit()print('发送完成!')if __name__ == '__main__':main()
发送短信

发送短信也是项目中常见的功能,网站的注册码、验证码、营销信息基本上都是通过短信来发送给用户的。在下面的代码中我们使用了互亿无线短信平台(该平台为注册用户提供了50条免费短信以及常用开发语言发送短信的demo,可以登录该网站并在用户自服务页面中对短信进行配置)提供的API接口实现了发送短信的服务,当然国内的短信平台很多,读者可以根据自己的需要进行选择(通常会考虑费用预算、短信达到率、使用的难易程度等指标),如果需要在商业项目中使用短信服务建议购买短信平台提供的套餐服务。

import urllib.parse
import http.client
import jsondef main():host  = "106.ihuyi.com"sms_send_uri = "/webservice/sms.php?method=Submit"# 下面的参数需要填入自己注册的账号和对应的密码params = urllib.parse.urlencode({'account': '你自己的账号', 'password' : '你自己的密码', 'content': '您的验证码是:147258。请不要把验证码泄露给其他人。', 'mobile': '接收者的手机号', 'format':'json' })print(params)headers = {'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'text/plain'}conn = http.client.HTTPConnection(host, port=80, timeout=30)conn.request('POST', sms_send_uri, params, headers)response = conn.getresponse()response_str = response.read()jsonstr = response_str.decode('utf-8')print(json.loads(jsonstr))conn.close()if __name__ == '__main__':main()

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

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

相关文章

Linux系列-Linux的常见指令(三)

&#x1f308;个人主页&#xff1a;羽晨同学 &#x1f4ab;个人格言:“成为自己未来的主人~” mv 1.剪切文件&#xff0c;目录 2.重命名 首先&#xff0c;我们先来看第一个作用 假如说&#xff0c;我们原先存在一个hello.txt&#xff0c;我们如果想要将这个文件移动到其他的…

上拉电阻和下拉电阻在电路中的作用(一)

上拉电阻和下拉电阻在电路中的作用&#xff08;一&#xff09; 1.什么是上下拉电阻2.上下拉电阻的作用&#xff1a;2.1.维持输入引脚处于稳定状态。2.2.配合三极管和MOS进行电平转换电路设计2.3.OC、OD电路&#xff08;Open Collector集电极开路、Open Drain漏电极开路&#xf…

什么是分库分表?为什么要分库分表?什么时候需要分库分表?怎么样拆分?(数据库分库分表详解)

文章目录 1、什么是分库分表&#xff1f;1.1、分库分表的概念1.2、分库分表的方式1.2.1、垂直分库1.2.2、垂直分表1.2.3、水平分库1.2.4、水平分表 2、为什么要分库分表&#xff1f;3、什么时候需要分库分表&#xff1f;4、分库分表的数据路由4.1、数据路由的目的4.2、数据路由…

class 9: vue.js 3 组件化基础(2)父子组件间通信

目录 父子组件之间的相互通信父组件传递数据给子组件Prop为字符串类型的数组Prop为对象类型 子组件传递数据给父组件 父子组件之间的相互通信 开发过程中&#xff0c;我们通常会将一个页面拆分成多个组件&#xff0c;然后将这些组件通过组合或者嵌套的方式构建页面。组件的嵌套…

2024开放原子开源生态大会 | 麒麟信安携手openEuler共建开源生态,共塑产业未来

9月25日-27日&#xff0c;由开放原子开源基金会主办的2024开放原子开源生态大会在北京开幕&#xff0c;大会以“开源赋能产业&#xff0c;生态共筑未来”为主题。工业和信息化部党组书记、部长金壮龙&#xff0c;北京市委副书记、市长殷勇&#xff0c;工业和信息化部总经济师、…

汇川机器人与PLC通信-ModbusTCP超详细案例

#SCARA机器人与H5UPLC通过ModbusTCP通信,HMI界面手动操作# 应用背景: 本项目案例部分软件界面已被更新,如机器人示教软件旧版本S01.19R03。但通信的原理基本一致,废话少说,我们直接上图。 一、PLC端配置 1.添加ROB通讯表(自定义),变量表内容包括ROB系统变量,IN区和…

Cadence元件A属性和B属性相互覆盖

最近在使用第三方插件集成到Cadence,协助导出BOM到平台上&#xff0c;方便对BOM进行管理和修改&#xff0c;结果因为属性A和属性B不相同&#xff0c;导致导出的BOM错误。如下图&#xff1a; ​​ 本来我们需要导出Q12&#xff0c;结果给我们导出了Q13&#xff0c;或者反之&…

基于opencv的人脸闭眼识别疲劳监测

1. 项目简介 本项目旨在实现基于眼部特征的眨眼检测&#xff0c;通过监测眼睛开闭状态来计算眨眼次数&#xff0c;从而应用于疲劳监测、注意力检测等场景。使用了面部特征点检测算法&#xff0c;以及眼部特征比率&#xff08;EAR, Eye Aspect Ratio&#xff09;来判断眼睛的闭…

Python 实现 excel 数据过滤

一、场景分析 假设有如下一份 excel 数据 shop.xlsx, 写一段 python 程序&#xff0c;实现对于车牌的分组数据过滤。 并以车牌为文件名&#xff0c;把店名输出到 车牌.txt 文件中。 比如 闽A.txt 文件内容为&#xff1a; 小林书店福州店1 小林书店福州店2 二、依赖安装 程序依…

【C++】拆分详解 - 模板

文章目录 一、泛型编程二、函数模板1. 概念2. 语法3. 函数模板的原理4. 函数模板的实例化5. 模板参数的匹配原则 三、类模板1. 语法2. 实例化 四、模板的特化1. 概念2. 函数模板特化3. 类模板特化3.1 全特化3.2 偏特化 / 半特化3.3 应用示例 4. 小结 五、模板的分离编译1. 分离…

Java:抽象类和接口

一.抽象类 1.抽象类概念和语法 ⨀概念&#xff1a; 在面向对象的概念中&#xff0c;所有的对象都是通过类来描绘的&#xff0c;但是并不是所有的类都是用来描绘对象的&#xff0c;如果一个类中没有包含足够的信息来描绘一个具体的对象&#xff0c;这样的类就是抽象类。 ⨀语…

JMeter使用不同方式传递接口参数

1、使用 HTTP 请求中的参数&#xff1a; 在 JMeter 的测试计划中&#xff0c;添加一个 "HTTP 请求" 元件。 在 "HTTP 请求" 元件的参数化选项中&#xff0c;可以添加参数的名称和值。可以手动输入参数&#xff0c;也可以使用变量来传递参数值。 如果要使…

Golang | Leetcode Golang题解之第497题非重叠矩形中的随机点

题目&#xff1a; 题解&#xff1a; type Solution struct {rects [][]intsum []int }func Constructor(rects [][]int) Solution {sum : make([]int, len(rects)1)for i, r : range rects {a, b, x, y : r[0], r[1], r[2], r[3]sum[i1] sum[i] (x-a1)*(y-b1)}return Sol…

自定义多级联动选择器指南(uni-app)

多端支持&#xff1a;可以运行在H5、APP、微信小程序还是支付宝小程序&#xff0c;都可以轻松使用改组件。自定义配置&#xff1a;您可以根据需要配置选择器的级数&#xff0c;使其适应不同的数据结构和用例。无限级联&#xff1a;此组件支持无限级联选择&#xff0c;使您能够创…

最好的ppt模板网站是哪个?做PPT不可错过的18个网站!

现在有很多PPT模板网站&#xff0c;但真正免费且高质量的不多&#xff0c;今天我就分享主流的国内外PPT模板下载网站&#xff0c;并且会详细分析这些网站的优缺点&#xff0c;这些网站都是基于个人实际使用经验的&#xff0c;免费站点会特别标注&#xff0c;让你可以放心下载&a…

信息安全工程师(64)其他恶意代码分析与防护

前言 恶意代码是指那些能够损害系统用户和系统所有者利益的软件&#xff0c;是故意在计算机系统上执行恶意任务的恶意代码的集合。 一、恶意代码分析 病毒&#xff08;Virus&#xff09; 定义&#xff1a;病毒是一种人为制造的、能够进行自我复制的、具有对计算机资源的破坏作用…

国家信息安全水平考试(NISP一级)最新题库-第十七章

目录 另外免费为大家准备了刷题小程序和docx文档&#xff0c;有需要的可以私信获取 1 受到了ARP欺骗的计算机&#xff0c;发出的数据包&#xff0c;     地址是错误的&#xff08;&#xff09; A.源IP&#xff1b;B.目的IP&#xff1b;C.源MAC&#xff1b;D.目的MAC 正…

rust入门基础总结

文章目录 前言1、输出格式规范一、占位符相关&#xff08;一&#xff09;{}与{:?} 二、参数替换方式&#xff08;一&#xff09;位置参数&#xff08;二&#xff09;具名参数 三、格式化参数&#xff08;一&#xff09;宽度&#xff08;二&#xff09;对齐&#xff08;三&…

基于K8S的StatefulSet部署mysql主从

StatefulSet特性 StatefulSet的网络状态 拓扑状态&#xff1a;应用的多个实例必须按照某种顺序启动&#xff0c;并且必须成组存在&#xff0c;例如一个应用中必须存在一个A Pod和两个B Pod&#xff0c;且A Pod必须先于B Pod启动的场景 存储状态&#xff1a;应用存在多个实例&…

Golang | Leetcode Golang题解之第500题键盘行

题目&#xff1a; 题解&#xff1a; func findWords(words []string) (ans []string) {const rowIdx "12210111011122000010020202" next:for _, word : range words {idx : rowIdx[unicode.ToLower(rune(word[0]))-a]for _, ch : range word[1:] {if rowIdx[unico…