博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python---基础知识回顾(六)网络编程2(处理粘包)
阅读量:4703 次
发布时间:2019-06-10

本文共 9053 字,大约阅读时间需要 30 分钟。

前戏:

之前在异步模块中提到过粘包现象,而且在使用twisted中提到过一种处理办法,按行接收lineReceived,当收到\r\n换行符时,才去缓冲区中获取到数据。

from twisted.internet import reactorfrom twisted.internet.protocol import Protocol,Factoryfrom twisted.protocols.basic import LineReceiverclass EchoServer(LineReceiver):    def connectionMade(self):   #建立连接的时候        print("Get connect from",self.transport.client)        self.factory.numPorts = self.factory.numPorts + 1        if self.factory.numPorts > 3:            self.transport.write("Too many connections , try later".encode("utf-8"))            self.transport.loseConnection()            self.factory.numPorts = self.factory.numPorts - 1        else:            self.transport.write("Successful connections".encode("utf-8"))    def connectionLost(self, reason):   #连接断开的时候        print(self.transport.client,"disconnect")        self.factory.numPorts = self.factory.numPorts - 1    def lineReceived(self,line):    #当收到一行数据的时候        data = "reve a line :%s"%line.decode("utf-8")        print(data)        self.transport.write(data.encode("utf-8"))factory = Factory()factory.protocol = EchoServerport = 8080reactor.listenTCP(port,factory)reactor.run()   #进入循环
服务器端lineReceived
import socketip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口sk = socket.socket()sk.connect(ip_port) #与服务器连接data = sk.recv(1024)print(data.decode("utf-8"))while True:    sk.sendall("fafwagawgwa".encode("utf-8"))    data = input(">>>:")    if not data:        continue    if data == "hh":        data += "\r\n"    try:        sk.sendall(data.encode("utf-8"))  # 向服务器端发送数据        data = sk.recv(1024)        print(data.decode("utf-8"))    except ConnectionAbortedError:        breaksk.close()
客户端
sk.sendall("fafwagawgwa".encode("utf-8"))    data = input(">>>:")    if not data:        continue    if data == "hh":        data += "\r\n"     sk.sendall(data.encode("utf-8"))  # 向服务器端发送数据

在这里我们先发送fafwagawgwa数据到服务器端的缓冲区中(未被获取),然后在客户端输入hh后,会发送换行符到服务器端,此时缓冲区中的数据“fafwagawgwahh\r\n”,当获取到换行符后,服务器会将数据获取打印

reve a line :fafwagawgwahh

粘包现象演示:

粘包问题产生的原因:

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

产生情况:

1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
import socketip_port = ("0.0.0.0",8080)sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)sk.bind(ip_port)sk.listen(5)while True:    conn,addr = sk.accept()    print("Connect from %s(%s)"%(addr[0],addr[1]))    count = 0    while True:        count += 1        bt_data = conn.recv(1024)        if not bt_data: #对方断开了连接            conn.close()            break        data = bt_data.decode("utf-8")        print(count,data)sk.close()
服务端
import socketip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口sk = socket.socket()sk.connect(ip_port) #与服务器连接message_list = ['1','2','3','4','5','6','7','8','9','10']num = 0while True:    try:        data = message_list[num]    except IndexError:        break    send_data = data.encode("utf-8")    sk.sendall(send_data)    num += 1sk.close()
客户端
索引   数据Connect from 127.0.0.1(3395)1 1232 43 54 65 76 87 98 10
输出结果

从输出结果,可以看出在服务器端接收的数据产生了粘包现象

2.接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
import socketip_port = ("0.0.0.0",8080)sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)sk.bind(ip_port)sk.listen(5)while True:    conn,addr = sk.accept()    print("Connect from %s(%s)"%(addr[0],addr[1]))    count = 0    while True:        count += 1        bt_data = conn.recv(5)        if not bt_data: #对方断开了连接            conn.close()            break        data = bt_data.decode("utf-8")        print(count,data)sk.close()
服务器端
import socket,timeip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口sk = socket.socket()sk.connect(ip_port) #与服务器连接message_list = ['1dsafwawf','2faafaw']num = 0while True:    try:        data = message_list[num]    except IndexError:        break    send_data = data.encode("utf-8")    sk.sendall(send_data)    num += 1    time.sleep(5)sk.close()
客户端
Connect from 127.0.0.1(3531)1 1dsaf2 wawf3 2faaf4 aw
输出结果

简单版本:

import socketip_port = ("0.0.0.0",8080)sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)sk.bind(ip_port)sk.listen(5)while True:    conn,addr = sk.accept()    print("Connect from %s(%s)"%(addr[0],addr[1]))    buffer = ""    while True:        bt_data = conn.recv(5)        if not bt_data: #对方断开了连接            conn.close()            break        data = bt_data.decode("utf-8")        buffer += data        if '\r\n' in buffer:            index = buffer.find('\r\n')            data = buffer[:index]            buffer = buffer[index+2:]            print(data)sk.close()
服务器端
import socket,timeip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口sk = socket.socket()sk.connect(ip_port) #与服务器连接message_list = ['1dsafwawf\r\n','2faafaw\r\n']num = 0while True:    try:        data = message_list[num]    except IndexError:        break    send_data = data.encode("utf-8")    sk.sendall(send_data)    num += 1    time.sleep(5)sk.close()
客户端
Connect from 127.0.0.1(4167)1dsafwawf2faafaw
输出结果

 改进版本(先发送一个报头,告诉服务端要接收的数据大小):

import socketimport structip_port = ("0.0.0.0",8080)sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)sk.bind(ip_port)sk.listen(5)while True:    conn,addr = sk.accept()    print("Connect from %s(%s)"%(addr[0],addr[1]))    while True:        head_data = conn.recv(4)  #先接收报头,含有文件大小        if not head_data: #对方断开了连接            conn.close()            break        #设置接收的大小以及接收数据        recv_size = struct.unpack('i',head_data)[0] #unpack返回元组        recv_data = bytes()        while recv_size:            recv_data += conn.recv(recv_size)            recv_size -= len(recv_data)        data = recv_data.decode("utf-8")        print(data)sk.close()
服务端
import socket,timeimport structip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口sk = socket.socket()sk.connect(ip_port) #与服务器连接message_list = ['1dsafwaffdawwawf','2faafwfwafwafgrehrssafawfaw']num = 0while True:    try:        data = message_list[num]    except IndexError:        break    send_data = data.encode("utf-8")    head_data = struct.pack('i',len(send_data)) #i是int类型4字节    sk.send(head_data)    sk.sendall(send_data)    num += 1    time.sleep(5)sk.close()
客户端
Connect from 127.0.0.1(5982)1dsafwaffdawwawf2faafwfwafwafgrehrssafawfaw
输出结果

最终版本:

(对于一些文件的发送,我们需要先发送一个报头,其中是文件信息的字节长度, 然后我们将文件的详细信息,以及文件的md5值发送过去,对方根据详细信息获取数据,对数据的检测更加完善)

 补充:使用hashlib进行文本(文件)比较

其中有一段提及使用md5值去比较文件一致性。

import socketimport structimport osimport hashlibimport jsonip_port = ("0.0.0.0",8080)sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)sk.bind(ip_port)sk.listen(5)def getMd5(path):    fp = open(path,"r")    content = fp.read()    data = hashlib.md5(content.encode("utf-8"))    fp.close()    return data.hexdigest(),contentwhile True:    conn,addr = sk.accept()    print("Connect from %s(%s)"%(addr[0],addr[1]))    while True:        try:            path = conn.recv(1024)        except ConnectionResetError:            conn.close()            break        if not path:            conn.close()            break        path = path.decode("utf-8")        header_data = {
'file_size':None,'file_md5':None,'file_name':None} #用来封装文件的信息 header_info = None #用来标志长度 这个先发送,根据这个长度去获取上面的数据(里面有详细信息) if not os.path.isfile(path): header_info = struct.pack('i',-1) #-1代表失败 conn.send(header_info) # 注意pack后的数据已经是字节型,所以我们不需要去编码 continue header_data['file_size'] = os.stat(path).st_size header_data['file_name'] = os.path.basename(path) header_data['file_md5'],content = getMd5(path) header_data = json.dumps(header_data).encode("utf-8") header_info = struct.pack('i',len(header_data)) conn.send(header_info) #发送header_info,其中是header_data的长度 conn.send(header_data) #这里的数据在上面已经编码了,不需要我们处理 conn.sendall(content.encode("utf-8")) #发送文件的内容sk.close()
文件下载服务端
import socketimport structimport jsonimport hashlibip_port = ("127.0.0.1",8080)    #用于绑定服务器端的地址和端口sk = socket.socket()sk.connect(ip_port) #与服务器连接def getMd5(content):    data = hashlib.md5(content)    return data.hexdigest()while True:    path = input(">>>(请输入下载的文件名):").strip()    if not path:        continue    if path == "quit":        break    sk.send(path.encode("utf-8"))   #发送文件路径    status = sk.recv(4)   #接收准备状态    status = struct.unpack('i',status)[0]  #转换数据类型,记得unpack返回的是一个元组    if status == -1:        print("请输入正确的文件路径")        continue    header_data = sk.recv(status)    header_data = header_data.decode("utf-8")    header_data = json.loads(header_data)    content = sk.recv(header_data['file_size'])    file_md5 = getMd5(content)    if file_md5 != header_data['file_md5']:        print("文件下载出错,请重试")        del content        continue    data = content.decode("utf-8") #获取到文件的所有数据    with open(header_data['file_name'],"w") as fp:        fp.write(data)    print("文件下载完毕!")sk.close()
文件下载客户端

 

转载于:https://www.cnblogs.com/ssyfj/p/9043769.html

你可能感兴趣的文章
基于EasyNVR摄像机无插件直播流媒体服务器实现类似于单点登录功能的免登录直播功能...
查看>>
python学习0day
查看>>
课堂练习之检测水军
查看>>
函数指针的使用
查看>>
位图数据结构的操作
查看>>
azkaban用户管理及权限配置
查看>>
GCD学习笔记
查看>>
PHP......会话控制SESSION与COOKIE
查看>>
[转]AchartEngineActivity引擎绘制柱状图、曲线图
查看>>
[转]javascript实现限制上传文件的大小
查看>>
我的Java设计模式-策略模式
查看>>
C# 报表接口样例,简单实用
查看>>
C++常见内存错误及解决方案
查看>>
控制台应用程序窗口无法输入汉字解决办法
查看>>
Java中实现String.padLeft和String.padRight
查看>>
winCVS 使用的一个小要点
查看>>
一个关于session的问题
查看>>
加快开发时间的8个CSS的预处理程序
查看>>
dom元素高度、屏幕高度 获取
查看>>
asp.net 设置session失效时间
查看>>