1. 程式人生 > >Python套接字

Python套接字

啟動 ket 並發 strip() 發送 傳輸數據 socket cli truct

一.基於tcp的套接字

  tcp是基於鏈接的,必須先啟動服務端,然後再啟動客戶端去鏈接服務端

  tcp服務端

from socket import *

# 服務端必須滿足至少三點:
# 1.綁定一個固定的ip和port
# 2.一直對外提供服務,穩定運行
# 3.能夠支持並發
server = socket(AF_INET, SOCK_STREAM)
server.bind((127.0.0.1, 9555))
server.listen(5)

client, addr = server.accept()
data = client.recv(1024)
print(data.decode(
utf-8)) client.send(data.upper()) client.close() server.close()

  tcp客戶端

from socket import *
client = socket(AF_INET,SOCK_STREAM)
client.connect((127.0.0.1,9555))

msg = input(>>>:).strip()
client.send(msg.encode(utf-8))
data = client.recv(1024)
print(data)

client.close()

二.修改bug+通信循環+鏈接循環

  上述存在客戶端退出時服務端即會崩潰的bug,而且無法實現用戶的多次輸入,以及服務端無法一直對外服務的問題,對此進行修改

  服務端

from socket import *

server = socket(AF_INET, SOCK_STREAM)
server.bind((127.0.0.1, 8081))
server.listen(5)

# 鏈接循環
while True:
    conn, client_addr = server.accept()
    print(client_addr)

    # 通信循環
    while True:
        try:
            data 
= conn.recv(1024) if len(data) == 0: break # 針對linux系統 print(-->收到客戶端的消息: , data) conn.send(data.upper()) except ConnectionResetError: break conn.close() server.close()

  客戶端

from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect((127.0.0.1, 8081))

# 通信循環
while True:
    msg=input(>>: ).strip() #msg=‘‘
    if len(msg) == 0:continue
    client.send(msg.encode(utf-8)) #client.send(b‘‘)
    # print(‘has send‘)
    data=client.recv(1024)
    # print(‘has recv‘)
    print(data)

client.close()

三.模擬ssh實現遠程執行命令

技術分享圖片
from socket import *
import subprocess

server = socket(AF_INET, SOCK_STREAM)
server.bind((127.0.0.1, 8081))
server.listen(5)

# 鏈接循環
while True:
    conn, client_addr = server.accept()
    print(client_addr)

    # 通信循環
    while True:
        try:
            cmd = conn.recv(1024) #cmd=b‘dir‘
            if len(cmd) == 0: break  # 針對linux系統
            obj=subprocess.Popen(cmd.decode(utf-8),
                             shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE
                             )
            stdout=obj.stdout.read()
            stderr=obj.stderr.read()
            print(len(stdout) + len(stderr))
            conn.send(stdout+stderr)
        except ConnectionResetError:
            break

    conn.close()

server.close()
服務端 技術分享圖片
from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect((127.0.0.1, 8081))

# 通信循環
while True:
    cmd=input(>>: ).strip()
    if len(cmd) == 0:continue
    client.send(cmd.encode(utf-8))
    cmd_res=client.recv(1024000)
    print(cmd_res.decode(gbk))

client.close()
客戶端

四.粘包問題以及解決方法

  粘包問題:

    只有TCP有粘包現象,UDP不存在粘包,粘包問題主要還是因為接收方不知道消息之間的界限,不知道一次性提取多少字節的數據所造成的.

  簡單粘包問題案例

技術分享圖片
from socket import *
import subprocess

server = socket(AF_INET, SOCK_STREAM)
server.bind((127.0.0.1, 8081))
server.listen(5)

conn,_=server.accept()
data1=conn.recv(5)
print(第一次收: ,data1)

data2=conn.recv(5)
print(第二次收: ,data2)

data3=conn.recv(4)
print(第三次收: ,data3)

# 粘包問題是tcp協議流式傳輸數據的方式導致的
# 如何解決粘包問題:接收端能夠精確地收幹凈每個數據包沒有任何殘留
服務端 技術分享圖片
from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect((127.0.0.1, 8081))

# tcp協議會將數據量較小且發送時間間隔較短的數據合並成一個數據報發送
client.send(bhello)
client.send(bworld)
client.send(begon)
客戶端

五.解決粘包問題

技術分享圖片
from socket import *
import subprocess
import struct
import json

server = socket(AF_INET, SOCK_STREAM)
server.bind((127.0.0.1, 8081))
server.listen(5)

# 鏈接循環
while True:
    conn, client_addr = server.accept()
    print(client_addr)

    # 通信循環
    while True:
        try:
            cmd = conn.recv(1024)  # cmd=b‘dir‘
            if len(cmd) == 0: break  # 針對linux系統
            obj = subprocess.Popen(cmd.decode(utf-8),
                                   shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE
                                   )
            stdout = obj.stdout.read()
            stderr = obj.stderr.read()
            # 1. 先制作報頭
            header_dic = {
                filename: a.txt,
                md5: asdfasdf123123x1,
                total_size: len(stdout) + len(stderr)
            }
            header_json = json.dumps(header_dic)
            header_bytes = header_json.encode(utf-8)

            # 2. 先發送4個bytes(包含報頭的長度)
            conn.send(struct.pack(i, len(header_bytes)))
            # 3  再發送報頭
            conn.send(header_bytes)

            # 4. 最後發送真實的數據
            conn.send(stdout)
            conn.send(stderr)
        except ConnectionResetError:
            break

    conn.close()

server.close()
服務端 技術分享圖片
from socket import *
import struct
import json

client = socket(AF_INET, SOCK_STREAM)
client.connect((127.0.0.1, 8081))

# 通信循環
while True:
    cmd = input(>>: ).strip()
    if len(cmd) == 0: continue
    client.send(cmd.encode(utf-8))
    # 1. 先收4bytes,解出報頭的長度
    header_size = struct.unpack(i, client.recv(4))[0]

    # 2. 再接收報頭,拿到header_dic
    header_bytes = client.recv(header_size)
    header_json = header_bytes.decode(utf-8)
    header_dic = json.loads(header_json)
    print(header_dic)
    total_size = header_dic[total_size]

    # 3. 接收真正的數據
    cmd_res = b‘‘
    recv_size = 0
    while recv_size < total_size:
        data = client.recv(1024)
        recv_size += len(data)
        cmd_res += data

    print(cmd_res.decode(gbk))

client.close()
客戶端

    

Python套接字