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...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
uniapp中使用aixos 报错
问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...
 
mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
MySQL JOIN 表过多的优化思路
当 MySQL 查询涉及大量表 JOIN 时,性能会显著下降。以下是优化思路和简易实现方法: 一、核心优化思路 减少 JOIN 数量 数据冗余:添加必要的冗余字段(如订单表直接存储用户名)合并表:将频繁关联的小表合并成…...
 
java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里
写一个shell脚本,把局域网内,把能ping通的IP和不能ping通的IP分类,并保存到两个文本文件里 脚本1 #!/bin/bash #定义变量 ip10.1.1 #循环去ping主机的IP for ((i1;i<10;i)) doping -c1 $ip.$i &>/dev/null[ $? -eq 0 ] &&am…...
02-性能方案设计
需求分析与测试设计 根据具体的性能测试需求,确定测试类型,以及压测的模块(web/mysql/redis/系统整体)前期要与相关人员充分沟通,初步确定压测方案及具体的性能指标QA完成性能测试设计后,需产出测试方案文档发送邮件到项目组&…...
比较数据迁移后MySQL数据库和ClickHouse数据仓库中的表
设计一个MySQL数据库和Clickhouse数据仓库的表数据比较的详细程序流程,两张表是相同的结构,都有整型主键id字段,需要每次从数据库分批取得2000条数据,用于比较,比较操作的同时可以再取2000条数据,等上一次比较完成之后,开始比较,直到比较完所有的数据。比较操作需要比较…...
 
夏普比率(Sharpe ratio)
具有投资常识的人都明白,投资光看收益是不够的,还要看承受的风险,也就是收益风险比。 夏普比率描述的正是这个概念,即每承受一单位的总风险,会产生多少超额的报酬。 用数学公式描述就是: 其中࿱…...
