python---基础知识回顾(六)网络编程2(处理粘包)
前戏:
之前在python---基础知识回顾(六)网络编程异步模块中提到过粘包现象,而且在使用twisted中提到过一种处理办法,按行接收lineReceived,当收到\r\n换行符时,才去缓冲区中获取到数据。
from twisted.internet import reactor
from twisted.internet.protocol import Protocol,Factory
from twisted.protocols.basic import LineReceiver class EchoServer(LineReceiver):
def connectionMade(self): #建立连接的时候
print("Get connect from",self.transport.client)
self.factory.numPorts = self.factory.numPorts +
if self.factory.numPorts > :
self.transport.write("Too many connections , try later".encode("utf-8"))
self.transport.loseConnection()
self.factory.numPorts = self.factory.numPorts -
else:
self.transport.write("Successful connections".encode("utf-8")) def connectionLost(self, reason): #连接断开的时候
print(self.transport.client,"disconnect")
self.factory.numPorts = self.factory.numPorts - 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 = EchoServer port =
reactor.listenTCP(port,factory)
reactor.run() #进入循环
服务器端lineReceived
import socket
ip_port = ("127.0.0.1",) #用于绑定服务器端的地址和端口
sk = socket.socket()
sk.connect(ip_port) #与服务器连接
data = sk.recv()
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()
print(data.decode("utf-8"))
except ConnectionAbortedError:
break
sk.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 socket
ip_port = ("0.0.0.0",)
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,)
sk.bind(ip_port)
sk.listen()
while True:
conn,addr = sk.accept()
print("Connect from %s(%s)"%(addr[],addr[]))
count =
while True:
count +=
bt_data = conn.recv()
if not bt_data: #对方断开了连接
conn.close()
break
data = bt_data.decode("utf-8")
print(count,data)
sk.close()
服务端
import socket
ip_port = ("127.0.0.1",) #用于绑定服务器端的地址和端口
sk = socket.socket()
sk.connect(ip_port) #与服务器连接
message_list = ['','','','','','','','','','']
num =
while True:
try:
data = message_list[num]
except IndexError:
break
send_data = data.encode("utf-8")
sk.sendall(send_data)
num +=
sk.close()
客户端
索引 数据
Connect from 127.0.0.1()
输出结果
从输出结果,可以看出在服务器端接收的数据产生了粘包现象
.接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
import socket
ip_port = ("0.0.0.0",)
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,)
sk.bind(ip_port)
sk.listen()
while True:
conn,addr = sk.accept()
print("Connect from %s(%s)"%(addr[],addr[]))
count =
while True:
count +=
bt_data = conn.recv()
if not bt_data: #对方断开了连接
conn.close()
break
data = bt_data.decode("utf-8")
print(count,data)
sk.close()
服务器端
import socket,time
ip_port = ("127.0.0.1",) #用于绑定服务器端的地址和端口
sk = socket.socket()
sk.connect(ip_port) #与服务器连接
message_list = ['1dsafwawf','2faafaw']
num =
while True:
try:
data = message_list[num]
except IndexError:
break
send_data = data.encode("utf-8")
sk.sendall(send_data)
num +=
time.sleep()
sk.close()
客户端
Connect from 127.0.0.1()
1dsaf
wawf
2faaf
aw
输出结果
简单版本:
import socket
ip_port = ("0.0.0.0",)
sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,)
sk.bind(ip_port)
sk.listen()
while True:
conn,addr = sk.accept()
print("Connect from %s(%s)"%(addr[],addr[]))
buffer = ""
while True:
bt_data = conn.recv()
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+:]
print(data)
sk.close()
服务器端
import socket,time
ip_port = ("127.0.0.1",) #用于绑定服务器端的地址和端口
sk = socket.socket()
sk.connect(ip_port) #与服务器连接
message_list = ['1dsafwawf\r\n','2faafaw\r\n']
num =
while True:
try:
data = message_list[num]
except IndexError:
break
send_data = data.encode("utf-8")
sk.sendall(send_data)
num +=
time.sleep()
sk.close()
客户端
Connect from 127.0.0.1()
1dsafwawf
2faafaw
输出结果
改进版本(先发送一个报头,告诉服务端要接收的数据大小):
import socket
import struct ip_port = ("0.0.0.0",) sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,)
sk.bind(ip_port)
sk.listen() while True:
conn,addr = sk.accept()
print("Connect from %s(%s)"%(addr[],addr[]))
while True:
head_data = conn.recv() #先接收报头,含有文件大小
if not head_data: #对方断开了连接
conn.close()
break
#设置接收的大小以及接收数据
recv_size = struct.unpack('i',head_data)[] #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,time
import struct ip_port = ("127.0.0.1",) #用于绑定服务器端的地址和端口 sk = socket.socket()
sk.connect(ip_port) #与服务器连接 message_list = ['1dsafwaffdawwawf','2faafwfwafwafgrehrssafawfaw'] num = while 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 +=
time.sleep()
sk.close()
客户端
Connect from 127.0.0.1()
1dsafwaffdawwawf
2faafwfwafwafgrehrssafawfaw
输出结果
最终版本:
(对于一些文件的发送,我们需要先发送一个报头,其中是文件信息的字节长度,
然后我们将文件的详细信息,以及文件的md5值发送过去,对方根据详细信息获取数据,对数据的检测更加完善)
补充:使用hashlib进行文本(文件)比较
python---基础知识回顾(九)图形用户界面-------Tkinter
其中有一段提及使用md5值去比较文件一致性。
import socket
import struct
import os
import hashlib
import json ip_port = ("0.0.0.0",) sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,)
sk.bind(ip_port)
sk.listen() def getMd5(path):
fp = open(path,"r")
content = fp.read()
data = hashlib.md5(content.encode("utf-8"))
fp.close()
return data.hexdigest(),content while True:
conn,addr = sk.accept()
print("Connect from %s(%s)"%(addr[],addr[]))
while True:
try:
path = conn.recv()
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代表失败
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 socket
import struct
import json
import hashlib ip_port = ("127.0.0.1",) #用于绑定服务器端的地址和端口 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() #接收准备状态
status = struct.unpack('i',status)[] #转换数据类型,记得unpack返回的是一个元组
if status == -:
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()
文件下载客户端
python---基础知识回顾(六)网络编程2(处理粘包)的更多相关文章
- python爬虫主要就是五个模块:爬虫启动入口模块,URL管理器存放已经爬虫的URL和待爬虫URL列表,html下载器,html解析器,html输出器 同时可以掌握到urllib2的使用、bs4(BeautifulSoup)页面解析器、re正则表达式、urlparse、python基础知识回顾(set集合操作)等相关内容。
本次python爬虫百步百科,里面详细分析了爬虫的步骤,对每一步代码都有详细的注释说明,可通过本案例掌握python爬虫的特点: 1.爬虫调度入口(crawler_main.py) # coding: ...
- python基础教程总结13——网络编程,
1.网络设计模块 1.1 socket模块 根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认. 1)服务器监听:是服务器端套接 ...
- 第八篇:python基础_8 面向对象与网络编程
本篇内容 接口与归一化设计 多态与多态性 封装 面向对象高级 异常处理 网络编程 一. 接口与归一化设计 1.定义 (1)归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了 ...
- python基础知识回顾之列表
在python 中,主要的常用数据类型有列表,元组,字典,集合,字符串.对于这些基础知识,应该要能够足够熟练掌握. 如何创建列表: # 创建一个空列表:定义一个变量,然后在等号右边放一个中括号,就创建 ...
- (网络编程)基于tcp(粘包问题) udp协议的套接字通信
import socket 1.通信套接字(1人1句)服务端和1个客户端 2.通信循环(1人多句)服务端和1个客户端 3.通信循环(多人(串行)多句)多个客户端(服务端服务死:1个客户端---&g ...
- Java基础知识强化之网络编程笔记24:Android网络通信之 AndroidAsync(基于nio的异步通信库)
1. AndroidAsync AndroidAsync 是一个基于nio的异步socket ,http(客户端服务器端),websocket,socket.io库,AndroidAsync 是一 ...
- Java基础知识强化之网络编程笔记23:Android网络通信之 Volley(Google开源网络通信库)
联合网上资料学习:http://www.open-open.com/lib/view/open1451223702339.html 一.Volley的介绍 1. Volley简介 在这之前,我们在程序 ...
- Java基础知识强化之网络编程笔记19:Android网络通信之 HttpClient和传统Post、Get方式的区别
1. HttpClient是什么 ? HTTP 协议可能是现在 Internet 上使用得最多.最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源.虽然在 ...
- Java基础知识强化之网络编程笔记18:Android网络通信之 使用HttpClient的Post / Get 方式读取网络数据(基于HTTP通信技术)
使用HttpClient进行Get方式通信,通过HttpClient建立网络链接,使用HttpGet方法读取数据,并且通过Response获取Entity返回值. 使用HttpClient进行Post ...
- Java基础知识强化之网络编程笔记17:Android网络通信之 使用Http的Post方式读取网络数据(基于HTTP通信技术)
使用Http的Post方式与网络交互通信.Post方式需要向网络传输一部分数据,同时具有输入流和输出流. 详见:Android(java)学习笔记210:采用post请求提交数据到服务器(qq登录案例 ...
随机推荐
- B2
组员1:吴晓晖(组长) 过去两天完成了哪些任务 代码重构基本完成 展示GitHub当日代码/文档签入记录 接下来的计划 推荐算法 还剩下哪些任务 组员2:陈锦谋 过去两天完成了哪些任务 重新制作图标 ...
- Leetcode题库——13.罗马数字转整数
@author: ZZQ @software: PyCharm @file: Luoma2Int.py @time: 2018/9/16 17:06 要求: 罗马数字转数字 字符 数值 I 1 V 5 ...
- Android界面设计适配不同屏幕的尺寸和密度解读
Android是运行在各种提供不同的屏幕尺寸和密度的设备.Android系统提供跨设备的统一开发环境和处理大部分的工作,以调整每个应用程序的用户界面,以在其上显示的画面. 同时,该系统提供了API,允 ...
- NET Core Mvc发布带视图文件的方法!
添加节点:<MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
- jquery on 事件嵌套 事件执行多次
今天做了个项目,就是想点击添加然后追加一列,点击这一列的修改按钮,在修改, //编辑事件 $('#eventTable').on('click','.edit_n',function(){ var i ...
- XLSReadWriteII5使用示例
之前一直是使用XLSReadWriteII4,今天更新到XLSReadWriteII5,测试了一下,发现一些操作变化比较大,现将XLSReadWriteII5的使用示例写一下,以下是代码和生成的exc ...
- [转帖]TLS 版本问题
转帖 From https://www.cnblogs.com/xjnotxj/p/7252043.html 一.环境: CentOS 6.8nginx 1.6.0php 7.0.10 二.背景 最近 ...
- nginx实现ldap认证
1.安装依赖. yum -y install openldap-devel yum install pcre pcre-devel -y yum -y install openssl openssl- ...
- 018 final 关键字的用途
final关键字的含义 final在Java中是一个保留的关键字,可以声明成员变量.方法.类以及本地变量.一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变量再次初 ...
- java 自动装箱
Java 编译器把原始类型自动转换为封装类的过程称为自动装箱(autoboxing),相当于调用包装类的valueof方法.举例说明: 源码: 编译之后的代码: