1. 程式人生 > >模擬ssh遠端socket程式設計粘包問題_服務端

模擬ssh遠端socket程式設計粘包問題_服務端

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# @Time    : 2018/6/2 18:29
# @Author  : chen
# @File    : 服務端.py
import json
import socket
import struct
import subprocess

"""
# 關於struct模組
res = struct.pack('i', 1230)
print(res, type(res), len(res))
# 輸出結果:b'\xce\x04\x00\x00' <class 'bytes'> 4
# 而1230轉成16進位制的結果為4CE,所以struct接收的結果是反向的
obj = struct.unpack('i', res)
print(obj)
# (1230,)
# unpack後,結果就是正向的,所以不需要在意這些細節,只要關注傳輸的是4位元組就可以了
"""
# 服務端需要兩個套接字,一個用來發送,另一個用來接收bind,recv # 客戶端只有一個套接字,connect phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # setsockopt(level,optname,value) # socket模組下的socket類,socket.AF_INET是網路模式 # socket.SOCK_STREAM是代表流模式,其實就是指的tcp """ # level定義了哪個選項將被使用。通常情況下是SOL_SOCKET,意思是正在使用的socket選項。它還可以通過設定一個特殊協議號碼來設定協議選項, # 然而對於一個給定的作業系統,大多數協議選項都是明確的,所以為了簡便,它們很少用於為移動裝置設計的應用程式。 # 這裡value設定為1,表示將SO_REUSEADDR標記為TRUE,作業系統會在伺服器socket被關閉或伺服器程序終止後馬上釋放該伺服器的埠, # 否則作業系統會保留幾分鐘該埠。 """
phone.bind(('127.0.0.1', 9901)) # 0-65535; 0-1024給作業系統使用 phone.listen(5) print('starting...') while True: conn, client_addr = phone.accept() # conn是接收的一個物件accept() -> (socket object, address info) # 可以將conn理解為三次握手的導向指標(箭頭) print(client_addr) while True: try: # 1.收命令
cmd = conn.recv(8096) # 1、單位:bytes 2、8096代表最大接收8096個bytes if not cmd: break # 適用於linux作業系統 print('客戶端資料', cmd) # 2.執行命令,拿到結果 obj = subprocess.Popen(cmd.decode('utf-8'), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout = obj.stdout.read() stderr = obj.stderr.read() # 3.把命令的結果返回給客戶端 # 第一步:製作固定長度的報頭 header_dic = { 'file_name': 'a.txt', 'md5': 'xxxxxdd', 'total_size': len(stdout) + len(stderr) } # 將字典序列化(字典轉成字元格式) header_json = json.dumps(header_dic) # 將字元格式編碼成二進位制 header_bytes = header_json.encode('utf-8') # 第二步:先發送報頭長度; 客戶端接收時一次只接收4位元組,這樣就不會出現粘包現象了 conn.send(struct.pack('i', len(header_bytes))) # 'i' 表示int 整型資料 # 第三步:再發送報頭 conn.send(header_bytes) # 傳送給網絡卡,從應用程式記憶體拷貝到系統記憶體,當資料為空時,作業系統就不會有任何操作 # 第四步:傳送真實資料 conn.send(stdout) conn.send(stderr) except ConnectionResetError: # 適用於windows作業系統 break conn.close() # 一次會話結束 phone.close() # 連線斷開 # 粘包的終極解決思路是,自己設定一個協議報頭,規定好長度(使用struct模組),然後再在報頭中填入字串長度問題 # 可以避免接收位元組長度過長超過int或long型的最大值(比如傳輸超大檔案這種),還能增加更多資訊(比如校驗md5檔案等)