1、socker層 (在程序中就是一個模塊功能能夠直接導入使用)python
Socker 是應用層與TCP/IP協議族通訊的中間軟件抽象層,它是一組接口,其實就i是一個門面模式,把複雜的協議放在socker後面。git
IP地址: 127.0.0.1是本機回還地址,只能本身識別本身,其餘人沒法訪問,用於python代碼客戶端和服務端的測試shell
2、 套接字(socker)的發展史json
1:基於文件類型的套接字家族:AF_UNIX(一切皆文件)網絡
2:基於網絡類型的套接字家族:AF_INET(被用於ipv6)socket
3、tcp協議和udp協議tcp
TCP:可靠的面向鏈接的協議(如:打電話),傳輸效率低於全雙工通訊ide
UDP:不可靠的、無鏈接的服務,傳輸效率高,一對一,一對多函數
TCP和TCP間的通訊測試
4、套接字(socker)的使用
client(客戶端)關鍵字:connect send recv
server(服務端)關鍵字:bind listen accept recv send conn.slose()
TCP協議是基於鏈接的,必須先啓動服務端,而後在啓動客戶端去鏈接服務端:
server:服務端
import socket """" 實現服務端24小時不間斷的服務功能,固定的IP和port """ server = socket.socket() # 生成一個對象 server.bind(('127.0.0.1',8080)) # 綁定ip和port server.listen(5) # 半鏈接池 """ 用兩層循環 實現客戶端與服務端之間的循環交互式 """ while True: conn,addr = server.accept() # 循環接收用戶端的請求 print(addr) while True: try:# 解決報錯拋出的異常處理(位置,類型,) data = conn.recv(1024) print(data) if len(data) == 0 : break # 針對mac和 Linux 客戶端異常退出以後 conn.send(data.upper()) # 返轉成大寫 except ConnectionRefusedError as e: # 提示的報錯信息 print(e) break conn.close()
client:客戶端
import socket client = socket.socket() client.connect(('127.0.0.1',8080)) while True: msg = input("請輸入:").encode("utf-8") if len (msg) == 0: continue client.send(msg) data = client.recv(1024) print(data)
send(發出)與recv(接收)對應關係,不能出現兩邊都相同的狀況
recv 是跟內存要數據的,
TCP的特色:
會將數據量比較小的而且時間間隔比較短的數據
一次性打包發送給對方
利用socker模塊,用代碼實現了服務端與客戶端的實際交互狀況,IP地址和pore端口地址必須徹底匹配,其中注意一些概念 如 server.listen(5) # 半鏈接池 指的是規定客戶端訪問的量,報錯的異常處理
一個做爲接收端,一個做爲反饋請求端,所用的參數也不同
5、黏包
黏包:指的是當客戶端同時請求所傳輸的內容過大,過長是,服務端反饋的結果可能只有其中的一部分,顯示不全,在執行其餘命令的時候又接受收到了以前執行的另一部分的結果。
補充的subprocess子進程模塊
import subprocess cmd = input('cmd>>>:') obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) print(obj.stdout.read().decode('gbk')) # 正確命令返回的結果 print(obj.stderr.read().decode('gbk')) # 錯誤的命令返回的結果 # subprocess獲取到的數據 拿完就沒有了 不能重複的拿 # print(obj.stdout.read().decode('gbk')) # 正確命令返回的結果 # print(obj.stderr.read().decode('gbk')) # 錯誤的命令返回的結果
只能單次獲取請求的數據,取完就沒了, 不能重複的取
該模塊能夠在python解釋器裏,實現終端的請求命令行執行並打印結果:
它的功能以及經常使用的操做
# subprocess模塊 # 1.用戶經過網絡鏈接上了你的這臺電腦 # 2.用戶輸入相應的命令 基於網絡發送給了你這臺電腦上某個程序 # 3.獲取用戶命令 裏面subprocess執行該用戶命令 # 4.將執行結果再基於網絡發送給用戶 # 這樣就實現 用戶遠程操做你這臺電腦的操做 # ''
該模塊能夠把一個類型,如數字,轉成固定長度的bytes
struct.pack:打包
struct.unpack解包
有「i」 ’q‘等模式,是處理數據大小的等級
dict_size = struct.unpack('i',header_dict)[0] # 解包的時候必定要加上索引0
服務端:結合json模塊 dumps序列化成一個字典,製做一個報頭
客戶端:用loads把字典反序列化成字符串出來,讀取報頭內容
當有黏包現象存在時如何解決?(即數據過大過長時)
服務端client:
1:先製做一個發送給客戶端色字典
2:製做字典的報頭
3:發送字典的報頭
4:再發真實的數據
客戶端srever:
1.先接受字典的報頭
2.解析拿到字典的數據長度
3.接受字典
4.從字典中獲取真實數據的長度
5.接受真實數據
用字典打包好報頭,獲取固定的長度後在傳輸
內置函數構造:
簡單黏包問題的存在,接收的數據和傳出去的不同
client 客戶端
import socket client = socket.socket() # 拿電話 client.connect(('127.0.0.1',8080)) # 撥號 寫的是對方的ip和port client.send(b'hello') client.send(b'baby')
server:服務端
import socket server = socket.socket() # 買手機 不傳參數默認用的就是TCP協議 server.bind(('127.0.0.1',8080)) # bind((host,port)) 插電話卡 綁定ip和端口 server.listen(5) # 開機 半鏈接池 conn, addr = server.accept() # 接聽電話 等着別人給你打電話 阻塞 data = conn.recv(5) # 聽別人說話 接收1024個字節數據 阻塞 print(data) data = conn.recv(5) # 聽別人說話 接收1024個字節數據 阻塞 print(data)
注意:只有TCP有粘包現象,UDP永遠不會粘包
實現解決黏包的問題?
客戶端:client
import socket import struct import json client = socket.socket() client.connect(('127.0.0.1',8080)) while True: msg = input('>>>:').encode('utf-8') if len(msg) == 0:continue client.send(msg) # 1.先接受字典報頭 header_dict = client.recv(4) # 2.解析報頭 獲取字典的長度 dict_size = struct.unpack('i',header_dict)[0] # 解包的時候必定要加上索引0 # 3.接收字典數據 dict_bytes = client.recv(dict_size) dict_json = json.loads(dict_bytes.decode('utf-8')) # 4.從字典中獲取信息 print(dict_json) recv_size = 0 real_data = b'' while recv_size < dict_json.get('file_size'): # real_size = 102400 data = client.recv(1024) real_data += data recv_size += len(data) print(real_data.decode('gbk')) """ 1.如何將對方發送的數據收乾淨
服務端:server
import socket import subprocess import struct import json server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn, addr = server.accept() while True: try: cmd = conn.recv(1024) if len(cmd) == 0:break cmd = cmd.decode('utf-8') obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE) res = obj.stdout.read() + obj.stderr.read() d = {'name':'jason','file_size':len(res),'info':'asdhjkshasdad'} json_d = json.dumps(d) # 1.先製做一個字典的報頭 header = struct.pack('i',len(json_d)) # 2.發送字典報頭 conn.send(header) # 3.發送字典 conn.send(json_d.encode('utf-8')) # 4.再發真實數據 conn.send(res) # conn.send(obj.stdout.read()) # conn.send(obj.stderr.read()) except ConnectionResetError: break conn.close()
文件的上傳和下載
(重點掌握,也是基於tcp協議的一個粘包問題,利用打包和解包)
import socket import json import os import struct client= socket.socket() client.connect(('127.0.0.1',8080)) while True: MOVIE_DIR = r'E:\python脫產10期視頻\day29\視頻' movie_list= os.listdir(MOVIE_DIR) for i,movie in enumerate(movie_list,1): # 枚舉列出全部數據 print(i,movie) # 用戶選擇 choice=input("請選擇你要下載的視頻>>:") if choice.isdigit(): choice = int(choice)-1 if choice in range(0,len(movie_list)): path = movie_list[choice] file_path = os.path.join(MOVIE_DIR,path) file_size = os.path.getsize(file_path) res_d={ 'file_name':"精彩視頻要你好看.MP4", 'file_size':file_size, 'msg':"作個年少有爲的青年" } json_d = json.dumps(res_d) json_bytes = json_d.encode('utf-8') header = struct.pack('i',len(json_bytes)) client.send(header) client.send(json_bytes) with open (file_path,'rb') as f: for line in f : client.send(line) else: print("not in range") else: print("must be a number") """ if else 的使用 先把正確邏輯的代碼寫好,後面再考慮搭配來寫else的其餘狀況 """
import json import socket import struct server = socket.socket() server.bind(('127.0.0.1',8080)) server.listen(5) while True: conn,addr = server.accept() while True: try: header_len = conn.recv(4) # 解析字典報頭 header_len=struct.unpack('i',header_len)[0] # 再接收字典數據 header_dic = conn.recv(header_len) real_dic = json.loads(header_dic.decode('utf-8')) total_size = real_dic.get('file_size') recv_size = 0 with open (real_dic.get('file_name'),'wb') as f: while recv_size < total_size: data = conn.recv(1024) f.write(data) recv_size+= len(data) print("上傳成功!") except ConnectionRefusedError as e: print(e) break conn.close()
簡易版本的QQ實現:
QQ服務端:
import socket server = socket.socket(type=socket.SOCK_DGRAM) server.bind(('127.0.0.1',8080)) while True: data,adder = server.recvfrom(1024) msg= input(">>>>:") server.sendto(msg.encode('utf-8'),adder)
QQ客戶端:
import socket client = socket.socket(type=socket.SOCK_DGRAM) server_address = ('127.0.0.1',8080) while True: msg = input('>>>:') msg = '來自客戶端1的消息:%s'%msg client.sendto(msg.encode('utf-8'),server_address) data, server_addr = client.recvfrom(1024) print(data.decode('utf-8'))