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登录案例 ...
随机推荐
- P4: Programming Protocol-Independent Packet Processors
P4: Programming Protocol-Independent Packet Processors 摘要 P4是一门高级语言,用于编程与协议无关的数据包处理器.P4与SDN控制协议相关联,类 ...
- Ubuntu下ssh连接在服务端显示图形界面
Ubuntu下ssh连接在服务端显示图形界面 step1 安装ssh服务 服务端安装运行ssh,在终端运行命令如下: sudo apt-get install openssh-server 在客户端安 ...
- 我是一名IT小小鸟
我是一只it小小鸟 书中介绍了it界大牛们大学期间的学习方法和对未来的职业规划,相比他们,自我感觉相距甚远,对这学科的热情程度也远远比不上他们. 就拿目前数据结构这门高深的课程,应通过更多的课外扩展来 ...
- 结对随即四则运算(带界面Java版)
//随机四则运算类 public class 随机四则运算 { public static void main(String[] args) { new 界面();//进入随机四则运算的首界面 } } ...
- 树莓派与Arduino Leonardo使用NRF24L01无线模块通信之基于RF24库 (四) 树莓派单子节点查询
考虑到项目的实际需要,树莓派作为主机,应该只在需要的时候查询特定节点发送的数据,因此接收到数据后需要根据头部判断是否是自己需要的数据,如果不是继续接收数据,超过一定时间未查询到特定节点的数据,则退出程 ...
- C++ 游戏之点点水果
大二时利用C++编写的点水果小游戏 程序代码总共3个文件,main.cpp Fruit.h Fruit.cpp 代码将在图片下面给出 至于讲解,由于过了一年多的时间,有点忘记了,但我会努力回忆并即时 ...
- fx投影效果分离
虽然忙着花花二期三期bug.bug ing,修改等待中突然看见一张logo.文字下面的阴影图,如下,就满脑子在想阴影到底咋做的.. 七拼八凑的尝试后大体样子是有,终究没有上图那种字体轮廓的阴影... ...
- python 菜鸟入门
python 菜鸟博客: http://www.cnblogs.com/wupeiqi/articles/5433893.html http://www.cnblogs.com/linhaifeng/ ...
- ORACLE LOG的管理
CREATE OR REPLACE PACKAGE PLOG IS /** * package name : PLOG *<br/> *<br/> *See : <a h ...
- [转帖] 学习一下 apache bench 的总结简介 ( LAMP的没用过..)
PS:网站性能压力测试是性能调优过程中必不可少的一环.只有让服务器处在高压情况下才能真正体现出各种设置所暴露的问题.Apache中有个自带的,名为ab的程序,可以对Apache或其它类型的服务器进行网 ...