前戏:

之前在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. java怎样把后台值传到前台

    后台代码:if(this.Request["type"]!=null){aaa=this.Request["type"].ToString(); try{bbb ...

  2. max值——单元测试

    设计思想 在调试的时候,尽可能的将所有可能出现的情况都考虑到,输入这些情况,查看程序运行的结果 源代码 #include<iostream> using namespace std; in ...

  3. 1到N中“1”出现的次数

    题目:给定一个十进制的正整数,写下从1开始,到N的所有整数,然后数一下其中出现“1”的个数 思路:刚开始做的时候,是想从1到N进行遍历,其中每个数都出现1的个数加起来,最后得出结果,但是老师让我们找规 ...

  4. 基于GUI的小学生四则运算系统

    前言:首先在此感谢我的结对搭档,没有她的帮助和引导绝不会有现在的项目.很高兴和她一起结对完成这个项目.搭档真的是棒棒哒! 一.Coding.Net项目地址: https://git.coding.ne ...

  5. VS提示“无法启动IIS Express Web服务器”的解决方法

    有时在使用Visual Studio运行项目时,会提示“无法启动IIS Express Web服务器”,如图: 可以依次尝试以下方法(我的情况使用第一种就解决了): 1.可能原因:误操作执行了:Sc ...

  6. WDS迁移注意事项

    先说背景:公司使用WDS来部署操作系统,目前DHCP和WDS都安装在同一台服务器上,但是此服务器已过保,所以筹划迁移,将WDS和DHCP分别迁移到两台服务器上.迁移计划是保持WDS暂时不动,DHCP先 ...

  7. 小程序 获取用户的openid

    wx.login({ success: res => { var code = res.code; //返回code // 小程序appid var appId = 'wxd751fc845c9 ...

  8. Java:JUnit4使用详解

    对于Junit的总是一知半解不太懂,现在认真梳理一下: 此次针对的是Junit4版本,注解也是在Junit4版本才有的,之前的版本并无注解功能.而注解开发基本上被认为是一种优秀的设计,所以我们写单元测 ...

  9. filebeat向kafka中传输数据报WARN Failed to connect to broker DOMSDev07:9092: dial tcp: lookup DOMSDev07: getaddrinfow: No such host is known.解决方法

    打开filebeat客户端所在机器C:\Windows\System32\drivers\etc目录,找到hosts文件 以记事本形式打开,在底部追加 “IP 主机名” 即可

  10. java 对象的解释过程