python筆記8 socket(TCP) subprocess模塊 粘包現象 struct模塊 基於UDP的套接字協議

socket

基於tcp協議socket

服務端shell

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 買電話
# socket.SOCK_STREAM 流式協議 就是TCP協議
phone.bind(('127.0.0.1', 8080))  # 買電話卡

phone.listen(5)  # 開機。
# 5 不是連接數,連接能夠產生N個,同一時刻只能監聽5個請求。

conn, addr = phone.accept() # 等待接電話  # 阻塞狀態
# print(222)
print(conn, addr)  # conn 表明的是socket通訊的對象,一個管道

client_data = conn.recv(1024)  # 交流過程
print(client_data)
conn.send(client_data.upper())

conn.close()
phone.close()

客戶端json

import socket

phone = socket.socket()  # 買電話

phone.connect(('127.0.0.1', 8080))  # 撥號

msg = input('>>>').strip()
phone.send(msg.encode('utf-8'))
server_data = phone.recv(1024)  # 限制的是最大接收字節數。
print(server_data)
phone.close()

服務端與客戶端循環 聊緩存

import socket
import subprocess
# socket.SOCK_STREAM 流式協議 就是TCP協議
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 買電話
phone.bind(('127.0.0.1', 8080))  # 綁定電話卡,寫本身的IP  (倆括號)
phone.listen(5)   #開機   5不是限制鏈接數,而是同一時刻只接受5個請求,能夠不寫,不寫爲默認,默承認以開啓N個
#運行後停在這裏,下邊不執行
while 1:
    conn,addr = phone.accept()  #等待接電話 phone.accept()  conn和addr分別接收phone..accept()生成的兩個參數
    print(addr)  #打印鏈接進來的客戶端
    while 1:
        try:
            client_data = conn.recv(1024)   #接受的字節數
            c = client_data.decode('gbk')
            obj = subprocess.Popen(c,
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)
            RET = obj.stdout.read()
            RET1 = obj.stderr.read()
            print(RET.decode('gbk'))
            conn.send(RET+RET1) #返回的信息
        except Exception:
            break
    conn.close()
phone.close()

客戶端服務器

import socket
phone = socket.socket()
phone.connect(('127.0.0.1', 8080))  # 撥號,寫服務端的IP  (倆括號)
while 1:
    fasong = input('>>>')
    if fasong.upper() == 'Q': break
    elif  not fasong :continue            #若是發送爲空,則跳過本次循環 not False3 = True
    phone.send(fasong.encode('gbk'))       #剛給服務端發信息
    server_data = phone.recv(1024)    #接收服務端的信息,1024限制的是最大接受的字節數
    print(server_data.decode('gbk'))
phone.close()

subprocess模塊

subprocess模塊用於接收shell界面執行的命令並返回結果socket

import subprocess 
obj = subprocess.Popen('ip a',          #輸入命令
                        shell=True,           #shell爲Tree
                       stdout=subprocess.PIPE,              #stdout接收正確
                       stderr=subprocess.PIPE)               #stderr接收錯誤返回
RET = obj.stdout.read()     
print(RET.decode('utf-8'))        

 粘包現象

tcp協議

1. 流式協議.數據所有都像水同樣連在一塊兒,一次性發送多字節(所有在客戶端操做系統的緩存區),客戶端每次只取1024字節,剩餘字節在緩存區等待下次取tcp

client_data = conn.recv(1024)    
server_data = phone.recv(1024)    

2.針對於(客戶端/服務端)發送的連續的小的數據,對方會一次性接收.spa

客戶端操作系統

import socket
phone = socket.socket()
phone.connect(('127.0.0.1', 8080))  # 撥號,寫服務端的IP  (倆括號)
phone.send(b'hello')              #先發送hello
phone.send(b'word')               #緊接着在發送word while 1:
    server_data = phone.recv(1024)    #接收服務端的信息,1024限制的是最大接受的字節數
    print(server_data.decode('gbk'))
phone.close()

 

服務端code

import socket
import subprocess
# socket.SOCK_STREAM 流式協議 就是TCP協議
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 買電話
phone.bind(('127.0.0.1', 8080))  # 綁定電話卡,寫本身的IP  (倆括號)
phone.listen(5)   #開機   5不是限制鏈接數,而是同一時刻只接受5個請求,能夠不寫,不寫爲默認,默承認以開啓N個
#運行後停在這裏,下邊不執行
conn,addr = phone.accept()  #等待接電話 phone.accept()  conn和addr分別接收phone..accept()生成的兩個參數
print(addr)  #打印鏈接進來的客戶端
while 1 :
    client_data = conn.recv(1024)   #接受的字節數
    print(client_data)
    conn.send(client_data.upper()) #返回的信息
conn.close()
phone.close()


('127.0.0.1', 53961)      #服務端接收客戶端的信息連在了一塊兒
b'helloword'

 

客戶端server

import socket
import time
phone = socket.socket()
phone.connect(('127.0.0.1', 8080))  # 撥號,寫服務端的IP  (倆括號)
phone.send(b'hello')
time.sleep(0.1)                #等待0.1秒在發送word
phone.send(b'word')
while 1:
    server_data = phone.recv(1024)    #接收服務端的信息,1024限制的是最大接受的字節數
    print(server_data.decode('gbk'))
phone.close()

服務端

import socket
import subprocess
# socket.SOCK_STREAM 流式協議 就是TCP協議
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 買電話
phone.bind(('127.0.0.1', 8080))  # 綁定電話卡,寫本身的IP  (倆括號)
phone.listen(5)   #開機   5不是限制鏈接數,而是同一時刻只接受5個請求,能夠不寫,不寫爲默認,默承認以開啓N個
#運行後停在這裏,下邊不執行
conn,addr = phone.accept()  #等待接電話 phone.accept()  conn和addr分別接收phone..accept()生成的兩個參數
print(addr)  #打印鏈接進來的客戶端
while 1 :
    client_data = conn.recv(1024)   #接受的字節數
    print(client_data)
    conn.send(client_data.upper()) #返回的信息
conn.close()
phone.close()
 
('127.0.0.1', 53985)             #接受的字符分開了
b'hello'
b'word'

解決粘包問題

發送固定頭部,固定頭部包含(數據的總大小) + 數據

struct模塊  將一個數據,int 轉化爲固定的bytes

import struct
ret = struct.pack('i',151923212)    #將151923212 轉換爲固定的bytes類型
print(ret,type(ret) ,len(ret),sep='\n') #sep='\n',將分隔符換爲\n

ret1 = struct.unpack('i',ret)
print(ret1)

b'\x0c*\x0e\t'
<class 'bytes'>
4                         #轉爲固定的4字節
(151923212,)

解決粘包問題代碼

low版 

一旦數據傳入過大則struct模塊報錯, struct模塊不能轉譯 較長的字符串

服務端:

將數據大小經過struct模塊轉爲固定大小發給客戶端

import socket
import subprocess
import struct
# socket.SOCK_STREAM 流式協議 就是TCP協議
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 買電話
phone.bind(('127.0.0.1', 8080))  # 綁定電話卡,寫本身的IP  (倆括號)
phone.listen(5)   #開機   5不是限制鏈接數,而是同一時刻只接受5個請求,能夠不寫,不寫爲默認,默承認以開啓N個
#運行後停在這裏,下邊不執行
conn,addr = phone.accept()  #等待接電話 phone.accept()  conn和addr分別接收phone..accept()生成的兩個參數
print(addr)  #打印鏈接進來的客戶端
while 1 :                            #如下爲粘包解決方法 try:
        client_data = conn.recv(1024)  # 接受的字節數
        obj = subprocess.Popen(client_data.decode('utf-8'),   #將接收的字節在本地shell執行 並返回結果
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,)
        ret = obj.stdout.read()           #正確結果
        ret1 = obj.stderr.read()          #錯誤結果
        ss = len(ret1 + ret)               #算返回結果的總長度
        ret2 = struct.pack('i',ss)         #經過struct將算返回結果的總長度變爲固定長度的bytes類型
        conn.send(ret2)    #發送報頭
        conn.send(ret)      #發送正確結果
        conn.send(ret1)    #發送錯誤結果
    except Exception:
        break
conn.close()
phone.close()

客戶端:

客戶端根據服務端發來的內容大小取內容

import socket
import time
import struct

phone = socket.socket()
phone.connect(('127.0.0.1', 8080))  # 撥號,寫服務端的IP  (倆括號)
while 1:
    msg = input('>>>').strip()          #輸入發往服務端的內容,若是是q退出,若是是空重新輸入
    if msg.upper() == 'Q':
        break
    elif not msg:         #not + 空就是 True 執行continue 從新輸入
        continue 
    phone.send(msg.encode('utf-8'))         #往服務端發送內容
    head = phone.recv(4)               #接收報頭 報頭大小爲固定4字節
    head_size = struct.unpack('i', head)[0]          #直接將報頭反解爲服務端發送內容的長度,返回是元組,取第一個值
    datas = 0      #定義一個datas大小爲0
    res = b''       #定義一個空的bytes類型的變量
    while datas < head_size:     #若是datas小於發送的內容的總長度爲真
        data = phone.recv(1024)   #取1024字節
        res = res + data                #將取出的內容追加到res裏
        datas += len(data)            #datas加上取出內容字節的大小
    print(res.decode('gbk'))         #讀出res裏的內容


phone.close()
uper版

服務端:

將服務端發送數據大小寫到字典,

將字典轉爲json,

再將json轉爲字符串,

取字符串長度給struct模塊轉爲固定大小

將固定大小,bytes類型的字典以及全部數據傳給客戶端

import socket
import subprocess
import struct
import json
# socket.SOCK_STREAM 流式協議 就是TCP協議
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 買電話
phone.bind(('127.0.0.1', 8080))  # 綁定電話卡,寫本身的IP  (倆括號)
phone.listen(5)   #開機   5不是限制鏈接數,而是同一時刻只接受5個請求,能夠不寫,不寫爲默認,默承認以開啓N個
#運行後停在這裏,下邊不執行
conn,addr = phone.accept()  #等待接電話 phone.accept()  conn和addr分別接收phone..accept()生成的兩個參數
print(addr)  #打印鏈接進來的客戶端
while 1 :
    try:
        client_data = conn.recv(1024)  # 接受的字節數
        obj = subprocess.Popen(client_data.decode('utf-8'),
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,)
        ret = obj.stdout.read()
        ret1 = obj.stderr.read()
        ss = len(ret1 + ret)
        head_dict = {                         #定義一個字典,total_size的值爲發送數據的大小,字典裏可定義多個鍵值傳遞多種信息
            'total_size':ss
        }
        head_json = json.dumps(head_dict)       #將字典轉爲json格式
        head_bytes = head_json.encode('utf-8')  #再將json格式轉爲bytes格式
        ret2 = struct.pack('i',len(head_bytes))   #將轉爲bytes格式的長度經過struct轉爲固定字節

        conn.send(ret2)             #發送固定字節
        conn.send(head_bytes)     #發送bytes類型的字典
        conn.send(ret)                #發送內容
        conn.send(ret1)               
    except Exception:
        break
conn.close()
phone.close()

客戶端:

將服務端發送的頭部大小獲取字典大小

經過字典大小獲取內容大小

經過內容大小獲取內容

import socket
import json
import struct

phone = socket.socket()
phone.connect(('127.0.0.1', 8080))  # 撥號,寫服務端的IP  (倆括號)
while 1:
    msg = input('>>>').strip()
    if msg.upper() == 'Q':
        break
    elif not msg:
        continue
    phone.send(msg.encode('utf-8'))
    he = struct.unpack('i', phone.recv(4))[0]   #將頭部的大小(phone.recv(4))經過struct模塊解析出字典bytes類型的大小
    head_dic_bytes = phone.recv(he)             #經過解析出字典bytes類型的大小獲取bytes類型字典的數據
    head_json = head_dic_bytes.decode('utf-8')  #將bytes數據反解爲json類型
    head_doc = json.loads(head_json)            #反解json得到字典
    datas = 0
    res = b''
    while datas < head_doc['total_size']:       #將字典total_size鍵對應的內容大小的值取出,得到內容
        data = phone.recv(1024)
        res = res + data
        datas += len(data)
    print(res.decode('gbk'))


phone.close()

基於UDP的套接字協議

服務端

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)   # (type=socket.SOCK_DGRAM)基於UDP的套接字TCP爲socket.AF_INET, socket.SOCK_STREAM

udp_sk.bind(('127.0.0.1', 10000))     #綁定服務器套接字

while 1:
    msg, addr = udp_sk.recvfrom(1024) #tcp裏的accept()裏的recv() 是阻塞的 這裏的recvfrom(1024)是非阻塞的
    print(msg.decode('utf-8'))        #打印內容
    udp_sk.sendto(b'hi',addr)         #返回給客戶端的內容

客戶端

import socket
ip = ('127.0.0.1',10000)                            #建立個IP
udp_sk=socket.socket(type=socket.SOCK_DGRAM)        #開啓udp的socket
while 1:
    sd = input('>>>').encode('utf-8')
    udp_sk.sendto(sd,ip)                             #給服務端發送內容
    back_msg,addr=udp_sk.recvfrom(1024)              #獲取服務端回覆的內容
    print(back_msg.decode('utf-8'),addr)             #打印服務端回覆的內容
udp_sk.close()
相關文章
相關標籤/搜索