前戏:

之前在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(处理粘包)的更多相关文章

  1. python爬虫主要就是五个模块:爬虫启动入口模块,URL管理器存放已经爬虫的URL和待爬虫URL列表,html下载器,html解析器,html输出器 同时可以掌握到urllib2的使用、bs4(BeautifulSoup)页面解析器、re正则表达式、urlparse、python基础知识回顾(set集合操作)等相关内容。

    本次python爬虫百步百科,里面详细分析了爬虫的步骤,对每一步代码都有详细的注释说明,可通过本案例掌握python爬虫的特点: 1.爬虫调度入口(crawler_main.py) # coding: ...

  2. python基础教程总结13——网络编程,

    1.网络设计模块 1.1 socket模块    根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认. 1)服务器监听:是服务器端套接 ...

  3. 第八篇:python基础_8 面向对象与网络编程

    本篇内容 接口与归一化设计 多态与多态性 封装 面向对象高级 异常处理 网络编程 一. 接口与归一化设计 1.定义 (1)归一化让使用者无需关心对象的类是什么,只需要知道这些对象都具备某些功能就可以了 ...

  4. python基础知识回顾之列表

    在python 中,主要的常用数据类型有列表,元组,字典,集合,字符串.对于这些基础知识,应该要能够足够熟练掌握. 如何创建列表: # 创建一个空列表:定义一个变量,然后在等号右边放一个中括号,就创建 ...

  5. (网络编程)基于tcp(粘包问题) udp协议的套接字通信

    import   socket 1.通信套接字(1人1句)服务端和1个客户端 2.通信循环(1人多句)服务端和1个客户端 3.通信循环(多人(串行)多句)多个客户端(服务端服务死:1个客户端---&g ...

  6. Java基础知识强化之网络编程笔记24:Android网络通信之 AndroidAsync(基于nio的异步通信库)

    1. AndroidAsync   AndroidAsync 是一个基于nio的异步socket ,http(客户端服务器端),websocket,socket.io库,AndroidAsync 是一 ...

  7. Java基础知识强化之网络编程笔记23:Android网络通信之 Volley(Google开源网络通信库)

    联合网上资料学习:http://www.open-open.com/lib/view/open1451223702339.html 一.Volley的介绍 1. Volley简介 在这之前,我们在程序 ...

  8. Java基础知识强化之网络编程笔记19:Android网络通信之 HttpClient和传统Post、Get方式的区别

    1. HttpClient是什么 ?     HTTP 协议可能是现在 Internet 上使用得最多.最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源.虽然在 ...

  9. Java基础知识强化之网络编程笔记18:Android网络通信之 使用HttpClient的Post / Get 方式读取网络数据(基于HTTP通信技术)

    使用HttpClient进行Get方式通信,通过HttpClient建立网络链接,使用HttpGet方法读取数据,并且通过Response获取Entity返回值. 使用HttpClient进行Post ...

  10. Java基础知识强化之网络编程笔记17:Android网络通信之 使用Http的Post方式读取网络数据(基于HTTP通信技术)

    使用Http的Post方式与网络交互通信.Post方式需要向网络传输一部分数据,同时具有输入流和输出流. 详见:Android(java)学习笔记210:采用post请求提交数据到服务器(qq登录案例 ...

随机推荐

  1. Task 4.2 求一个矩阵的最大子矩阵的和

    任务:输入一个二维整形数组,数组里有正数也有负数.二维数组中连续的一个子矩阵组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为O(n). (1)设计思想:把二维矩阵分解成 ...

  2. Android笔记-2-TextView的属性详解

    [Android 基础]TextView的属性详解 android:autoLink :设置是否当文本为URL链接/email/电话号码/map时,文本显示为可点击的链接.可选值(none/web / ...

  3. Java集合技巧

    集合的一些技巧:   需要唯一吗? 需要:Set 需要制定顺序:  需要: TreeSet 不需要:HashSet 但是想要一个和存储一致的顺序(有序):LinkedHashSet 不需要:List ...

  4. CANOpen的几种操作以及数据

    其实3年前在21ic就准备做这篇文章了,那时,CANOpen也只是刚刚在国内推广,所以几乎没有项目用到.现在有了实际的项目,完全确认了以前移植和测试的代码,所以列举一些CANOpen的底层操作以及数据 ...

  5. 订制EditText光标

    订制EditText光标 设置背景android:background="@null" 设置光标样式:android:textCursorDrawable="@drawa ...

  6. [2017BUAA软工]个人项目心得体会:数独

    心得体会 回顾此次个人项目,感受比较复杂,最明显的一点是--累!代码编写.单元测试.代码覆盖.性能优化,环环相扣,有种从作业发布开始就一直在赶DDL的感觉,但是很充实,也学习到和体验了很多东西.最令人 ...

  7. 关于JEE web项目 Servlet中 “/” 的解释 ;

    1.关于"/" 可以代表web应用的根目录,也可以代表站点的根目录: 1>如果交给浏览器解析,则代表web站点的根目录,如果交给web服务器解析则代表项目的根目录: 2> ...

  8. Powershell笔记之MVA课程

    很早之前看过MVA的Powershell课程,最近准备回顾一下,还是有一些意外的收获. <<快速入门 : PowerShell 3.0 高级工具和脚本>> 1. Invoke- ...

  9. intel 的架构图

    第一代: 第二代 第三代 以及对比

  10. 13个实用的Linux find命令示例

    除了在一个目录结构下查找文件这种基本的操作,你还可以用find命令实现一些实用的操作,使你的命令行之旅更加简易. 本文将介绍15种无论是于新手还是老鸟都非常有用的Linux find命令. 首先,在你 ...