python網路程式設計、套接字、HTTP協議
網路程式設計
網路目的 : 資料的傳輸
網路資料傳輸是一個複雜的過程
OSI 七層模型 --》 網路通訊標準化流程
- 應用層 : 提供使用者服務,具體內容由特定程式規定
- 表示層 : 資料的壓縮優化
- 會話層 : 建立應用連線,選擇傳輸層服務
- 傳輸層 : 提供不同的傳輸服務,流量控制
- 網路層 : 路由選擇,網路互連
- 鏈路層 : 提供鏈路交換,具體訊息以幀傳送
- 物理層 : 物理硬體,介面,網絡卡,線路
osi七層模型優點 : 將功能分開,降低了網路傳輸中的耦合性,每一部分完成自己的功能。可以在開發和實施的過程中各司其職。實現高內聚和低耦合的功能。
高內聚 : 單個模組功能儘量單一
低耦合 : 模組之間儘量減少關聯和影響
四層
- 應用層 : 應用層 表示層 會話層
- 傳輸層 : 傳輸層
- 網路層 : 網路層
- 物理鏈路層: 鏈路層和物理層
五層(tcp/ip模型)
- 應用層 : 應用層 表示層 會話層
- 傳輸層 : 傳輸層
- 網路層 : 網路層
- 鏈路層 : 鏈路層
- 物理層 : 物理層
協議(網路協議):在網路通訊中,各方必須遵守的規定。包括建立什麼樣的連線,訊息結構等
應用層 : TFTP HTTP DNS SMTP
傳輸層 : TCP UDP
網路層 : IP
物理層 : IEEE
網路基本概念
1、主機: "localhost" 表示本臺計算機
網路上 : 只在本地測試使用
'localhost' '127.0.0.1'
如果想在網路上進行測試(自動使用本地可用網絡卡IP)
'0.0.0.0' '' '172.60.50.93'
檢視本地 IP 網路資訊
linux上: ifconfig
win上檢視本地IP: ipconfig
ping www.baidu.com --->14.215.177.38(百度的IP地址)
獲取計算機名稱
socket.gethostname()
'tedu'
獲取主機IP
socket.gethostbyname('localhost')
'127.0.0.1'
2、IP地址
在網路上用於區分一臺計算
IPv4 : 點分十進位制 e.g. 192.168.1.72 0-255
32位二進位制表示
IPv6 : 128
網路連線測試命令: ping 172.60.50.92
特殊IP
127.0.0.1 本地測試IP
0.0.0.0 本地網絡卡通用IP
192.168.1.0 表示當前網段
192.168.1.1 表示閘道器
192.168.1.255 廣播地址
獲取伺服器主機資訊
socket.gethostbyaddr("www.baidu.com")
('127.0.0.1', [], ['119.75.213.61'])
主機 別名 IP地址
將ip十進位制轉化為二進位制
socket.inet_aton("192.168.1.2")
b'\xc0\xa8\x01\x02'
將ip二進位制轉化為十進位制
socket.inet_ntoa(b"\xc0\xa8\x01\x02")
'192.168.1.2'
域名 : 網路伺服器IP地址的名稱
url : 在網路上定位某個資源位置
3、埠號 :埠號是網路地址的一部分,在一個系統中每個網路應用監聽不同的埠,以獲取對應埠傳輸的資訊
數字範圍 : 1--65535
1--255 : 一些眾所周知的埠
256--1023 : 系統應用
1024---65535 : 自用
推薦用 >10000 8888 9999 7777 6666
測試一個軟體埠號
socket.getservbyname('mysql')
3306
socket.getservbyname('http')
80
socket.getservbyname('ssh')
22
傳輸層服務
面向連線的傳輸服務 ---》 tcp協議
傳輸特徵:提供可靠的傳輸服務
可靠性表現: 資料在傳輸過程中,無失序,無差錯,無重複,無丟失
* 傳輸過程中有建立和斷開連線的過程
三次握手:建立資料傳輸兩端的持續連線
1. 客戶端向伺服器發起連線請求(我可以牽你手嗎)
2. 伺服器收到連線請求進行確認,返回報文(可以)
3. 客戶端收到伺服器確認進行連線建立(牽手成功)
四次揮手:斷開連線的兩端,保證資料的傳輸完整
1.主動方傳送報文,告知被動方要斷開連線(我們分手吧,你準備好)
2.被動方返回報文,告知收到請求,準備斷開(知道了)
3.被動方再次傳送報文,告知準備完畢可以斷開(你分手吧)
4.主動方傳送報文完成斷開(分手了)
適用情況:檔案的上傳下載,網路情況良好,需要必須保證可靠性的情況
比如 : 資訊聊天,檔案上傳下載,郵件,網頁獲取
面向無連線的傳輸服務 ---》 udp協議
傳輸特徵 :
* 不保證傳輸的可靠性
* 無需建立三次握手和四次揮手的連線斷開過程
* 訊息的收發比較自由,不受其他約束(請原諒我這一生放蕩不羈愛自由)
適用情況 : 網路情況較差,對可靠性要求不高,收發訊息的兩端不適合建立固定連線
比如 :網路視訊,群聊,傳送廣播
套接字----socket
socket模組的套接字屬性
(s表示一個套接字物件)
s.type 獲取套接字型別 # SocketKind.SOCK_STREAM 流式套接字
s.family 獲取地址族型別 # AddressFamily.AF_INET 獲取地址族型別
s.fileno() 獲取套接字的檔案描述符(每一個IO作業系統都會為其分配一個不同的正整數,該正整數即為此IO作業系統的檔案描述符)
s.getsockname() 獲取套接字繫結的地址 # ('192.168.191.3', 8888)
s.getpeername() 獲取連線套接字另一端的地址 # ('192.168.191.3', 7826)
s.setsockopt(level,optname,value) 設定套接字選項,豐富修改原有套接字功能
引數: level 設定選項的型別 optname 每個選項型別中的子選項 value 為選項設定值
s.getsockopt(level,optname) 獲取套接字選項的值
1 from socket import * 2 s = socket() 3 print(s.type) # SocketKind.SOCK_STREAM 流式套接字 4 print(s.family) # AddressFamily.AF_INET 獲取地址族型別 5 print(s.fileno()) # 376 6 s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) # 設定埠可重用 7 print(s.getsockopt(SOL_SOCKET,SO_REUSEADDR))# 獲取選項值 1 8 s.bind(("192.168.191.3",8888)) 9 print(s.getsockname()) # 獲取繫結的地址 ('192.168.191.3', 8888) 10 s.listen() 11 c,addr = s.accept() # addr也是連結客戶端的地址 12 print(c.getpeername()) # ('192.168.191.3', 7826)獲取連結套接字客戶端地址 13 data = c.recv(1024) 14 print(data) # b'i' 15 c.close() 16 s.close()View Code
socket套接字程式設計
套接字:通過程式語言提供的函式介面進行組合,更簡單的完成基於tcp和udp通訊的網路程式設計
套接字的分類
流式套接字(SOCK_STREAM):傳輸層基於tcp的協議進行通訊
資料報套接字(SOCK_DGRAM):傳輸層基於udp的協議進行通訊
底層套接字(SOCK_RAM):訪問底層協議的套接字
網路收發緩衝區
1、協調讀寫速度、減少和磁碟互動
2、recv和send實際上是從緩衝區獲取內容,和向緩衝區傳送內容
recv()特性
1、如果連線斷開,recv會立即結束阻塞返回空字串
2、當接收快取區為空時會阻塞
3、如果recv一次接收不完緩衝區內容,下次會繼續接收,確保資料不丟失
send()特性
1、如果另一端不存在還試圖使用send進行傳送則會產生BrokenPipError異常
2、當傳送緩衝區滿時會阻塞
本地套接字
作用:用於本地不同程式間的進行資料傳輸
本地套接字的建立流程
1、建立套接字物件
sockfd = socket(AF_UNIX,SOCK_STREAM)
2、繫結本地套接字檔案,如果檔案不存在,則自動建立檔案(繫結套接字檔案)
sockfd.bind(file)
判斷一個資料夾下是否有某個檔案 os.path.exists('./tcp_client.py')
刪除一個檔案 os.remove(file) os.remove(file)
3、監聽 listen
4、接收發送訊息 recv send
from socket import * import os sock_file = './sock' # 使用哪個檔案作為套接字檔案 if os.path.exists(sock_file):# 判斷檔案是否已經存在 os.unlink(sock_file) sockfd = socket(AF_UNIX,SOCK_STREAM) # 建立本地套接字 sockfd.bind(sock_file) # 繫結 sockfd.listen(5) # 監聽 while True: c,addr = sockfd.accept() # 建立連線 while True: data = c.recv(1024) if not data: break print(data.decode()) c.close() sockfd.close()服務端
from socket import * sock_file = "./sock" # 確保通訊兩端用相同的套接字檔案 sockfd = socket(AF_UNIX,SOCK_STREAM) # 建立套接字 sockfd.connect(sock_file) # 連結 while True: msg = input("Msg>>") if msg: sockfd.send(msg.encode()) else: break sockfd.close()客戶端
TCP粘包
產生原因:TCP傳輸採用位元組流的方式,訊息之間沒有邊界,如果傳送的速度比接收速度快,會造成多次傳送的內容被一次接收,形成意義上的誤解即粘包
產生條件:當使用send快速的連續傳送極有可能產生粘包
影響:如果每次傳送的內容代表一個獨立的意思,需要單獨識別,就會產生粘包。但是如果多次傳送的內容就是一個連續的整體,此時就不需要處理。
如何處理:
1、每次傳送後加一個結尾標誌,接收端通過標誌進行判斷
2、傳送一個數據結構
3、每次傳送中間有一個短暫的延遲(有一個間隔)
TCP接收多個客戶端連線,且可以持續傳送訊息
from socket import * sockfd = socket(AF_INET,SOCK_STREAM) #建立套接字 sockfd.bind(('127.0.0.1',9999)) #繫結地址 sockfd.listen(5) #設定監聽 while True: # 等待客戶端連線 print("Waiting for connect...") connfd,addr = sockfd.accept() print("Connect from",addr) while True: # 訊息收發 data = connfd.recv(1024) if not data: break print("Receive:",data.decode()) n = connfd.send(b"Receive your message") print("send %d bytes"%n) connfd.close() # 關閉套接字 sockfd.close()TCP-server
from socket import * sockfd = socket() #建立套接字 sockfd.connect(('127.0.0.1',9999)) #發起連線 while True: #訊息收發 msg = input("Msg>>") if not msg: break sockfd.sendall(msg.encode()) data = sockfd.recv(1024) print(data.decode()) sockfd.close()TCP-client
HTTP
http協議-->超文字傳輸協議 應用層協議,HTTP是基於TCP協議編碼的。
用途:網頁的獲取,基於網站的資料傳輸,基於http協議的資料傳輸
特點
- 應用層協議。傳輸層使用TCP傳輸
- 無狀態協議,不記錄使用者的通訊內容
- http1.1---->http2.0 成熟穩定
工作模式:
使用http雙方均遵守http協議規定傳送接收訊息體
請求方,根據協議組織請求內容給物件
服務方,收到內容按照協議解析
服務方,將回復內容按照協議組織傳送給請求方
請求方,收到回覆根據協議解析
HTTP請求格式
格式: 請求行\ 請求頭\ 空行\ 請求體
1、請求行(熟悉格式及作用): 提供具體的請求類別, 請求內容
GET / index.html / HTTP/1.1
請求類別 請求內容 協議版本
請求種類 : GET 獲取網路資源
POST 提交一定的附加資料,得到返回 結果
HEAD 獲取響應頭
PUT 更新伺服器資源
DELETE 刪除伺服器資源
CONNECT 預留
TRACE 測試
OPTIONS 獲取伺服器效能
2、請求頭 : 對請求內容的具體描述, 以鍵值對的形式對請求資訊進行描述
e.g.
Accept: text/html
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
Connection: keep-alive
3、空行
4、請求體 : 具體的請求引數
(GET引數,或者POST提交的內容)
HTTP響應格式
1、響應行 反饋具體的響應情況
HTTP/1.1 200 OK
版本資訊 響應碼 附加資訊
響應碼 :
- 1xx 提示資訊 表示請求已經接受
- 2xx 響應成功
- 3xx 響應需要重新請定向
- 4xx 客戶端錯誤
- 5xx 伺服器錯誤
常見響應碼 :
- 200 成功
- 404 請求頁面不存在
- 401 沒有訪問許可權
- 500 伺服器發生未知錯誤
- 503 伺服器暫時無法執行
2、響應頭 對響應資訊的具體描述
e.g.
Cache-Control: private
Connection: Keep-Alive
3、空行
4、響應體:將客戶想要的內容進行返回
搭建HTTP本地伺服器
做的是一個本地服務端,接收來自瀏覽器客戶端的請求
# 返回第一行 GET / HTTP/1.1
- 接收http請求; 解析http請求
- 響應一個網頁給客戶端
from socket import * # 處理客戶端請求 def handle(connfd): request = connfd.recv(4096) # 接收請求 # 防止客戶端斷開request為空 if not request: return request_line = request.splitlines()[0] # 返回第一行 GET / HTTP/1.1 info = request_line.decode().split(' ')[1] if info == '/': with open('index.html') as f: response = "HTTP/1.1 200 OK\r\n" # 響應行 response += "Content-Type:text/html\r\n" # 響應頭 response += '\r\n' # 空行 response += f.read() # 響應體 else: response = "HTTP/1.1 404 Not Found\r\n" response += "Content-Type:text/html\r\n" response += '\r\n' response += "<h1>Sorry...</h1>" connfd.send(response.encode()) # 傳送給瀏覽器 sockfd = socket() # 搭建tcp網路 sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) sockfd.bind(('0.0.0.0',8000)) # 繫結地址 sockfd.listen(3) # 設定監聽 while True: connfd,addr = sockfd.accept() # 獲取連線端和地址 handle(connfd) # 處理客戶端請求
在瀏覽器輸入地址:127.0.0.1:8888,即可得到網頁顯示!
&n