Python網路程式設計零基礎2
因為基礎內容較多所以內容回顧較多(程式碼放到下面)
-
網路效能衡量指標
- 頻寬:通訊通道上支援的最高資料傳輸頻率
- 速率:每秒中傳輸多少bits資料
- 吞吞量:單位時間內通過某個網路資料量
- 時延:傳輸時延,傳播時延,處理時延,排隊時延
-
常用概念
- 伺服器:提供服務的一方
- 客戶端:請求服務的一方
- 通訊:資料傳輸過程
- 協議:資料組織、編碼、傳輸、校驗、解碼規則
-
通訊過程的幾個問題
- DNS:域名系統,獲得對方IP地址
域名 ====> IP地址 - 聯絡上對方:客戶端主動發起連線
- 通訊過程的正確性:協議來保證
- 如何正確理解對方的意思:協議來保證
- 關閉連線
- DNS:域名系統,獲得對方IP地址
-
OSI/ISO七層參考模型
- 應用層:提供使用者服務,負責具體功能的實現
- 表示層:資料格式的壓縮,優化,加密
- 會話層:建立應用的連線,選擇合適的傳輸服務
- *傳輸層:提供傳輸服務(程式之間)
- 網路層:主機到主機間的資料傳輸
路由,分段 - 鏈路層:進行資料交換,控制具體收發
相鄰主機之間的通訊 - 物理層:定義機械、物理、電信標準
-
TCP/IP四層模型
- 應用層(應用層、表示層、會話層)
- 傳輸層(傳輸層)
- 網路層(網路層)
- 物理鏈路層(資料鏈路層、物理層)
- 將第一次分為兩層,就是五層模型
-
IP地址:網路上唯一的地址
-
組成:32bits
-
表示方式:點分十進位制的表示方式
將每8bits分成一組,使用十進位制的數字表示
11111111 11111111 11111111 11111111
255.255.255.255 -
特殊IP
127.0.0.1 本機IP地址
0.0.0.0 本機上繫結的所有IP地址
網路地址 主機地址全部為0
192.168.1.5
192.168.1.0
廣播地址 主機地址部分全部為1
192.168.1.5
192.168.1.255 -
私有地址:預留的一部分地址段,內網使用
A類:10.0.0.1–10.255.255.254
B類:172.16.0.1–172.31.255.254
C類:192.168.0.1–192.168.255.254
-
-
埠號(埠地址):標識不同的服務
組成:16bits整數
範圍:1~65535
系統保留:1~1023 系統保留埠 -
傳輸層:程序到程序的資料傳輸
-
TCP:傳輸控制協議
特點:面向連線,可靠傳輸,流量控制
適用:資料量大,可靠性要求較高的通訊
傳輸效率較低 -
UDP:使用者報文協議
特點:無連線,不可靠傳輸
適用:資料量小,可靠性要求不高
傳輸效率較高
-
-
建立、釋放連線過程
-
建立:三次握手
- 客戶端發起連線請求,帶上自己的序列號N
- 伺服器接收連線,產生應答
N+1:期望收到你下一個包的序號N+1
K:我自己的序列號K - 客戶端傳送確認
K+1:期望收到你下一個包的序號是K+1
-
釋放:四次揮手
- 主動方發起斷開請求
- 被動方應答,表示準備斷開
- 被動方發出訊息,表示可以斷開
- 主動方發出指令,確認斷開
-
-
伺服器、客戶端工作流程
-
伺服器:
建立套接字->繫結->監聽->接收->資料傳輸->關閉 -
客戶端:
建立套接字->連線->資料傳輸->關閉
-
-
網路套接字(socket)
-
什麼是套接字
用於計算機網路通訊的一種介面 -
套接字的型別
-
流式套接字(SOCK_STREAM)
可靠性傳輸、面向連線、資料無差錯傳送
資料是一個位元組流 -
資料報套接字
不可靠、無連線的資料傳輸
資料是一個一個獨立的資料報,每次傳送都是
一個數據報
-
-
常用方法
socket() 建立套接字
bind() 繫結地址
listen() 監聽
accept() 接收連線
send() 傳送
recv() 接收
connect() 連線(只能用於客戶端)
close() 關閉
-
今天的內容:
1. 服務迴圈接收、客戶端迴圈傳送
將伺服器的recv(), 客戶端的send()放入迴圈中
2. 網路緩衝區
- 每個socket被建立後,都會分配兩個緩衝區
輸入、輸出緩衝區,分別用於資料收、發
- 當send函式呼叫時,先將資料寫入緩衝區
send函式就認為傳送成功,並返回
由TCP協議決定何時從緩衝區傳送到對方
- 當呼叫recv函式接收時,從輸入緩衝區讀取資料
而不是直接從網路讀取
- 優點:提高讀寫效率,協調收發速度
3. 粘包
- 粘包:多個數據包的資料到達接收緩衝區
後一個包的頭接著上一個包尾
例如:
AAABBB (兩個資料包)
- 什麼時候需要處理粘包
如果多個數據包資料不同的分組,需要處理
屬於同一個分組,不需要處理
- 如何處理:
方式一:在資料包的頭部、尾部增加特殊標記
讀取資料的時候,讀取兩個筆記中間的部分
{2H:AAA:}{2H:BBB:}
方式二:在每個資料包頭增加長度資訊
根據長度決定讀取多少個位元組資料
例如:頭部採用4個位元組表示長度
0003AAA0004BBBB
0003:表示該包資料部分長度為3
讀取3個位元組:AAA
0004:表示該包資料部分長度為4
讀取4個位元組:BBBB
**4. UDP套接字 **
1)面向無連線、不可靠傳輸、傳輸效率較高、
適用於資料量少、可靠性要求不高的資料傳輸
2)UDP工作流程
- 伺服器
建立套接字->繫結地址->收發資料->關閉
* 沒有listen,accept過程
- 客戶端
建立套接字->傳送、接收資料->關閉
3)UDP套接字的主要函式
- 建立套接字
sockfd = socket(AF_INET, SOCK_DGRAM)
引數:AF_INET 協議族,通過IP、埠
地址進行通訊
SOCK_DGRAM 套接字型別
表示使用者報文套接字
- 繫結(伺服器端)
sockfd.bind(address)
引數:address 繫結地址,由IP、埠構成
- 接收:recvfrom
data, addr = recvfrom(buffersize)
引數:buffersize 每次最多接收的位元組數
返回值:data 接收的內容
addr 訊息傳送者的地址
- 傳送:sendto
n = sendto(data, addr)
引數:data 要傳送的資料,bytes格式
addr 接收者地址
返回值:實際傳送的位元組數
5. TCP和UDP套接字的對比
TCP UDP
位元組流 資料報
會產生粘包 不會產生粘包
可靠傳輸 不可靠傳輸
需要listen 不需要
accept
客戶端connect 客戶端不需要connect
recv,send recvfrom,sendto
6. socket常用的屬性方法
family屬性:地址型別
AF_INET:傳送方、接收方通過IP、埠來標識
IPV4
AF_UNIX:本地socket,傳送方、接收方位於
同一臺機器,地址為檔案路徑
AF_INET6:IPV6地址型別
type:socket型別
SOCK_STREAM: 流式socket
SOCK_DGRAM: 資料報socket
getpeername(): 獲取連線端的地址資訊
getsockname(): 獲取套接字繫結的地址
fileno(): 獲取套接字的檔案描述符
檔案描述符:作業系統核心分配的一個非負整數
每個檔案描述符代表一個檔案
當開啟或新建一個檔案
核心都會返回一個檔案描述符
檔案讀寫、網路socket資料收發、
外設操作都是通過檔案描述符進行
例如:
- 標準輸入:sys.stdin 0 預設鍵盤
- 標準輸出:sys.stdout 1 預設螢幕
setsockopt(level, optname, value)
功能:設定套接字的選項
不同的套接字選項代表不同的功能
引數:
level: 設定選項的型別
optname: 選項型別中的子型別
value:為選項設定的值
7. 廣播
1)什麼是廣播
- 單播:一點發送、一點接收
- 多播:也稱組播,一點發送,特定的多個點接收
- 廣播:一點發送,該網段內容所有的節點接收
廣播地址:IP地址的主機地址部分全部為1
例如:192.168.1.3,轉換成二進位制
二進位制:11000000 10101000 00000001 00000011
|------- 網路地址 -------| |-主機-|
廣播地址:把主機地址部分全部替換成1
11000000 10101000 00000001 11111111
192.168.1.255
2)Python中如何實現廣播
- socket既傳送也接收廣播的時候
首先設定SO_BROADCAST選項為1,來支援廣播通訊
setsockopt(SOL_SOCKET, SO_BROADCAST, 1)
- 傳送廣播,使用特殊地址broadcast
而不是標準的IP地址和主機名
基於UDP會話Server程式碼示例
# UDP 服務端 Server
import socket,time
tiMe = time.asctime()[11:19]
# 建立socket連線
sk = socket.socket(type=socket.SOCK_DGRAM)
# 繫結
sk.bind(('127.0.0.1',9999))
print('當前UDP服務端準備就緒',tiMe)
# 傳送訊息迴圈體
while True:
# 獲取訊息和地址
msg,addr = sk.recvfrom(1024)
print('當前基於UDP%s連線已經建立'%addr[0])
#列印訊息
print('來自客戶端',addr[0],'的連線:| ',msg.decode('utf-8'),
' |接收時間:',tiMe)
#傳送訊息
data = input('輸入要傳送的UDP:')
sk.sendto(data.encode(),addr)
# 判斷退出
if not msg or not data:
break
# 關閉連線
sk.close()
基於UDP會話Client程式碼示例
#UDP 客戶端Client
import socket,time
tiMe = time.asctime()[11:19]
# 建立套接字
sk = socket.socket(type=socket.SOCK_DGRAM)
# 繫結IP
ip_port = ('127.0.0.1',9999)
print('當前UDP客戶端準備就緒',tiMe)
# 傳送訊息迴圈體
while True:
# 傳送訊息
data = input('輸入要傳送的UDP:')
if not data:
sk.sendto(''.encode(),ip_port)
break
sk.sendto(data.encode(),ip_port)
# 接收訊息
msg,addr = sk.recvfrom(1024)
print('當前基於UDP%s連線已經建立'%addr[0])
print('來自伺服器',addr[0],'的回覆:| ',msg.decode('utf-8'),
' |接收時間:',tiMe)
if not msg:
break
# 關閉連線
sk.close()