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登录案例 ...
随机推荐
- TCP/IP,HTTP,HTTPS,WEBSocket协议
我看看着挺多的,我暂时没时间自己写,有需要的请借鉴 http://mp.weixin.qq.com/s?__biz=MzI0MDQ4MTM5NQ==&mid=2247486927&id ...
- C#简单窗体应用程序(三)
使用C#创建窗体应用程序的基本步骤: (1)创建项目: (2)用户界面设计: (3)属性设置: (4)编写程序代码: (5)保存.调试.运行: 例题:设计歌曲列表界面,效果如下: 第一步:创建项目: ...
- angularJS1笔记-(6)-自定义过滤器
html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...
- 关于localStorage 应用总结
window.localStorage 设置数据几种方式 1.localStorage.setItem('name',c); 2.localStorage.name=c; 3.localStorage ...
- lr关联-保存数组并调用(转)
LOADRUNNER中的一个关联技巧 众所周知,在LoadRunner中,关联是一个很重要的动作,大多数的脚本在录制完成后并不能直接回放,需要通过一定的关联才能成功回放.关联的技巧有很多,这里 ...
- TP5 多入口文件配置的坑
闲话不多说,TP5(5.0.20) 在配置多入口文件的时候你是否遇到过一下的问题呢? 开发设计的需求吧网站拆分为前台.后台.API 3 个模块,对应的也需要3个入口文件,后台和API入口文件是用PAT ...
- Angular中sweetalert弹框的使用详解
最近太忙了,项目中使用的弹框老板嫌太丑,让我们优化一下,我在网上找了一下,找到了sweetalert弹框,算是比较好看的弹框了.所以我就想办法将sweetalert用到项目中,在项目中引入sweeta ...
- javascript易混淆的split()、splice()、slice()方法详解
很多时候,一门语言总有那么些相似的方法,容易让人傻傻分不清楚,尤其在不经常用的时候.而本文主要简单总结了JavaScript中的关于字符串和数组中三个容易混淆的方法.旨在方便查阅,在容易混淆的时候有据 ...
- Python实现奖金计算两种方法的比较
应发奖金计算 简述:企业发放的奖金根据利润提成.利润(profit)低于或等于10万元时,奖金可提10%: 利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部分,可提成 ...
- linux grep --我最喜欢的命令~~
转:http://www.cnblogs.com/end/archive/2012/02/21/2360965.html Grep 命令 用法大全 1. 参数: -I :忽略大小写 -c :打印匹配的 ...