Python-网络编程之粘包、UDP

粘包问题

什么是粘包问题呢?

在我们写 tcp socket编程的时候,tcp协议是一个流式的协议,服务端第一次发送的数据,客户端无法准确一次性的接收完毕,下一次发送的数据与上一次数据粘在一起。

即:

1、 无法预测对方需要接收的数据大小长度

2、 多次连续发送数据量小的,并且时间间隔短的数据 会一次性打包一起发送

TCP 协议的特性:

​ 会将多次连续发送的数据量小的,并且时间间隔短的数据一次性发送完毕

subprocess模块

我们用subprocess 模块来演示一个粘包问题。

# 服务端
# coding=utf-8
import socket
import subprocess

server = socket.socket()
address = ("127.0.0.1",8888)
server.bind(address)
server.listen(5)

while True:
    conn , addr = server.accept()
    while True:
        try:
            cmd = conn.recv(1024).decode("utf-8")
            print(cmd)

            if cmd == "q":
                break

            obj = subprocess.Popen(
                cmd,shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )

            res = obj.stdout.read() + obj.stderr.read()
            conn.send(res)
        except Exception:
            break
    conn.close()
# 客户端
# coding=utf-8

import socket
client = socket.socket()
address = ("127.0.0.1",8888)
client.connect(address)

while True:
    cmd = input("请输入>>>")
    client.send(cmd.encode("utf-8"))

    if cmd == "q":
        break

    data = client.recv(1024)
    print(data.decode("gbk"))

client.close()

终端打印结果

请输入>>>tasklist

映像名称                       PID 会话名              会话#       内存使用 
========================= ======== ================ =========== ============
System Idle Process              0 Services                   0         24 K
System                           4 Services                   0      3,172 K
smss.exe                       352 Services                   0      1,200 K
csrss.exe                      464 Services                   0      6,656 K
wininit.exe                    572 Services                   0      5,168 K
csrss.exe                      588 Console                    1     77,948 K
services.exe                   636 Services                   0     12,288 K
lsass.exe                      652 Services                   0     11,164 K
lsm.exe                        660 Services                   0      4,664 K
winlogon.exe                   744 Console                    1      8,180 K
svchost.exe                    812 Services                   0     10,040 K
svchost.
请输入>>>dir
exe                    892 Services                   0      8,280 K
svchost.exe                    980 Services                   0     21,744 K
svchost.exe                    168 Services                   0     21,060 K
svchost.exe                    388 Services                   0     40,792 K
svchost.exe                    908 Services                   0     12,252 K
WUDFHost.exe                  1100 Services                   0      8,096 K
ZhuDongFangYu.exe             1188 Services                   0     23,784 K
svchost.exe                   1236 Services                   0     17,532 K
wlanext.exe                   1412 Services                   0      5,268 K
conhost.exe                   1420 Services                   0      2,860 K
dwm.exe                       1536 Console                    1     38,436 K
explorer.exe                  1588 Console                    1     70,028 K
spoolsv.exe                   1612 Services                   0     12,204 K
svchost.exe       

我们看到在输完tasklist 命令之后,再次输入其他命令,还是上次命令的结果,

那么久证明了 服务端第一次发送的数据,客户端无法准确一次性的接收完毕,下一次发送的数据与上一次数据粘在一起。我们在客户端接收的时候,接收的数据大小是1024个字节,服务器执行结果 超出了接收的大小,所以相当于把结果堆到了后面,一起发送,所以下次执行命令得不到正确的结果,那么我们需要解决这个问题就需要用到 struct 模块

struct模块

struct 模块必须先定义报头,,先发送报头,再发送真实数据

# 服务端
# coding=utf-8

import struct
import socket
import subprocess

server = socket.socket()
address = ("127.0.0.1",8888)
server.bind(address)
server.listen(5)

while True:
    conn,addr = server.accept()
    while True:
        try:
            cmd = conn.recv(1024).decode("utf-8")
            print(cmd)

            if cmd == "q":
                break

            obj = subprocess.Popen(
                cmd ,shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE
            )

            res = obj.stdout.read() + obj.stderr.read()
			
            # 将执行结果计算长度用i模式打包
            res_len = struct.pack("i",len(res))

            # 先发送数据长度
            conn.send(res_len)

            # 再发送真实数据
            conn.send(res)
        except Exception:
            break
    conn.close()
# 客户端
# coding=utf-8

import socket
import struct


client = socket.socket()
address = ("127.0.0.1",8888)
client.connect(address)

while True:
    cmd = input("请输入命令>>>")
    client.send(cmd.encode("utf-8"))
    if cmd == "q":
        break
	
    # 用struct模块接收的长度统一是4个字节,
    head = client.recv(4)
    # 然后用struct模块解包得到数据的真实长度,返回的是元组类型,0表示第一个元素,就是真实长度
    data_len = struct.unpack("i",head)[0]
	
    # 然后再把真实长度接收即可。
    data = client.recv(data_len)
    print(data.decode("gbk"))
client.close()

UDP协议编程

udp也是一种传输协议

1)不需要建立双向通道

2)不会粘包

3)客户端给服务端发送数据,不需要等待服务端返回接收成功

4)数据容易丢失,数据不安全。

TCP:就相当于与在打电话

UDP:就相当于与在发短信

简易qq聊天室

# 服务端
# coding=utf-8
import socket

server = socket.socket(type=socket.SOCK_DGRAM)
address = ("127.0.0.1",8888)
server.bind(address)

while True:
    msg,addr = server.recvfrom(1024)
    print(msg.decode("utf-8"))

    # 服务端往客户端发送消息
    send_msg = input("服务端发送消息")
    server.sendto(send_msg.encode("utf-8"),addr)

# 客户端
# coding=utf-8
import socket
client = socket.socket(type=socket.SOCK_DGRAM)
address = ("127.0.0.1",8888)

while True:
    send_msg = input("请输入信息")
    client.sendto(send_msg.encode("utf-8"),address)

    data = client.recv(1024)
    print(data.decode("utf-8"))

posted @ 2019-10-19 17:15  GeminiMp  阅读(255)  评论(0编辑  收藏  举报