注:需要了解網(wǎng)絡(luò)編程基本概念的讀者钝荡,可以閱讀小編的 Python網(wǎng)絡(luò)編程(一)
Python 網(wǎng)絡(luò)編程(一)的最后介紹了傳輸層的兩個協(xié)議:TCP & UDP督函。TCP提供面向連接的可靠的傳輸層服務(wù)锹锰,創(chuàng)建連接時三次握手朋截,斷開連接時四次揮手嚎朽;UDP提供無連接的不可靠的傳輸層服務(wù)逾苫,適用于對實時性要求較高卿城,可靠性較低的網(wǎng)絡(luò)傳輸情況,如視頻傳輸铅搓。
1.TCP網(wǎng)絡(luò)編程實現(xiàn)
1)服務(wù)端
①創(chuàng)建流式套接字
s = socket.socket(socket.AF_INIT,socket.SOCK_STREAM)
或者:
s = socket.socket()
備注:AF_INIT--->地址族協(xié)議類型:IPV4
SOCK_STREAM--->套接字類型:流式套接字
②綁定IP和端口
ADDR = ('IP地址',端口)
s.bind(ADDR)
③設(shè)置監(jiān)聽
s.listen(5)
將套接字設(shè)置為"監(jiān)聽套接字"瑟押,并設(shè)置"監(jiān)聽隊列"長度為5
④阻塞等待客戶端連接
connfd,addr = s.accept()
返回值:connfd--->用于和指定客戶端通信的新套接字;addr--->連接客戶端的IP
⑤消息收發(fā)
接收:data = connfd.recv(bufersize).decode()
發(fā)送:n = connfd.send(data.encode())
⑥關(guān)閉套接字:
connfd.close()
s.close()
服務(wù)端代碼實現(xiàn):
'''示意服務(wù)端
接收客戶端消息并打印
當(dāng)客戶端斷開與服務(wù)端的連接后星掰,recv立即結(jié)束阻塞勉耀,返回空字符串
'''
import socket
# 創(chuàng)建套流式接字
sockfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ADDR = ('127.0.0.1', 8888)
# 綁定IP和端口
sockfd.bind(ADDR)
# 設(shè)置監(jiān)聽
sockfd.listen(5)
while True:
print('Waiting for Connection...')
# 阻塞等待客戶端連接s
connfd,addr = sockfd.accept()
print('Connect to client:',addr)
while True:
# 接收
data = connfd.recv(1024).decode()
if not data:
break
print('Receive:',data)
# 發(fā)送,send返回發(fā)送的字節(jié)數(shù)
n = connfd.send('Receive your message!'.encode())
print('feedback %d byte' % n )
# 關(guān)閉套接字
connfd.close()
# 關(guān)閉套接字
sockfd.close()
2)客戶端
- 無需綁定IP和端口蹋偏,端口由系統(tǒng)自動分配
- 無需設(shè)置監(jiān)聽
- 無需創(chuàng)建新的套接字
流程:
①創(chuàng)建流式套接字
②發(fā)起連接請求
ADDR = ('IP',端口)
s.connect(ADDR)
③消息收發(fā)
n = s.send(data.encode())
data = s.recv(bufersize).decode()
④關(guān)閉套接字
s.close()
客戶端代碼實現(xiàn):
'''示意客戶端
向服務(wù)端發(fā)送消息便斥,接收并打印服務(wù)端返回的消息
客戶端輸入空行或者'q'時請求與服務(wù)端斷開
'''
import socket
# 創(chuàng)建套流式接字
sockfd = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ADDR = ("127.0.0.1", 8888)
# 向服務(wù)端發(fā)起連接請求
sockfd.connect(ADDR)
while True:
data = input("發(fā)送消息>>>") or 'q'
if data == 'q':
break
# 發(fā)送
sockfd.send(data.encode())
# 接收
back = sockfd.recv(1024)
print(back.decode())
sockfd.close()
2.UDP網(wǎng)絡(luò)編程實現(xiàn)
1)服務(wù)端
①創(chuàng)建數(shù)據(jù)報套接字:
sockfd = socket(AF_INET,SOCK_DGRAM)
②綁定服務(wù)端地址(服務(wù)端須要有固定的IP和端口號):
ADDR = ('IP地址',端口)
sockfd.bind(ADDR)
③消息的收發(fā):
data,addr = sockfd.recvfrom(buffersize)
n = sockfd.sendto(data,ADDR)
④關(guān)閉套接字:
sockfd.close()
服務(wù)端代碼實現(xiàn):
'''UDP服務(wù)端
綁定命令行輸入的服務(wù)端地址
接收客戶端發(fā)送的消息,并返回
'''
import socket
import sys
# 判斷命令行參數(shù)個數(shù)
if len(sys.argv) < 3:
print('''
argv is error!!
runs as
python3 udp_client.py 127.0.0.1 8888
''')
sys.exit(0)
HOST = sys.argv[1]
# 端口為整數(shù)
PORT = int(sys.argv[2])
# 服務(wù)端地址
ADDR = (HOST,PORT)
# 創(chuàng)建數(shù)據(jù)報套接字
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# 綁定服務(wù)端地址
s.bind(ADDR)
while True:
# 接收
data,addr = s.recvfrom(1024)
print("Receive from {} : {}".format(addr,data.decode()))
# 發(fā)送
s.sendto('Receive your message'.encode(),addr)
s.close()
2)客戶端
①創(chuàng)建套接字
sockfd = socket(AF_INET,SOCK_DGRAM)
②數(shù)據(jù)收發(fā)
data,addr = sockfd.recvfrom(bufersize).decode()
n = sockfd.sendto(data,ADDR)
③關(guān)閉套接字
sockfd.close()
客戶端代碼實現(xiàn):
'''UDP客戶端
向命令行輸入的服務(wù)端地址發(fā)送消息
同時接收該服務(wù)端發(fā)來的消息
'''
import socket
import sys
# 判斷命令行參數(shù)個數(shù)
if len(sys.argv) < 3:
print('''
argv is error!!
runs as
python3 udp_client.py 127.0.0.1 8888
''')
sys.exit(0)
HOST = sys.argv[1]
# 端口為整數(shù)
PORT = int(sys.argv[2])
# 服務(wù)端地址
ADDR = (HOST,PORT)
# 創(chuàng)建數(shù)據(jù)報套接字
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
while True:
data = input("Send the message >>>") or 'q'
if data == 'q':
break
# 發(fā)送
s.sendto(data.encode(),ADDR)
# 接收
r_data,addr = s.recvfrom(1024)
print(r_data.decode())
# 關(guān)閉套接字
s.close()
3.相關(guān)概念
1) 網(wǎng)絡(luò)收發(fā)緩沖區(qū):
緩沖區(qū)的功能:①協(xié)調(diào)讀寫速度威始,②減少和磁盤的交互
recv和send實際上是從緩沖區(qū)獲取內(nèi)容枢纠,向緩沖區(qū)發(fā)送內(nèi)容
2) TCP粘包:
原因:TCP采用字節(jié)流的方式,消息之間沒有邊界黎棠,如果發(fā)送和接收速度不匹配晋渺,會造成多次發(fā)送的內(nèi)容被一次接收,形成意義上的誤解即粘包脓斩。
條件:當(dāng)使用send快速的連續(xù)發(fā)送極有可能產(chǎn)生粘包
如何處理:
①發(fā)送時添加結(jié)束位標志木西,接收端通過標志進行判斷
②發(fā)送一個數(shù)據(jù)結(jié)構(gòu)
③當(dāng)連續(xù)發(fā)送時每次發(fā)送有一個短暫延遲 sleep(0.1)
3) 'TCP流式套接字'和'UDP數(shù)據(jù)報套接字'的區(qū)別
①TCP傳輸數(shù)據(jù)使用字節(jié)流的方式傳輸,UDP是數(shù)據(jù)報,因此TCP會產(chǎn)生粘包現(xiàn)象随静。
②TCP對網(wǎng)絡(luò)條件要求高八千,UDP更適合實時傳輸
③TCP傳輸是建立在連接的基礎(chǔ)上,保證傳輸?shù)目煽啃粤敲停欢鳸DP一次接收一個數(shù)據(jù)報恋捆,不保證完整性
④TCP使用listen accept建立連接,udp不需要
⑤收發(fā)消息TCP使用recv和send重绷,UDP使用recvfrom和sendto
4.下節(jié)預(yù)告
Python 網(wǎng)絡(luò)編程(三)將為大家介紹TCP流式套接字的典型應(yīng)用:文件傳輸以及http網(wǎng)頁傳輸沸停;UDP的典型應(yīng)用:局域網(wǎng)發(fā)送廣播消息和接收廣播消息~