网络传输粘包的解决方法,然后实际应用于一个FTP项目。

本文分享了一个使用Python和socket模块实现的FTP项目,重点解决了TCP传输中的粘包问题,确保文件上传和下载的准确性。项目已部署在GitHub上,支持登录、注册、文件上传、下载和删除功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前用python写了一个FTP,已经部署到了我的github上了。But,有Bug,就是传输中粘包问题没有得到很好的解决,经过这两天的重新修改,已经可以实现正常的功能了。另外,之前写的代码都是在windows上,但是学了linux,就实际应用一下。所以我把我的项目都拿到Linux 来做了。接下来就开始谈谈吧。

先来一张这次FTP项目的流程图刺激一下(以前没怎么画过流程图,第一次画这样的,也不标准,就是按照自己的逻辑去画的。因为太多,所以部分内容也没画上去,如引起不适,见谅见谅)

顺便说说这个项目吧:就是写个客户端,一个服务器,然后客户端可以远程登录服务器,将自己的文件上传到服务器,服务器也提供文件下载功能,所以这就相当于一个远程服务器帮你存储文件,让你可以随时查看和下载。

由于代码有点多?(1000多行吧),所以就不会全部展示在博客上,有兴趣的码友欢迎访问我部署到GitHub上的源码

碎碎念。这次主要是利用python 的socket这个模块来写的。目前可以实现的功能是,针对客户端:可以登录、注册、向服务器上传文件、也可以下载文件、还可以删除服务器里的文件(当然只是针对自己的账户的文件)。在服务器端:可以允许用户登录、注册、上传文件、下载文件,其中会有一些更加细节的东西,比如服务器存储用户的账号和密码信息的方式,一般密码不会是明文存储的,不然的话,可以管理服务器的程序员就可能盗取用户信息。所以都是通过MD5算法加密的,这种加密技术因为其安全性,用的很广泛。而且是单向的,也就是无法解密密文。举个例子,一个字符串为“12345”,那么加密之后就是"827ccb0eea8a706c4c34a16891f84e7b",所以即便有人可以拿到用户信息,他也无法知道你的密码。另外,就是传输过程中的数据都是字节,所以要对传输的信息做处理,转化成字节才可以传输。可以使用json模块。

现在开始介绍几个重要的地方。

1. 导入模块,建立连接

服务器端:

import json
import socket
import os
import sys
import time
from ..DataBase import MdFive

Base_path = os.path.dirname(os.getcwd())
sys.path.append(Base_path)

 from ..DataBase import MdFive 从父目录下导入一个包DataBase,然后导入这个包下的一个叫MdFive的python文件。

self.buff = 2048 
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.sock.bind(('127.0.0.1', 8001))
self.sock.listen(5)
self.conn, self.addr = self.sock.accept()

定义一个类之后,在类里面定义一个方法,功能是接听客户端的连接。 但是这儿的IP地址只能允许本地用户访问,其实就是你自己,所以为了实现远程的登录,你可以将这个的127.0.0.1换成一个你的实际IP。

 客户端:

import socket
import json
import os
self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.conn.connect(('127.0.0.1', 8001))

定义一个客户端类,然后连接服务器,注意一个问题,这儿的IP和端口是一个元组的元素,端口是整数,不是字符串。

2.然后就是各个模块功能的实现,由于太多,我就不展示了,主要讲一个上传文件或下载文件中的重要问题——粘包。因为我们这样的连接方式是通过TCP 协议去传输的(还有一种方式是UDP,对UDP协议感兴趣的请自行百度吧)。TCP传输中,因为只要建立连接之后,客户端就可以随时向服务器发送请求。但是两次或多次请求之间,客户端的请求一直向服务器传输,这个过程如果网速,或者因为一些其他的原因,会不会存在两次的请求内容粘在一起,作为一个请求传输给服务器呢,显然是可能的。这样的话服务器无法判段是粘包所导致的,所以可能就会返回一个不可知的响应给客户端了。比如,我们要从服务器下载一个文件,但是文件内容过大,所以它不会一次性全部传输过来,而要分成好几批,这个过程中,服务器应该怎么处理这每一批的请求?那么怎么解决这个问题?

如果我们的在发送请求前,先告诉一下服务器,我们将要传输一个多少字节的内容给服务器,那么服务器那边就会做出处理,接收多大的字节作为一个请求来处理。即,假如我们要下载一个视频文件,那么完整的过程应该是这样的:

客户端发送一个请求,告诉服务器,我要下载文件了。服务器收到请求之后,就去找到这个文件,并且读取文件,获得文件大小之后,把文件大小传给客户端,客户端接收到这个响应,处理之后,用一个循环去循环的接收这个文件,比如文件大小为102400个字节,那么如果设定recv(1024),即每次最多接收1024个字节,那么就会循环100次,一直等到全部接收完,才结束。形象一点就是下面这个流程图

这个过程要注意一下,因为是下载文件,所以可以一边接收,一边写入。我是这样实现的

        count_1 = fsize / self.buff  # count_1是整数部分
        # fisize是文件大小,self.buff是每次接收的最大字节数,一般为1024的整数倍,也不要太大。
        count_2 = fsize % self.buff  # count_2是余数部分,因为文件接收的最后一个次可能没有self.buff,
        #  不这样的话,最后一次就会一直等待接收,直到接收到的字节为self.buff,
        count = 1
        with open('../download_file/'+filename, 'wb') as f:
            try:
                while True:
                    try:
                        if count_2 == 0:
                            f.write(self.conn.recv(self.buff))
                             # 这里接收到的文件不用json做处理了,因为本来就是以二进制写入的就是要字节
                            count += 1
                            if count > count_1:
                                break
                        else:
                            if count <= count_1:
                                f.write(self.conn.recv(self.buff))
                            else:
                                f.write(self.conn.recv(self.buff))
                            count += 1
                            if count > (count_1 + 1):
                                break
                    except Exception as e:
                        print(e)
                print("文件{}下载成功!".format(filename))
                self.conn.send(json.dumps(msg).encode())
                return True
            except Exception as e:
                msg["status"] = 0  # 0代表下载失败了,如果是1就是成功
                self.conn.send(json.dumps(msg).encode())
                print("下载失败!", e)
                return False

以后应该还会新增一些功能,比如提供公共区的文件下载、支持管理员登录去管理服务器、以及用户文件分享等吧。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大骨熬汤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值