套接字socker的通訊-struct模塊-粘包問題

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.將執行結果再基於網絡發送給用戶
# 這樣就實現  用戶遠程操做你這臺電腦的操做
# ''

 

struct模塊

該模塊能夠把一個類型,如數字,轉成固定長度的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'))

 

相關文章
相關標籤/搜索