遠程執行命令
先來學習一個新模塊 , 一會用到的..
新模塊: subprocess 執行系統命令 r = subprocess.Popen('ls',shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE) subprocess.Popen(a,b,c,d) a: 要執行的系統命令(str) b: shell = True 表示確定我當前執行的命令為系統命令 c: 表示正確信息的輸出管道 d: 表示錯誤信息的輸出管道
下邊直接上代碼,一看就懂. TCP的

import socket import subprocess sk = socket.socket() sk.bind(('127.0.0.1',9090)) sk.listen() conn,addr = sk.accept() while 1: cmd = conn.recv(1024).decode('utf-8') res = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE, stderr=subprocess.PIPE) std_out = res.stdout.read()# 讀取正確的返回信息 std_err = res.stderr.read()# 讀取錯誤的返回信息 if std_out: conn.send(std_out) else: conn.send(std_err) conn.close() sk.close()

import socket sk = socket.socket() sk.connect_ex(('127.0.0.1',9090)) while 1: cmd = input('請輸入一個命令>>>') sk.send(cmd.encode('utf-8')) print(sk.recv(204800000).decode('gbk')) sk.close()
就是引用了一個模塊的功能,其他的還是簡單的收發功能.
res=subprocess.Popen(cmd.decode('utf-8'), shell=True, stderr=subprocess.PIPE, stdout=subprocess.PIPE) 的結果的編碼是以當前所在的系統為准的,如果是windows,那么res.stdout.read()讀出的就是GBK編碼的,在接收端需要用GBK解碼 且只能從管道里讀一次結果 注意
--------------------粘包---------------------------
UDP不會發生黏包--------
UDP(user datagram protocol,用戶數據報協議)是無連接的,面向消息的,提供高效率服務。
不會使用塊的合並優化算法,, 由於UDP支持的是一對多的模式,所以接收端的skbuff(套接字緩沖區)采用了鏈式結構來記錄每一個到達的UDP包,
在每個UDP包中就有了消息頭(消息來源地址,端口等信息),這樣,對於接收端來說,就容易進行區分處理了。
即面向消息的通信是有消息保護邊界的。
對於空消息:tcp是基於數據流的,於是收發的消息不能為空,這就需要在客戶端和服務端都添加空消息的處理機制,防止程序卡住,
而udp是基於數據報的,即便是你輸入的是空內容(直接回車),也可以被發送,udp協議會幫你封裝上消息頭發送過去。
不可靠不黏包的udp協議:udp的recvfrom是阻塞的,一個recvfrom(x)必須對唯一一個sendinto(y),收完了x個字節的數據就算完成,若是y;x數據就丟失,
這意味着udp根本不會粘包,但是會丟數據,不可靠。
補充說明:
用UDP協議發送時,用sendto函數最大能發送數據的長度為:65535- IP頭(20) – UDP頭(8)=65507字節。
用sendto函數發送數據時,如果發送數據長度大於該值,則函數會返回錯誤。(丟棄這個包,不進行發送)
用TCP協議發送時,由於TCP是數據流協議,因此不存在包大小的限制(暫不考慮緩沖區的大小),這是指在用send函數時,數據長度參數不受限制。
而實際上,所指定的這段數據並不一定會一次性發送出去,如果這段數據比較長,會被分段發送,如果比較短,可能會等待和下一次數據一起發送。
總結
黏包現象只發生在tcp協議中:
1.從表面上看,黏包問題主要是因為發送方和接收方的緩存機制、tcp協議面向流通信的特點。
2.實際上,主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的
解決方案------------
我們可以借助一個模塊,這個模塊可以把要發送的數據長度轉換成固定長度的字節。這樣客戶端每次接收消息之前只要先接受這個固定長度字節的內容看一看接下來要接收的信息大小,那么最終接受的數據只要達到這個值就停止,就能剛好不多不少的接收完整的數據了。
文件上傳解決粘包 的代碼: 利用字典先傳過去文件大小即可....

import socket import json import struct sk = socket.socket() sk.bind(('127.0.0.1',9090)) sk.listen() conn,addr = sk.accept() dic_size = conn.recv(4)# 先接受4字節長度的一個bytes, 代表字典的大小 dic_size = struct.unpack('i',dic_size)[0]# 將這個特殊的bytes轉變成原數字 dic_str = conn.recv(dic_size).decode('utf-8')# 根據字典大小去獲取字典,以免和底下獲取文件內容發生粘包 dic = json.loads(dic_str)# 反序列化 得到字典 opt filename filesize if dic['opt'] == 'upload': '''接收文件''' filename = '1'+dic['filename']# 將文件名字修改,防止重名 with open(filename,'wb') as f: while dic['filesize']: content = conn.recv(1024) f.write(content) dic['filesize'] -= len(content) elif dic['opt'] == 'download': '''給客戶端傳輸文件''' conn.close() sk.close()

import socket import os import json import struct sk = socket.socket() sk.connect(('127.0.0.1',9090)) l = ['upload','download'] for i,v in enumerate(l): print(i+1,v) dic = {'opt':None,'filename':None,'filesize':None} while 1: opt = input("請輸入功能選項>>>")# 客戶要執行的操作選項 if opt == '1': '''upload''' file_dir = input('請輸入文件路徑>>>')# 'E:/sylar/python_workspace/day34/作業/時間同步機制_client.py' file_name = os.path.basename(file_dir)# 獲取文件名字 file_size = os.path.getsize(file_dir)# 獲取文件大小 dic['opt'] = l[int(opt)-1] dic['filename'] = file_name dic['filesize'] = file_size dic_str = json.dumps(dic)# 將字典序列化成一個字符串形式的字典 dic_size = len(dic_str)# 獲取字典的大小 ds = struct.pack('i',dic_size)# 把一個小於21.3E的一個數字轉變成一個4字節長度的bytes sk.send(ds + dic_str.encode('utf-8'))# 發送給服務器 with open(file_dir,'rb') as f: while file_size: content = f.read(1024)# 文件內容 sk.send(content) file_size -= len(content) elif opt == '2': '''download''' pass else: print('有誤') sk.close()
收發概括