12 正则表达式 | HTTP协议相关介绍
文章目录
- 正则表达式
- re模块最基础操作(匹配开头)
- 匹配单个字符
- 匹配多个字符
- 匹配开头结尾
- 匹配分组
- 对于group的理解
- r的作用
- re 模块高级用法
- compile
- search
- findall
- 易错点
- sub
- 直接替换
- 函数替换
- split 根据匹配进行切割字符串,并返回一个列表
- python 贪婪和非贪婪
- HTTP 协议
- 对于F12的一些组件介绍
- 浏览器解析过程
- http 协议的结束符
- B/S模式下的Web静态服务器
- Web 静态服务器-1-显示固定的页面
- Web 静态服务器-2-显示需要的页面
- Web 静态服务器-3-多进程
- Web 静态服务器-4-多线程
- Web 静态服务器-5-非堵塞模式
- Web 静态服务器-6-epoll
- Web 静态服务器-7-gevent 版
正则表达式
在 Python 中需要通过正则表达式对字符串进行匹配的时候,可以使用一个模块,名字为 re。
re模块最基础操作(匹配开头)
# 导入 re 模块
import re
# 使用 match 方法进行匹配操作
result = re.match(正则表达式,要匹配的字符串)
# 如果上一步匹配到数据的话,可以使用 group 方法来提取数据
result.group()
示例:
import reresult = re.match("helloworld","helloworld.cn")print(result.group())
输出的结果:
helloworld
说明:re.match() 能够匹配出以== xxx 开头==的字符串
匹配单个字符
字符 | 功能 |
---|---|
. | 匹配任意 1 个字符(除了\n) |
[ ] | 匹配[ ]中列举的字符 |
\d | 匹配数字,即 0-9 dicimal |
\D | 匹配非数字,即不是数字 |
\s | 匹配空白,即 空格,tab 键 space |
\S | 匹配非空白 |
\w | 匹配单词字符,即 a-z、A-Z、0-9、_ (汉字) word |
\W | 匹配非单词字 |
示例:
import reret = re.match("t.o","two")
print(ret.group())# 大小写 h 都可以的情况
ret = re.match("[hH]","hello Python")
print(ret.group())# 匹配 0 到 9 第二种写法
ret = re.match("[0-9]Hello Python","7Hello Python")
print(ret.group())
ret = re.match("[0-35-9]Hello Python","7Hello Python")
print(ret.group())# 使用\d 进行匹配
ret = re.match("嫦娥\d 号","嫦娥 1 号发射成功")
print(ret.group())# 等等
匹配多个字符
字符 | 功能 |
---|---|
* | 匹配前一个字符出现 0 次或者无限次,即可有可无 |
+ | 匹配前一个字符出现 1 次或者无限次,即至少有 1 次 |
? | 匹配前一个字符出现 1 次或者 0 次,即要么有 1 次,要么没有 |
{m} | 匹配前一个字符出现 m 次 |
{m,n} | 匹配前一个字符出现从 m 到 n 次 |
示例:
import re# *的功能实现
ret = re.match("[A-Z][a-z]*","Aabcdef")
print(ret.group())
# 输出结果:Aabcdef# +的功能实现
names = ["name1", "_name", "2_name", "__name__"]
for name in names:ret = re.match("[a-zA-Z_]+[\w]*",name)
if ret:print("变量名 %s 符合要求" % ret.group())
else:print("变量名 %s 非法" % name)
# 输出结果:
# 变量名 name1 符合要求
# 变量名 _name 符合要求
# 变量名 2_name 非法
# 变量名 __name__ 符合要求# ?的功能实现
ret = re.match("[1-9]?\d","09")
print(ret.group())
# 输出结果:0# {m}的功能实现
ret = re.match("[a-zA-Z0-9_]{8,20}","1ad12f23s34455ff66")
print(ret.group())
# 输出结果:1ad12f23s34455ff66
匹配开头结尾
字符 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
示例:
email_list = ["xiaoWang@163.com","xiaoWang@163.comheihei",".com.xiaowang@qq.com"]
for email in email_list:ret = re.match("[\w]{4,20}@163\.com$", email)if ret:print("%s 是符合规定的邮件地址,匹配后的结果是:%s" %(email,ret.group()))else:print("%s 不符合要求" % email)
或者说,实际上我们仅需要所有情况下在最前面加上^ 最后面加上$也可以解决大多数问题。
匹配分组
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
(ab) | 将括号中字符作为一个分组 |
\num | 引用分组 num 匹配到的字符串 |
(?P<name>) | 分组起别名 |
(?P=name) | 引用别名为 name 分组匹配到的字 |
[^符号]* 代表没有遇到符号就一直进行匹配,一直匹配下去
例子:
import re# |的使用
ret = re.match("[1-9]?\d$|100","100")
print(ret.group())
# 输出:100# ()分组的使用
ret = re.match("\w{4,20}@(163|126|qq)\.com", "test@126.com")
print(ret.group())
# 输出:test@126# ([^-]*) 代表没有遇到小横杠-就一直进行匹配,一直匹配下去
ret = re.match("([^-]+)-(\d+)","010-12345678")
print(ret.group())
# 输出:'010-12345678'# 使用\num,需要注意的是,这边需要使用元字符串,即类似 r""这种格式
ret = re.match(r"<([a-zA-Z]*)>\w*</\1>", "<html>hh</html>")
print(ret.group())# (?P<name>) (?P=name)
# 注意P要大写
ret = re.match(r"<(?P<name1>\w*)><(?P<name2>\w*)>.*</(?P=name2)></(?P=name1)>","<html><h1>www.qq.com</h1></html>")
print(ret.group())
# 输出:<html><h1>www.qq.com</h1></html>
对于group的理解
实际上:
re.group(0)和re.group()输出的是符合的正则表达式的东西
re.group(1)输出的的是第一个分组内的东西(如果有分组才有这一项,没有的话就无这一项)
r的作用
Python 中字符串前面加上 r 表示原生字符串,
与大多数编程语言相同,正则表达式里使用"“作为转义字符,这就可能造成反斜杠困扰。假如你需要匹配文本中的字符”“,那么使用编程语言表示的正则表达式里将需要 4 个反斜杠”\":前两个和后两个分别用于在编程语言里转义成反斜杠,转换成两个反斜杠后再在正则表达式里转义成一个反斜杠。
Python 里的原生字符串很好地解决了这个问题,有了原生字符串,你再也不用担心是不是漏写了反斜杠,写出来的表达式也更直观。
值得关注的是这个r只解决了//,就是只对/生效,如果需要匹配.,实际上也需要使用转义/.
re 模块高级用法
compile
compile实际上就是为了避免写这个正则表达式的式子太长,每次都要进行书写,就先把这个封装成一个对象,然后调用这个对象里面的方法进行使用。
com = re.compile(r'\d{4}/[01]?[0-9]/[1-3]?[0-9]\s(?:0[0-9]|1[0-9]|2[0-4])\:[0-5][0-9]')
com.match('2020/7/20 18:20')
search
功能:使用正则匹配式去字符串中寻找第一个符合该格式的子串
import re
ret = re.search(r"\d+", "阅读次数为 9999")
print(ret.group())
输出:
9999
findall
功能:寻找字符串中所有符合正则表达式的字串
基础样例:
import re
ret = re.findall(r"\d+", "python = 9999, c = 7890, c++ = 12345")
print(ret)
输出(值得关注的是返回的是一个列表,而非一个对象,然后使用group方法进行调用):
['9999', '7890', '12345']
易错点
对于包含分组的正则表达式,findall会去匹配分组内的正则表达式。
ret_s = hello world, now is 2020/7/20 18:48, 现在是2020/7/20 18:48
# compile避免了每次都去写正则,findall 有问题
com = re.compile(r'\d{4}/[01]?[0-9]/[1-3]?[0-9]\s(0[0-9]|1[0-9]|2[0-4]):[0-5][0-9]')
ret = com.findall(ret_s)
print(ret)# ?:可以避免findall只提取分组内的内容
com1 = re.compile(r'\d{4}/[01]?[0-9]/[1-3]?[0-9]\s(?:0[0-9]|1[0-9]|2[0-4]):[0-5][0-9]')
ret = com1.findall(ret_s)
print(ret)
输出:
['18', '18']
['2020/7/20 18:48', '2020/7/20 18:48']
sub
功能:替换字符串
直接替换
import re
s = 'hello world, now is 2020/7/20 18:48, 现在是 2020 年 7 月 20 日 18 时 48 分。'
ret_s = re.sub(r'年|月', r'/', s)
ret_s = re.sub(r'日|分', r' ', ret_s)
ret_s = re.sub(r'时', r':', ret_s)
print(ret_s)
输出:
hello world, now is 2020/7/20 18:48, 现在是2020/7/20 18:48 。
函数替换
不论使用匿名函数还是使用真实的函数都是可以的,唯一值得关注的是传进来的对象是正则表达式匹配后的字符串类型的数据,如果需要整形化处理,记得加上int(),最后也需要返回str()类型的数据
import redef add(temp):strNum = temp.group()num = int(strNum) + 1return str(num)ret = re.sub(r"\d+", add, "python = 997")
print(ret)ret = re.sub(r"\d+", lambda temp: str(int(temp.group()) + 1), "python = 99")
print(ret)
split 根据匹配进行切割字符串,并返回一个列表
import re
ret = re.split(r":| ","info:xiaoZhang 33 shandong")
print(ret)
python 贪婪和非贪婪
Python 里数量词默认是贪婪的(在少数语言里也可能是默认非贪婪),总是尝试匹配尽可能多的字符;非贪婪则相反,总是尝试匹配尽可能少的字符。
解决方式:非贪婪操作符“?”,这个操作符可以用在"*“,”+“,”?"的后面,要求正则匹配的越少越好。
test_str='''
<img data-original="https://rpic.douyucdn.cn/appCovers/2016/11/13/1213973_201611131917_small.jpg" src="https://rpic.douyucdn.cn/appCovers/2016/11/13/1213973_201611131917_small.jpg" style="display: inline;">
'''
# 贪婪的写法,最后得到的数据会匹配到最后一个jpg,实际上就是*一直吃到最后
print(re.search(r"https://.*\.jpg",test_str).group())
# 非贪婪的写法,最后得到的数据会匹配到最开始的一个jpg
print(re.search(r"https://.*?\.jpg", test_str).group())
输出结果:
https://rpic.douyucdn.cn/appCovers/2016/11/13/1213973_201611131917_small.jpg" src="https://rpic.douyucdn.cn/appCovers/2016/11/13/1213973_201611131917_small.jpg
https://rpic.douyucdn.cn/appCovers/2016/11/13/1213973_201611131917_small.jpg
HTTP 协议
对于F12的一些组件介绍
监测一下百度官网(这边采用edge浏览器,其他的用谷歌的也是差不多的。)
Elements 显示网页的结构
Network 显示浏览器和服务器的通信
我们点 Network,确保第一个小红灯亮着,浏览器就会记录所有浏览器和服务器之间的通信。
点开即可查看对应的是什么,然后进行对比以下的格式会有更深的领悟,这边列一下我这边接受到百度http协议的包,并对于比较重要的概念的介绍。
General(常规):
Request URL: https://www.baidu.com/
Request Method: GET
Status Code: 200 OK
Remote Address: 36.152.44.96:443
Referrer Policy: unsafe-url
- Request URL:即你请求的网站
- Request Method:请求资源的方法,一般请求资源的方法有四种:get(查询),post(新增),put(修改),delete(删除)
- Status Code:状态,200代表成功返回响应
- Remote Address:远程连接多少ip的多少port,而我们采用http即都是80端口,https是443端口
- Referrer Policy:来源页面政策,暂时不用了解,想要了解,可以百度
Response header(响应标头)这一部分就是服务器返回来的数据header:
HTTP/1.1 200 OK
Bdpagetype: 1
Bdqid: 0x87d5ef110007ca38
Connection: keep-alive
Content-Encoding: gzip
Content-Security-Policy: frame-ancestors 'self' https://chat.baidu.com http://mirror-chat.baidu.com https://fj-chat.baidu.com https://hba-chat.baidu.com https://hbe-chat.baidu.com https://njjs-chat.baidu.com https://nj-chat.baidu.com https://hna-chat.baidu.com https://hnb-chat.baidu.com http://debug.baidu-int.com;
Content-Type: text/html; charset=utf-8
Date: Tue, 15 Aug 2023 06:17:21 GMT
Server: BWS/1.1
Set-Cookie: BDSVRTM=0; path=/
Set-Cookie: BD_HOME=1; path=/
Set-Cookie: H_PS_PSSID=36544_39107_38831_26350_39138_39132_39100; path=/; domain=.baidu.com
Strict-Transport-Security: max-age=172800
Traceid: 169208024116233405549787992221453634104
X-Ua-Compatible: IE=Edge,chrome=1
Transfer-Encoding: chunked
HTTP 响应分为 Header 和 Body 两部分(Body 是可选项),我们在 Network 中看到的 Header 最重要的几行如下:
- HTTP/1.1 200 OK
200 表示一个成功的响应,后面的 OK 是说明。
如果返回的不是 200,那么往往有其他的功能,例如
1.1 失败的响应有 404 Not Found:网页不存在
1.2 500 Internal Server Error:服务器内部出错
1.3 …等等… - Content-Type: text/html
Content-Type 指示响应的内容,这里是 text/html 表示 HTML 网页。请注意,浏览器就是依靠 Content-Type 来判断响应的内容是网页还是图片,是视频还是音乐。浏览器并不靠 URL 来判断响应的内容,所以,即使 URL 是http://www.baidu.com/meimei.jpg,它也不一定就是图片。 - HTTP 响应的 Body 就是 HTML 源码,我们在菜单栏选择“视图”,“开发者”,“查看网页源码”就可以在浏览器中直接查看 HTML 源码
- Connection: keep-alive当中代表连接是长连接,也就是说你与服务器会保持连接,不会像短链接一样,仅仅在使用的时候connect服务器,发送完数据之后就结束连接
- Transfer-Encoding: chunked流式文件后面介绍
Request Header(请求头)这就是浏览器发给服务器的数据,因为这些数据是有格式的,所以就称作http协议:
GET / HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6
Cache-Control: max-age=0
Connection: keep-alive
Cookie: 这边有一串数据,里面包含了token等等一系列东西
Host: www.baidu.com
Referer: https://www.baidu.com/s?tn=15007414_9_dg&wd=%E7%99%BE%E5%BA%A6
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.200
sec-ch-ua: "Not/A)Brand";v="99", "Microsoft Edge";v="115", "Chromium";v="115"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
最主要的头两行分析如下,第一行:
- GET / HTTP/1.1
GET 表示一个读取请求,将从服务器获得网页数据,/表示 URL 的路径,URL 总是以/开头,/就表示首页,最后的 HTTP/1.1 指示采用的 HTTP 协议版本是 1.1。目前 HTTP 协议的版本就是 1.1,但是大部分服务器也支持 1.0 版本,主要区别在于 1.1 版本允许多个 HTTP 请求复用一个 TCP 连接,以加快传输速度。 - 从第二行开始,每一行都类似于 Xxx: abcdefg:
- Host: www.sina.com
表示请求的域名是 www.baidu.com。如果一台服务器有多个网站,服务器就需要通过 Host 来区分浏览器请求的是哪个网站
浏览器解析过程
当浏览器读取到新浪首页的 HTML 源码后,它会解析 HTML,显示页面,然后,根据 HTML 里面的各种链接,再发送 HTTP 请求给新浪服务器,拿到相应的图片、视频、JavaScript 脚本、CSS 等各种资源,最终显示出一个完整的页面。所以我们在 Network 下面能看到很多额外的 HTTP 请求
http 协议的结束符
http 的 header 和 body 之间空行分割的,又因为每个头部项是以 \r\n 作为结束符,所以,数据流中是以 \r\n\r\n 来分割解析请求头(响应头)与请求体(响应体)的。如下图所示:
理解一下使用/r/n/r/n进行隔断数据,但是现在这一句话也不是很正确,这边写者也只是粗浅理解。
补充:链接
B/S模式下的Web静态服务器
我们这边写的代码属于的就是web服务器端,算作对于前面网络编程epoll,进程池,线程池,协程的综合,虽然不如web服务器端常用的框架apache和nginx,但写一下还是有利于自己对于http协议以及一系列网络编程的理解:
Web 静态服务器-1-显示固定的页面
#! /bin/usr/python3.6
# 编辑人:lgt
# 时间2023年08月15日import socketdef tcp_server():# 创建socket对象tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 复用端口tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)# bindtcp_server.bind(('',2000))# listentcp_server.listen(128)# 阻塞连接等待连接对象client,client_arr = tcp_server.accept()# 接收消息http_head = client.recv(10000)print(http_head.decode('utf-8'))# 连接之后,这边默认直接返回一个前端的页面,第一个demo不搞太难# response头的标准写法 每行的结束以\r\n为标志response = "HTTP/1.1 200 OK\r\n"# 多加个\r\n代表协议头结束response += "\r\n"# 此时加上body的内容response += '<html><h1>hello world</h1></html>'# 传回消息client.send(response.encode('utf-8'))client.close()# 这边就不关闭tcp服务器了if __name__ == '__main__':tcp_server()
输出过去给浏览器的:
此时打开ipconfig进行查看,然后使用浏览器进行访问端口,即可看到对应的页面。
此时服务器端口能发现返回了这一个协议属性,所以我们可以再次进行相对应的修改
Web 静态服务器-2-显示需要的页面
这里我们思考一个问题,如果我们采用单进程有没有可能可以多个网页进行和服务器进行沟通,得到页面请求?
直觉告诉我们是不太可能的,原因就是我们的思想背局限在长连接上了,在短连接的情况下是完全可以做到的,下面就是使用单进程,短连接的方式进行写的。
import socket
import redef tcp_server():"""tcp_server的创建:return: None"""# 创建socket对象tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 复用端口tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)# bindtcp_server.bind(('',2000))# listentcp_server.listen(128)while True:# 阻塞连接等待连接对象client,client_arr = tcp_server.accept()solve_task(client)# 关闭tcp服务器,实际上没有关闭,原因就是上面的是死循环tcp_server.close()def solve_task(client:socket):"""实现http协议传输以及接受需求的对应的网页资源的返回:param client:客户端:return:None"""# 接收消息print('*' * 100)http_head = client.recv(10000)print(http_head.decode('utf-8'))# 定义正则表达式,提取print('*' * 100)# [^/]*吞掉/之前所有的字符,后面重要部分使用()进行括起来,后面使用group(1)进行提取re1 = re.compile(r'[^/]*(/[^ ]*)')file1 = re1.match(http_head.decode('utf-8')).group(1)print(file1)if file1 == '/':file1 = '/index.html'# 拿到了想要返回的html的样式,这时候就应该返回相关的数据try:f = open('./html' + file1 , 'rb')except:# 如果文件没有找到response = "HTTP/1.1 404 NOT FOUND\r\n"# 多加个\r\n代表协议头结束response += "\r\n"response += "<html><h1>------file not found-----</h1></html>"client.send(response.encode("utf-8"))else:# 如果文件找到了# response头的标准写法 每行的结束以\r\n为标志response = "HTTP/1.1 200 OK\r\n"# 多加个\r\n代表协议头结束response += "\r\n"# 此时加上body的内容body = f.read()f.close()# 传回消息client.send(response.encode('utf-8'))client.send(body)# 此处是关键,判断短连接的重要标志client.close()if __name__ == '__main__':tcp_server()
Web 静态服务器-3-多进程
import socket
import re
from multiprocessing import Process
def tcp_server():"""tcp_server的创建:return: None"""# 创建socket对象tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 复用端口tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)# bindtcp_server.bind(('',2000))# listentcp_server.listen(128)while True:# 阻塞连接等待连接对象client,client_arr = tcp_server.accept()# 值得关注的是args=(client,)需要有一个,p = Process(target=solve_task,args=(client,))p.start()# 此处为什么要关掉呢?这里因为我们使用的是进程,对于进程来说,资源属于引入计数,也就是别的进程到这里是复制主进程的所有的变量,再进去的,此时主进程就可以关掉了client.close()# 关闭tcp服务器,实际上没有关闭,原因就是上面的是死循环tcp_server.close()def solve_task(client:socket):"""实现http协议传输以及接受需求的对应的网页资源的返回:param client:客户端:return:None"""# 接收消息print('*' * 100)http_head = client.recv(10000)print(http_head.decode('utf-8'))# 定义正则表达式,提取print('*' * 100)# [^/]*吞掉/之前所有的字符,后面重要部分使用()进行括起来,后面使用group(1)进行提取re1 = re.compile(r'[^/]*(/[^ ]*)')file1 = re1.match(http_head.decode('utf-8')).group(1)print(file1)if file1 == '/':file1 = '/index.html'# 拿到了想要返回的html的样式,这时候就应该返回相关的数据try:f = open('./html' + file1 , 'rb')except:# 如果文件没有找到response = "HTTP/1.1 404 NOT FOUND\r\n"# 多加个\r\n代表协议头结束response += "\r\n"response += "<html><h1>------file not found-----</h1></html>"client.send(response.encode("utf-8"))else:# 如果文件找到了# response头的标准写法 每行的结束以\r\n为标志response = "HTTP/1.1 200 OK\r\n"# 多加个\r\n代表协议头结束response += "\r\n"# 此时加上body的内容body = f.read()f.close()# 传回消息client.send(response.encode('utf-8'))client.send(body)# 此处是关键,判断短连接的重要标志client.close()if __name__ == '__main__':tcp_server()
Web 静态服务器-4-多线程
#! /bin/usr/python3.6
# 编辑人:lgt
# 时间2023年08月15日import socket
import re
import threading
def tcp_server():"""tcp_server的创建:return: None"""# 创建socket对象tcp_server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 复用端口tcp_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)# bindtcp_server.bind(('',2000))# listentcp_server.listen(128)while True:# 阻塞连接等待连接对象client,client_arr = tcp_server.accept()# 值得关注的是args=(client,)需要有一个,p = threading.Thread(target=solve_task,args=(client,))p.start()# 多线程时,client传递给子线程以后,主线程不能关闭# client.close()# 关闭tcp服务器,实际上没有关闭,原因就是上面的是死循环tcp_server.close()def solve_task(client:socket):"""实现http协议传输以及接受需求的对应的网页资源的返回:param client:客户端:return:None"""# 接收消息print('*' * 100)http_head = client.recv(10000)print(http_head.decode('utf-8'))# 定义正则表达式,提取print('*' * 100)# [^/]*吞掉/之前所有的字符,后面重要部分使用()进行括起来,后面使用group(1)进行提取re1 = re.compile(r'[^/]*(/[^ ]*)')file1 = re1.match(http_head.decode('utf-8')).group(1)print(file1)if file1 == '/':file1 = '/index.html'# 拿到了想要返回的html的样式,这时候就应该返回相关的数据try:f = open('./html' + file1 , 'rb')except:# 如果文件没有找到response = "HTTP/1.1 404 NOT FOUND\r\n"# 多加个\r\n代表协议头结束response += "\r\n"response += "<html><h1>------file not found-----</h1></html>"client.send(response.encode("utf-8"))else:# 如果文件找到了# response头的标准写法 每行的结束以\r\n为标志response = "HTTP/1.1 200 OK\r\n"# 多加个\r\n代表协议头结束response += "\r\n"# 此时加上body的内容body = f.read()f.close()# 传回消息client.send(response.encode('utf-8'))client.send(body)# 此处是关键,判断短连接的重要标志client.close()if __name__ == '__main__':tcp_server()
Web 静态服务器-5-非堵塞模式
import time
import socket
import sys
import reclass WSGIServer(object):"""定义一个WSGI服务器的类"""def __init__(self, port, documents_root):# 1. 创建套接字self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)# 2. 绑定本地信息self.server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)self.server_socket.bind(("", port))# 3. 变为监听套接字self.server_socket.listen(128)self.server_socket.setblocking(False)self.client_socket_list = list()self.documents_root = documents_rootdef run_forever(self):"""运行服务器"""# 等待对方链接while True:# time.sleep(0.5) # for test#下面try的目的是接收请求并放入列表try:new_socket, new_addr = self.server_socket.accept()except Exception as ret:# print("-----1----", ret) # for testpasselse:print(time.time())new_socket.setblocking(False)self.client_socket_list.append(new_socket)#遍历列表中的连接,如果有浏览器发过来数据,那么就处理for client_socket in self.client_socket_list:try:request = client_socket.recv(4096).decode('utf-8')except Exception as ret:# print("------2----", ret) # for testpasselse:if request:#有数据就处理数据self.deal_with_request(request, client_socket)else:#浏览器断开了client_socket.close()self.client_socket_list.remove(client_socket)print(time.time())# print(self.client_socket_list)def deal_with_request(self, request, client_socket):"""为这个浏览器服务器"""if not request:returnrequest_lines = request.splitlines()#这个for循环是为了打印看数据# for i, line in enumerate(request_lines):# print(i, line)# 提取请求的文件(index.html)# GET /a/b/c/d/e/index.html HTTP/1.1ret = re.match(r"([^/]*)([^ ]+)", request_lines[0])if ret:# print("正则提取数据:", ret.group(1))# print("正则提取数据:", ret.group(2))file_name = ret.group(2)if file_name == "/":file_name = "/index.html"# 读取文件数据try:f = open(self.documents_root+file_name, "rb")except:response_body = "file not found, 请输入正确的url"response_header = "HTTP/1.1 404 not found\r\n"response_header += "Content-Type: text/html; charset=utf-8\r\n"response_header += "Content-Length: %d\r\n" % (len(response_body))response_header += "\r\n"# 将header返回给浏览器client_socket.send(response_header.encode('utf-8'))# 将body返回给浏览器client_socket.send(response_body.encode("utf-8"))else:content = f.read()f.close()response_body = contentresponse_header = "HTTP/1.1 200 OK\r\n"response_header += "Content-Length: %d\r\n" % (len(response_body))response_header += "\r\n"# 将header返回给浏览器client_socket.send( response_header.encode('utf-8') + response_body)# 设置服务器服务静态资源时的路径
DOCUMENTS_ROOT = "./html"def main():"""控制web服务器整体"""# python3 xxxx.py 7890if len(sys.argv) == 2:port = sys.argv[1]if port.isdigit():port = int(port)else:print("运行方式如: python3 xxx.py 7890")returnprint("http服务器使用的port:%s" % port)http_server = WSGIServer(port, DOCUMENTS_ROOT)http_server.run_forever()if __name__ == "__main__":main()
Web 静态服务器-6-epoll
import socket
import re
import selectdef service_client(new_socket, request):"""为这个客户端返回数据"""# 1. 接收浏览器发送过来的请求 ,即http请求 # GET / HTTP/1.1# .....# request = new_socket.recv(1024).decode("utf-8")# print(">>>"*50)# print(request)if not request:returnrequest_lines = request.splitlines()print("")print(">"*20)print(request_lines)if not request_lines:return# GET /index.html HTTP/1.1# get post put delfile_name = ""ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])if ret:file_name = ret.group(1)# print("*"*50, file_name)if file_name == "/":file_name = "/index.html"# 2. 返回http格式的数据,给浏览器try:f = open("./html" + file_name, "rb")except:response = "HTTP/1.1 404 NOT FOUND\r\n"response += "\r\n"response += "------file not found-----"new_socket.send(response.encode("utf-8"))else:html_content = f.read()f.close()response_body = html_contentresponse_header = "HTTP/1.1 200 OK\r\n"response_header += "Content-Length:%d\r\n" % len(response_body)response_header += "\r\n"response = response_header.encode("utf-8") + response_bodynew_socket.send(response)def main():"""用来完成整体的控制"""# 1. 创建套接字tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 2. 绑定tcp_server_socket.bind(("", 7890))# 3. 变为监听套接字tcp_server_socket.listen(128)tcp_server_socket.setblocking(False) # 将套接字变为非堵塞# 创建一个epoll对象epl = select.epoll()# 将监听套接字对应的fd注册到epoll中epl.register(tcp_server_socket.fileno(), select.EPOLLIN)fd_event_dict = dict()while True:fd_event_list = epl.poll() # 默认会堵塞,直到 os监测到数据到来 通过事件通知方式 告诉这个程序,此时才会解堵塞# [(fd, event), (套接字对应的文件描述符, 这个文件描述符到底是什么事件 例如 可以调用recv接收等)]for fd, event in fd_event_list:# 等待新客户端的链接if fd == tcp_server_socket.fileno():new_socket, client_addr = tcp_server_socket.accept()epl.register(new_socket.fileno(), select.EPOLLIN)fd_event_dict[new_socket.fileno()] = new_socket #字典,键是filenoelif event==select.EPOLLIN:# 判断已经链接的客户端是否有数据发送过来#如何通过不遍历来定位socketrecv_data = fd_event_dict[fd].recv(4096).decode("utf-8")if recv_data:service_client(fd_event_dict[fd], recv_data)else:fd_event_dict[fd].close()epl.unregister(fd)del fd_event_dict[fd] #从字典中移除# 关闭监听套接字tcp_server_socket.close()if __name__ == "__main__":main()
Web 静态服务器-7-gevent 版
import socket
import re
import gevent
from gevent import monkey# monkey组件
monkey.patch_all()def service_client(new_socket):'''为客户端返回数据'''# 接收http请求request = new_socket.recv(1024).decode('utf-8')if request:request_lines = request.splitlines()print("")print(">" * 20)print(request_lines)file_name = ""ret = re.match(r"[^/]+(/[^ ]*)", request_lines[0])if ret:file_name = ret.group(1)if file_name == "/":file_name = "/index.html"print(file_name)try:f = open("./html" + file_name, "rb")except:response = "HTTP/1.1 404 NOT FOUND\r\n"response += "\r\n"response += "-------file not found-------"new_socket.send(response)else:html_content = f.read()f.close()response = "HTTP/1.1 200 OK\r\n"response += "\r\n"new_socket.send(response.encode('utf-8'))new_socket.send(html_content)new_socket.close()def main():'''创建套接字'''# 初始化tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)# 绑定tcp_server_socket.bind(("", 7890))# 激活tcp_server_socket.listen(128)while True:new_socket, socket_addr = tcp_server_socket.accept()gevent.spawn(service_client, new_socket)tcp_server_socket.close()if __name__ == '__main__':main()
相关文章:

12 正则表达式 | HTTP协议相关介绍
文章目录 正则表达式re模块最基础操作(匹配开头)匹配单个字符匹配多个字符匹配开头结尾匹配分组对于group的理解r的作用re 模块高级用法compilesearchfindall易错点 sub直接替换函数替换 split 根据匹配进行切割字符串,并返回一个列表 python…...

【C语言】数组概述
🚩纸上得来终觉浅, 绝知此事要躬行。 🌟主页:June-Frost 🚀专栏:C语言 🔥该篇将带你了解 一维数组,二维数组等相关知识。 目录: 📘前言:…...

8. 实现业务功能--用户注册
目录 1. 顺序图 2. 参数要求 3. 接口规范 4. 创建扩展 Mapper.xml 5. 修改 DAO 6. 创建 Service 接口 7. 实现接口 8. 测试接口 9. 实现 Controller 9.1 密码加密处理 10. 实现前端界面 业务实现过程中主要的包和目录及主要功能: model 包:实体对象 d…...
深入浅出Pytorch函数——torch.nn.init.eye_
分类目录:《深入浅出Pytorch函数》总目录 相关文章: 深入浅出Pytorch函数——torch.nn.init.calculate_gain 深入浅出Pytorch函数——torch.nn.init.uniform_ 深入浅出Pytorch函数——torch.nn.init.normal_ 深入浅出Pytorch函数——torch.nn.init.c…...

版本控制工具Git集成IDEA的学习笔记(第一篇Gitee)
目录 一、Gitee的使用 1、注册网站会员 2、用户中心 3、创建远程仓库 4、配置SSH免密登录 二、集成IDEA,Git项目搭建 1、本地仓库搭建 1)创建一个新项目 2)打开终端,在当前目录新建一个Git代码库 3)忽略文件 …...
【链表】 61. 旋转链表
61. 旋转链表 解题思路 首先计算出链表长度将链表长度进行取余遍历链表 对链表进行分割 得到两个子链表重新连接两个链表比如1 2 3 4 5 k 2 进行分割得到 1 2 3 和 4 5两个子链表 /*** Definition for singly-linked list.* public class ListNode {* int val;* Lis…...
深入浅出Pytorch函数——torch.nn.init.kaiming_uniform_
分类目录:《深入浅出Pytorch函数》总目录 相关文章: 深入浅出Pytorch函数——torch.nn.init.calculate_gain 深入浅出Pytorch函数——torch.nn.init.uniform_ 深入浅出Pytorch函数——torch.nn.init.normal_ 深入浅出Pytorch函数——torch.nn.init.c…...
查询Oracle和MySQL数据库中当前所有连接信息
查询Oracle当前所有连接信息: SELECTs.sid AS 会话ID,s.serial# AS 序列号,s.username AS 用户名,s.osuser AS 操作系统用户,s.machine AS 客户端机器,s.program AS 客户端程序,s.status AS 会话状态,s.sql_id AS 正在执行的SQL_ID,t.sql_text AS 正在执行的SQL文本…...

Android glide框架及框架涉及到的设计模式
目录 原文链接Android glide框架 简单使用介绍Glide 框架整体结构设计Glide 框架的优点基本使用:Glide占位符 Android glide框架涉及到的设计模式 原文链接 Android glide框架 简单使用介绍 Glide:快速高效的Android图片加载库,可以自动加载…...

使用yolov5进行安全帽检测填坑指南
参考项目 cGitHub - PeterH0323/Smart_Construction: Base on YOLOv5 Head Person Helmet Detection on Construction Sites,基于目标检测工地安全帽和禁入危险区域识别系统,🚀😆附 YOLOv5 训练自己的…...

【BASH】回顾与知识点梳理(三十二)
【BASH】回顾与知识点梳理 三十二 三十二. SELinux 初探32.1 什么是 SELinux当初设计的目标:避免资源的误用传统的文件权限与账号关系:自主式访问控制, DAC以政策规则订定特定进程读取特定文件:委任式访问控制, MAC 32.2 SELinux 的运作模式安…...

vscode远程调试PHP代码
目录 一、准备工作 二、ssh连接和xdebug配置 1.ssh连接 2.xdebug配置 三、xdebug调试,访问 一、准备工作 1.安装vscode里面的两个扩展 2.安装对应PHP版本的xdebug 去xdebug官方,复制自己的phpinfo源码到方框里,再点击Analyse Xdebug: …...
flink1.17 实现 udf scalarFunctoin get_json_object 支持 非标准化json
特色 相比官方的json_value,该函数支持非标准化json,比如v是个object,但是非标准json会外套一层引号,内部有反引号. eg: {"kkkk2": "{\"kkkk1\":\"vvvvvvv\"}" } 支持value为 100L 这种java格式的bigint. {"k":999L…...

基于VUE3+Layui从头搭建通用后台管理系统(前端篇)九:自定义组件封装下
一、本章内容 续上一张,本章实现一些自定义组件的封装,包括文件上传组件封装、级联选择组件封装、富文本组件封装等。 1. 详细课程地址: 待发布 2. 源码下载地址: 待发布 二、界面预览 三、开发视频 基于VUE3+Layui从头搭建通用后台管...
设计模式详解-装饰器模式
类型:结构型模式 实现原理:装饰器模式通过将对象包装在装饰器类中,并在保持类方法签名完整性的前提下,提供额外功能 作用:动态地给一个对象添加一些额外的职责。增加功能方面,装饰器模式比生成子类更灵活…...

Android5:活动生命周期
创建项目Stopwatch activity_main.xml <?xml version"1.0" encoding"utf-8"?> <LinearLayoutxmlns:android"http://schemas.android.com/apk/res/android"xmlns:tools"http://schemas.android.com/tools"android:layout_w…...
第2章 数据结构和算法概述
2.3线性结构和非线性结构 数据结构包括: 线性结构和非线性结构 2.3.1线性结构 线性结构作为最常用的数据结构,其特点是数据元素之间存在一对一的线性关系线性结构有两种不同的存储结构,即顺序存储结构(数组)和链式存储结构(链表)。顺序存储的线性表称…...

WPF国际化的实现方法(WpfExtensions.Xaml)
https://blog.csdn.net/eyupaopao/article/details/120090431 resx资源文件实现 resx资源文件,实现的过程比第一种复杂,但resx文件本身编辑比较简单,维护起来比较方便。需要用到的框架:WpfExtensions.Xaml 为每种语言添加.resx资…...

【Linux】—— 进程程序替换
目录 序言 (一)替换原理 1、进程角度——见见猪跑 1️⃣ 认识 execl 函数 2、程序角度——看图理解 (二)替换函数 1、命名理解 2、函数理解 1️⃣execlp 2️⃣execv 3️⃣execvp 4️⃣execle 5️⃣execve 6️⃣execve…...

idea创建javaweb项目,jboss下没有web application
看看下图这个地方有没有web application...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)
笔记整理:刘治强,浙江大学硕士生,研究方向为知识图谱表示学习,大语言模型 论文链接:http://arxiv.org/abs/2407.16127 发表会议:ISWC 2024 1. 动机 传统的知识图谱补全(KGC)模型通过…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
【Android】Android 开发 ADB 常用指令
查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 🍺 最新版brew安装慢到怀疑人生?别怕,教你轻松起飞! 最近Homebrew更新至最新版,每次执行 brew 命令时都会自动从官方地址 https://formulae.…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...

【版本控制】GitHub Desktop 入门教程与开源协作全流程解析
目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork(创建个人副本)步骤 2: Clone(克隆…...

npm安装electron下载太慢,导致报错
npm安装electron下载太慢,导致报错 背景 想学习electron框架做个桌面应用,卡在了安装依赖(无语了)。。。一开始以为node版本或者npm版本太低问题,调整版本后还是报错。偶尔执行install命令后,可以开始下载…...