Flask框架小程序后端分離開發(fā)學(xué)習(xí)筆記《3》客戶端向服務(wù)器端發(fā)送請(qǐng)求
Flask是使用python的后端,由于小程序需要后端開發(fā),遂學(xué)習(xí)一下后端開發(fā)。
一、為什么請(qǐng)求數(shù)據(jù)需要先編碼
#構(gòu)造一個(gè)HTTP請(qǐng)求
http_request = 'GET / HTTP/1.1\r\nhost:{}\r\n\r\n'.format(host)
#發(fā)送HTTP請(qǐng)求給服務(wù)器
#send函數(shù)只接受bytes 作為參數(shù)
# str.encode把str轉(zhuǎn)換為bytes,編碼是utf-8
request = http_request.encode('utf-8')
可以看到上述代碼構(gòu)建了一個(gè)HTTP請(qǐng)求,在發(fā)送之前需要將發(fā)送之前,使用http_request.encode(‘utf-8’)。
這是因?yàn)槲覀冊(cè)谶@構(gòu)建的請(qǐng)求是字符串文本,而電腦只認(rèn)識(shí)二進(jìn)制0和1,所以需要將其數(shù)據(jù)類型轉(zhuǎn)換為二進(jìn)制類型。
這里的utf-8是一種編碼規(guī)則,utf-8就像小學(xué)學(xué)習(xí)的字典一樣,給每個(gè)字符都定義了一個(gè)對(duì)應(yīng)的二進(jìn)制的值代表它。
例如:在UTF-8編碼中,字符"G"(大寫字母G)用二進(jìn)制表示是 01000111。
當(dāng)然編碼規(guī)則有非常多,除此之外常見的還有ASCII編碼、UNICODE編碼、GBK等。
二、如何recv所有數(shù)據(jù)
# recv 可以接收客戶端發(fā)送過來的數(shù)據(jù)
# 參數(shù)是要接收的字節(jié)數(shù)
# 返回值是一個(gè)bytes類型(隨便寫的,1024是長(zhǎng)度,只接收客戶端發(fā)送的1024字節(jié),我們之后可以用while持續(xù)接收)
request = connection.recv(1024)
上述代碼中的1024是接收請(qǐng)求的字節(jié)長(zhǎng)度,即,一個(gè)請(qǐng)求發(fā)送過來,我只接收1024字節(jié)長(zhǎng)度的數(shù)據(jù)。如果只有這樣的話,我們只能接收到前1024字節(jié)的長(zhǎng)度,如果數(shù)據(jù)長(zhǎng)度大于此,后面的數(shù)據(jù)就沒接收到。
有些朋友就說,那我把這個(gè)長(zhǎng)度設(shè)置的很大很大就得了。理論上,recv 函數(shù)的這個(gè)參數(shù)確實(shí)可以設(shè)置為任何正整數(shù)值。
但是,實(shí)際上接收的數(shù)據(jù)量取決于幾個(gè)因素,包括網(wǎng)絡(luò)條件、套接字類型和對(duì)方發(fā)送數(shù)據(jù)的方式。在實(shí)際應(yīng)用中,常見的做法是設(shè)置這個(gè)值為 4096 或 8192,這通常足以處理大多數(shù)情況下的數(shù)據(jù)傳輸需求。但如果需要,這個(gè)值可以被設(shè)置得更大,比如 65536(64KB)或更高。
重要的是要了解,recv 函數(shù)讀取的是緩沖區(qū)中的數(shù)據(jù),因此設(shè)置的值應(yīng)該與您預(yù)期的數(shù)據(jù)量和緩沖區(qū)大小相匹配。設(shè)置過大的值不會(huì)導(dǎo)致錯(cuò)誤,但可能會(huì)導(dǎo)致不必要的內(nèi)存占用。同時(shí),接收的數(shù)據(jù)可能會(huì)少于請(qǐng)求的字節(jié)數(shù),這并不是異常,而是正?,F(xiàn)象,因?yàn)門CP套接字可能會(huì)分多次接收完整的數(shù)據(jù)。
在代碼中,我們可以通過循環(huán)的方式,分多次接收完整的數(shù)據(jù),如下:
# recv 可以接收客戶端發(fā)送過來的數(shù)據(jù)
buffer_size = 1000
r = b''
while True:
request = connection.recv(buffer_size)
r += request
# 取到的數(shù)據(jù)長(zhǎng)度不夠recv的參數(shù)的時(shí)候,說明數(shù)據(jù)己經(jīng)取完了
if len(request) < buffer_size:
break
有些朋友就會(huì)產(chǎn)生疑惑,request = connection.recv(buffer_size)會(huì)從上一次沒接收完的斷點(diǎn)繼續(xù)接收數(shù)據(jù)嗎?還是一直都重復(fù)接收數(shù)據(jù)的前buffer_size 字節(jié)數(shù)據(jù)呢?
答:
在網(wǎng)絡(luò)編程中,當(dāng)你使用像 recv(buffer_size) 這樣的方法從一個(gè)網(wǎng)絡(luò)連接接收數(shù)據(jù)時(shí),它會(huì)按照你指定的緩沖區(qū)大?。ㄔ谶@個(gè)例子中是 buffer_size)接收數(shù)據(jù)。如果數(shù)據(jù)流中還有更多數(shù)據(jù),但不足以填滿整個(gè)緩沖區(qū),recv 將會(huì)返回當(dāng)前可用的數(shù)據(jù),并且下一次調(diào)用 recv 時(shí)會(huì)繼續(xù)從上次停止的地方接收數(shù)據(jù)。
三、http頭
當(dāng)構(gòu)建好http服務(wù)器時(shí),使用瀏覽器輸入域名和端口訪問時(shí),服務(wù)器端會(huì)發(fā)現(xiàn)有兩次請(qǐng)求過來。如下在代碼旁注釋功能。
D:\python3.10.7\python.exe E:\flask_learn\http1.py
ip and request,('127.0.0.1', 52625)
GET / HTTP/1.1 請(qǐng)求方式與協(xié)議 /路徑是默認(rèn)路徑
Host: localhost:2000 主機(jī)號(hào),一定要有,不然找不到誰發(fā)送的,給不了回復(fù)
Connection: keep-alive TCP保持連接,不用一直重復(fù)連接-斷開。(還有個(gè)屬性是close,即關(guān)閉)
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
User-Agent重要字段,偽裝瀏覽器信息,避免不兼容
Accept: 瀏覽器接收什么類型的數(shù)據(jù)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
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
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
希望可以接收的語言,中英之類
ip and request,('127.0.0.1', 52626)
GET /favicon.ico HTTP/1.1 第二次的這個(gè)路徑不一樣了,
Host: localhost:2000
Connection: keep-alive
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"
sec-ch-ua-mobile: ?0
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
sec-ch-ua-platform: "Windows"
Accept: image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: no-cors
Sec-Fetch-Dest: image
Referer: http://localhost:2000/
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
第二次請(qǐng)求,是請(qǐng)求的圖標(biāo),如下文章來源:http://www.zghlxwxcb.cn/news/detail-815984.html
代碼示例:客戶端向服務(wù)器端發(fā)送請(qǐng)求
# 構(gòu)建一個(gè)簡(jiǎn)單本地http服務(wù)器
import socket
# 這個(gè)程序就是一個(gè)套路程序,套路程序
# 沒必要思考為什么會(huì)龍區(qū)年#記住套路,能用,就夠了
# 運(yùn)行這個(gè)程序后,瀏覽器打開localhost:2000就能訪問了;localhost是本機(jī)域名,本機(jī)ip:127.0.0.1
# 服務(wù)器的host為空字符串,表示接受任意ip地址的連接
# post是端口,這里設(shè)置為2000,隨便選的一個(gè)數(shù)字(盡量1024以上,以下是操作系統(tǒng)保留的端口)
host = ''
port = 2000
#s是—個(gè)socket實(shí)例
s = socket.socket()
#s.bind用于綁定(因?yàn)榉?wù)器有一個(gè)固定的端口,所以需要綁定host和port)
#注意bind 函數(shù)的參數(shù)是一個(gè)tuple
s.bind((host, port))
#用一個(gè)無限循環(huán)采處理請(qǐng)求
while True:
# 套路,先要s.listen開始監(jiān)聽
# 注意參數(shù)5的含義不必關(guān)心
s.listen(5)
# 當(dāng)有客戶端過來連接的時(shí)候,s.accept函數(shù)就會(huì)返回2個(gè)值
# 分別是 連接 和 客戶端ip 地址
# 其實(shí)程序是在這等著接收連接呢
connection, address = s.accept()
# recv 可以接收客戶端發(fā)送過來的數(shù)據(jù)
# 參數(shù)是要接收的字節(jié)數(shù)
# 返回值是一個(gè)bytes類型(隨便寫的,1024是長(zhǎng)度,只接收客戶端發(fā)送的1024字節(jié),我們之后可以用while持續(xù)接收)
buffer_size = 1000
r = b''
while True:
request = connection.recv(buffer_size)
r += request
# 取到的數(shù)據(jù)長(zhǎng)度不夠recv的參數(shù)的時(shí)候,說明數(shù)據(jù)己經(jīng)取完了
if len(request) < buffer_size:
break
# bytes類型調(diào)用decode ( ' utf-8 ')來轉(zhuǎn)成一個(gè)字符串( str)
print('ip and request,{}\n{}'.format(address, request.decode('utf-8')))
# b''表示這是一個(gè)bytes 對(duì)象
response = b'HTTP/1.1 200 hao\r\n\r\n<h1>Hello world!</h1>'
# 用sendall發(fā)送給客戶端
connection.sendall(response)
# 發(fā)送完畢后,關(guān)閉本次連接
connection.close()
#codinq: utf-8
# 構(gòu)造一個(gè)請(qǐng)求,發(fā)送給自己本機(jī)上一個(gè)代碼構(gòu)建的服務(wù)器
import socket
# socket 是操作系統(tǒng)用來進(jìn)行網(wǎng)絡(luò)通信的底層方案#簡(jiǎn)而言之,就是發(fā)送/接收數(shù)據(jù)
#創(chuàng)建一個(gè)socket對(duì)象
#參數(shù)socket.AF_INET表示是ipv4協(xié)議]#參數(shù)socket. sOCK_STREAM表示是tcp協(xié)議
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#這兩個(gè)其實(shí)是默認(rèn)值,所以你可以不寫,如下
#s = socket.socket() socket只能用于http協(xié)議的,不能用于https
#s = ssl. wrap_socket ( socket.socket ( ) ) 這個(gè)用于https
#主機(jī)(域名或者ip)和端口
host = 'localhost'
port = 2000
#用connect函數(shù)連接上主機(jī),參數(shù)是一個(gè)tuple
s.connect( (host,port) )
#連接上后,可以通過這個(gè)函數(shù)得到本機(jī)的ip和端口(本地ip路由器分配得到,本地端口操作系統(tǒng)分配的)
ip, port = s.getsockname()
print('本機(jī)ip 和port {}\{}'.format(ip, port))
#構(gòu)造一個(gè)HTTP請(qǐng)求
http_request = 'GET / HTTP/1.1\r\nhost:{}\r\n\r\n'.format(host)
#發(fā)送HTTP請(qǐng)求給服務(wù)器
#send函數(shù)只接受bytes 作為參數(shù)
# str.encode把str轉(zhuǎn)換為bytes,編碼是utf-8
request = http_request.encode('utf-8')
print('請(qǐng)求',request)
s.send(request)
# 接受服務(wù)器的響應(yīng)數(shù)據(jù)
#參數(shù)是長(zhǎng)度,這里為1023字節(jié)
#所以這里如果服務(wù)器返回的數(shù)據(jù)中超過1023的部分你就得不到了,正經(jīng)一次性最長(zhǎng)大概1500字節(jié),
response = s.recv (1023)
#輸出響應(yīng)的數(shù)據(jù),bytes類型
print('響應(yīng)',response)
#轉(zhuǎn)成str再輸出
print('響應(yīng)的str 格式',response.decode ('utf-8'))
運(yùn)行結(jié)果:
發(fā)送請(qǐng)求的客戶端運(yùn)行結(jié)果:
服務(wù)器端:
可以看到我運(yùn)行了兩次發(fā)送請(qǐng)求的程序,操作系統(tǒng)分配給其程序的兩次端口不一樣。文章來源地址http://www.zghlxwxcb.cn/news/detail-815984.html
到了這里,關(guān)于Flask框架小程序后端分離開發(fā)學(xué)習(xí)筆記《3》客戶端向服務(wù)器端發(fā)送請(qǐng)求的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!