python之socket编程3

1 什么是粘包

只有TCP有粘包现象,UDP永远不会粘包

应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向连接的,面向流的,收发两端都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法,将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这也是容易出现粘包问题的原因;

而UDP面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据。

粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

两种情况下会发生粘包

(1.)发送端需要等缓冲区满才发送出去,造成粘包。发送数据时间间隔很短,数据很小会合到一起,产生粘包

(2)接收方不及时收取缓冲区的包,造成多个包接收,客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包

#服务端
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=("127.0.0.1",8080)
phone.bind(ip_port)
phone.listen(5)
conn,addr=phone.accept()
data1=conn.recv(1024)
data2=conn.recv(1024)
print("第1个包",data1)
print("第2个包",data2)

#客户端
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=("127.0.0.1",8080)
phone.connect(ip_port)
phone.send("helloworld".encode("utf8"))
phone.send("SB".encode("utf8"))

>>
第1个包 b'helloworldSB'#产生粘包现象
第2个包 b''

使用时间模块,并不能够彻底解决问题

#服务端
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=("127.0.0.1",8080)
phone.bind(ip_port)
phone.listen(5)
conn,addr=phone.accept()
data1=conn.recv(1024)
data2=conn.recv(1024)
print("第1个包",data1)
print("第2个包",data2)


#客户端
import socket,time
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=("127.0.0.1",8080)
phone.connect(ip_port)
phone.send("helloworld".encode("utf8"))
time.sleep(3)
phone.send("SB".encode("utf8"))

>>
第1个包 b'helloworld'#send先执行一次后,发送至客户端内存
第2个包 b'SB'#延迟三秒后,,再执行发送
#服务端
import socket
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=("127.0.0.1",8080)
phone.bind(ip_port)
phone.listen(5)
conn,addr=phone.accept()
data1=conn.recv(1)
data2=conn.recv(1024)
print("第1个包",data1)
print("第2个包",data2)
	
#客户端
import socket,time
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=("127.0.0.1",8080)
phone.connect(ip_port)


phone.send("helloworld".encode("utf8"))
time.sleep(3)
phone.send("SB".encode("utf8"))
>>
第1个包 b'h'  #recv第一次只取一个字节
第2个包 b'elloworld'  #第二次再次执行 ,”SB”还在服务器的内存中

2 自定制报头解决粘包问题

数据封装报头:

固定长度

包含对将要发送数据的描述信息

struct模块
import struct
print(struct.pack("i",111))
>>
b'o\x00\x00\x00'   #转成字节模式

import struct
res=struct.pack("i",111)#struct.pack 打包
print(len(res))
>>
4  #转成字节长度为固定4

import struct
res=struct.pack("i",111)
# print(len(res))
print(struct.unpack("i",res))#解包
(111,)#获得以元组形式的结果

简单实现:

#服务端
import socket
import struct
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=("127.168.0.1",8080)
phone.bind(ip_port)
phone.listen(5)
#连接循环
while True:
    conn,addr=phone.accept()
    print("cline addr",addr)
    #通讯循环
    while True:
        try:
            cmd=conn.recv(1024)
            res=subprocess.Popen(cmd.decode("utf8"),
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
            out_res=res.stdout.read()
            err_res=res.stderr.read()
            data_size=len(out_res)+len(err_res)
            #发送报头
            conn.send(struct.pack("i",data_size))
            #发送数据部分
            conn.send(out_res)
            conn.send(err_res)
        except Exception:
            break
    conn.close()
phone.close()

#客户端
import socket
import struct
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=("127.168.0.1",8080)
phone.connect(ip_port)
#通信循环
while True:
    #发消息
    cmd=input(">>").strip()
    if not cmd:continue
    phone.send(bytes(cmd,encoding="utf8"))
    #收报头
    baotou=phone.recv(4)
    data_size=struct.unpack("i",baotou)[0]
    #收数据
    recv_size=0
    recv_data=b""
    while recv_size<data_size:
        data=phone.recv(1024)
        recv_size+=len(data)
        recv_data+=data
    print(recv_data.decode("gbk"))
phone.close()

完全解决:

#服务端
import socket
import struct
import subprocess
import json
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=("127.168.0.1",8080)
phone.bind(ip_port)
phone.listen(5)
#连接循环
while True:
    conn,addr=phone.accept()
    print("cline addr",addr)
    #通讯循环
    while True:
        try:
            cmd=conn.recv(1024)
            res=subprocess.Popen(cmd.decode("utf8"),
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
            out_res=res.stdout.read()
            err_res=res.stderr.read()
            data_size=len(out_res)+len(err_res)
            head_dic={"data_size":data_size}
            head_json=json.dumps(head_dic)
            head_bytes=head_json.encode("utf8")
            #发送报头
            head_len=len(head_bytes)
            conn.send(struct.pack("i",data_size))
            conn.send(head_bytes)
            #发送数据部分
            conn.send(out_res)
            conn.send(err_res)
        except Exception:
            break
    conn.close()
phone.close()

#客户端
import socket
import struct
import json
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip_port=("127.168.0.1",8080)
phone.connect(ip_port)
#通信循环
while True:
    #发消息
    cmd=input(">>").strip()
    if not cmd:continue
    phone.send(bytes(cmd,encoding="utf8"))
    #收报头的长度
    head_struct=phone.recv(4)
    head_len=struct.unpack("i",head_len)[0]

    #再收报头
    head_bytes=phone.recv(head_len)
    head_json=head_bytes.decode("utf8")
    head_dic=json.loads(head_json)
    print(head_dic)
    data_size=head_dic["data_size"]

    #收数据
    recv_size=0
    recv_data=b""
    while recv_size<data_size:
        data=phone.recv(1024)
        recv_size+=len(data)
        recv_data+=data
    print(recv_data.decode("gbk"))
phone.close()

  

 

posted @ 2017-05-07 13:37  qianxiamo  阅读(184)  评论(0编辑  收藏  举报