一、开发环境

server端:centos 7  python-3.6.2

客户端:Windows 7 python-3.6.2 pycharm-2018

程序目的:1、学习使用socketserver实现并发处理多个客户端。

             2、了解使用struct解决TCP粘包。

二、程序设计

(本人菜鸟一枚,对于开发规范,接口设计完全不懂,完全是随心所欲,自娱自乐。写博客主要是记录自己学习的点点滴滴,如有不足之处还请见谅。)

1、server端

1.1 目录结构如下:

 1.2 目录简介:

FTP_SERVER:程序主目录

app:程序主逻辑目录,目录下有四个模块:

FTPserver.py:FTP  Server端启动入口。

login.py:认证注册模块,用于处理用户注册,登录认证。

dataAnalysis.py:命令解析模块,负责解析,执行客户端命令。

FileOpertion.py:负责文件读,写。数据发送,数据接收。

db:存放user_pwd.db文件,用于存放用户信息(用户名,密码,FTP目录总空间,已使用空间等)

lib:存放公共数据。

1.3 模块中类的继承关系

1.4 执行流程

1.4.1 程序启动文件FTPserver.py,程序启动后进入监听状态。核心代码如下:

class MyFtpServer(socketserver.BaseRequestHandler):

    def handle(self):  # 重写handle方法,处理socket请求
print(f"连接来自{self.client_address}的客户端")
commom_obj = Commom()
data_analy = DataAnalysis()
login_obj = Login()
while 1:
# 执行用户选项:1、登陆系统 2、注册账号。并返回一个结果
status_id = login_obj.run_client_choice(self.request, commom_obj)
if status_id == "": # 登陆成功
if not self.run_ftp_server(data_analy,commom_obj): # 执行ftpserver主功能
break
elif int(status_id) == -1: # client断开连接了
break
print(f"客户端{self.client_address}断开了连接") def run_ftp_server(self,data_analy,commom_obj):
""""
登陆成功后,接收客户端发来的命令,并进行处理
:param data_analy:负责解析,执行客户端命令的对象
:param commom_obj:程序执行时所需的数据对象
:return 返回false代表客户端断开连接了
"""
while True:
try:
cmd_len_pack = self.request.recv(4)
cmd_len = struct.unpack('i',cmd_len_pack)[0] # 获取命令长度,防止粘包
except Exception:
break
recv_data = self.request.recv(cmd_len).decode('utf-8') # 接收客户端数据
if recv_data.upper() == "Q": # 客户端提出断开连接了
break
# 解析,处理客户端的命令
data_analy.syntax_analysis(recv_data, self.request, commom_obj)
return False if __name__ == '__main__':
print('运行FTP服务')
ip_port = ('192.168.10.10',9000)
# 创建并发服务端对象
server = socketserver.ThreadingTCPServer(ip_port, MyFtpServer)
# 开启服务
server.serve_forever()

1.4.2 服务端进入监听状态后,客户端发起连接请求,服务端接收连接请求后会等待客户单发来状态码,1表示请求登录FTP服务器,2表示客户端要注册用户,注册用户需要服务端手动反馈状态码1才可注册。处理用户登录,注册模块login.py核心代码如下:

class Login(FileOperation):
"""
登陆注册类。主要负责用户的登陆认证,和用户注册。
"""
def run_client_choice(self,socket_obj,commom):
"""
获取客户端的请求,1是登陆,2是注册用户
:param socket_obj: socket对象
:param commom: ftpserver运行时所需要的数据对象
:return:
"""
recv_choice = socket_obj.recv(1).decode("utf-8") # 获取用户选项:1是登陆,2是注册用户
if recv_choice == "": # client请求登陆
return self.login_authen(socket_obj,commom)
elif recv_choice == "": # client请求注册账号
return self.register_user(socket_obj,commom)
else:
return -1 # client断开连接了 # 用户登陆认证
def login_authen(self,socket_obj,commom):
"""
客户端登陆认证
:param socket_obj: socket对象
:param commom: ftpserver运行时需要的数据对象
:return:返回1代表登陆成功
"""
# 接收client发来的用户名,密码
recv_userPwd = self.recv_data(socket_obj).decode("utf-8").split("|")
# 效验用户名密码
check_ret = self.check_user_pwd(recv_userPwd, socket_obj,commom)
if check_ret: # 用户名密码正确
self.check_user_home_dir(commom,recv_userPwd[0]) # 检测用户家目录
return commom.status_info["login_success"]
else:
return commom.status_info["login_fail"] ... # 注册用户
def register_user(self,socket_obj,commom):
"""
:param socket_obj:
:param commom:
:return: 返回是否允许注册的结果,1允许客户端注册,2拒绝客户端注册
"""
while True:
choice_id = input("请输入回应码:1是允许注册,2是不允许注册:")
if choice_id.isdigit() and 3 > int(choice_id) > 0:
socket_obj.send(choice_id.encode("utf-8")) # 发通知告知客户端,处理结果
if choice_id == "": # 注册用户
return self.client_register(socket_obj, commom)
return choice_id
else:
print("您输入的信息有误,请重新输入。") ...

1.4.3 客户端登录成功后,服务端会等待接收客户端发来的命令,命令的解析,执行由dataAnalysis.py模块执行,核心代码如下:

class DataAnalysis(FileOperation):
"""
数据分析处理类,主要负责解析client发送过来的指令。
"""
def syntax_analysis(self,recv_data, socket_obj, commom):
"""
负责解析客户端传来的数据。
:param recv_data:接收到的客户端用户数据
:param socket_obj:socket对象
:param commom:数据对象
:return:
"""
clientData = recv_data.split(" ")
if hasattr(self,clientData[0]): # 判断对象方法是否存在
get_fun = getattr(self,clientData[0])#获取对象方法
get_fun(clientData,socket_obj,commom) # 运行对象方法
else:
pass
...

执行客户端命令后,继续等待接收客户端发来的命令,如此循环...。

2、客户端

2.1 目录结构如下:

2.2 目录简介:

client:程序主目录。

bin:程序入口,程序启动文件main.py用于建立socket连接,然后调用FTPclient.py模块下的run_ftp_client方法运行程序。

app:程序主逻辑,目录下有四个模块如下:

FTPclient.py:FTP客户端,根据用户选项,执行用户指令。

login.py:认证注册模块,用于处理用户注册,登录认证。

dataAnalysis.py:命令解析模块,解析用户输入的命令,发给服务端获取结果。

FileOpertion.py:负责文件读,写。

lib:存放公共数据,有两个文件:

commom.py:主要存放的是公共变量。

help.txt:存放的是帮助文档,当用户执行help命令时会调用该文件。

2.3 模块中类的继承关系

2.4 执行流程

2.4.1 程序入口main.py,启动后会与FTP服务端建立连接,与服务端连接成功后会调用FTPclient.py模块下的run_ftp_client方法,执行用户功能。核心代码如下:

socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_obj.connect(("192.168.10.10",9000)) client_obj = Client()
client_obj.run_ftp_client(socket_obj) # 接收用户输入的选项,执行对应的功能

2.4.2 FTPclient.py模块下的run_ftp_client方法会打印菜单,并等待用户输入选项,执行相应功能,核心代码如下:

class Client(Login,DataAnalysis):

    def run_ftp_client(self,socket_obj):
"""
运行用户输入的选项:1、是登陆 2、是注册账号
:return:
"""
while True:
self.login_menu() # 打印系统菜单
choice_id = self.get_user_choice() # 获取用户输入的选项
if choice_id:
if self.run_user_choice(choice_id,socket_obj):
break
else:
print("您输入的有误")
def get_user_choice(self):
"""
获取用户输入的选项
:return:
"""
choice_id = input("请输入选项:")
if choice_id.isdigit() and 4 > int(choice_id) > 0 or choice_id.upper() == "Q":
return choice_id
return False
def run_user_choice(self,choice_id,socket_obj):
if choice_id == "": # 登陆系统
socket_obj.send(choice_id.encode("utf-8")) # 发通知告知服务器准备登陆
if self.run_login(socket_obj) == True: # 执行登陆
return True
elif choice_id == "": # 注册用户
socket_obj.send(choice_id.encode("utf-8")) # 请求服务器,注册用户
self.register_user(socket_obj) # 执行注册
elif choice_id.upper() == "Q": # 退出程序
socket_obj.send(choice_id.encode("utf-8")) # 通知服务器,准备退出程序
socket_obj.close()
print("程序正常退出")
return True def run_login(self,socket_obj,):
"""
运行登陆认证模块,如果登陆成功执行程序主逻辑,否则重新登陆。
:param socket_obj:
:return:
"""
if self.login_authention(socket_obj):
while True:
send_data = input(">>>").strip(" ") # 获取发送数据(用户执行的命令)
if send_data.upper() == "Q": # 正常退出程序
socket_obj.send(send_data.encode("utf-8")) # 通知服务区断开连接
socket_obj.close()
print("程序正常退出")
return True
if self.syntax_analysis(send_data, socket_obj): # 解析用户数据并处理数据
print("异常退出")
return True
return False def login_menu(self):
print("-"*41)
print(" 欢迎登陆迷你FTPv1.0")
print("-"*41)
print("1、登陆系统")
print("2、用户注册")
print("Q、退出程序")

2.4.3 login.py模块主要用于处理注册和登录的功能,核心代码如下:

class Login(Commom):
def login_authention(self,socket_obj):
"""
登陆认证
:param socket_obj:socket 对象
:return:
"""
user_pwd = self.get_user_pwd() # 获取用户名密码
self.send_data(socket_obj,user_pwd) # 将用户名和密码发给服务器
recv_status = socket_obj.recv(2).decode("utf-8") # 等待接收状态码
print(self.status_info[recv_status]) # 打印状态码对应的结果
if self.status_info[recv_status] == '登录成功':
return True
return False
... def register_user(self,socket_obj):
"""
等待服务端反馈是否允许注册用户。
:param socket_obj:
:return:
"""
print("请等待服务端回应.....")
recv_status = socket_obj.recv(1).decode("utf-8")
if recv_status == "": # 服务端同意申请账号
user_pwd = self.get_regist_user_pwd() # 获取注册用户名和密码
if user_pwd:
self.send_data(socket_obj,user_pwd)
result = socket_obj.recv(2).decode("utf-8")
print(self.status_info[result])
else:
print("用户名密码有误")
else: # 客户端拒绝申请账号的请求
print("服务端拒绝了您申请账号的请求,请与管理员取得联系。")
return False
...

2.4.4 用户登录成功后,会等待接收用户输入命令,由dataAnalysis.py模块负责解析用户输入的命令,并将命令发给FTP服务器,然后接收服务器的反馈。核心代码如下:

class DataAnalysis(FileOperation):

    def syntax_analysis(self,cmd,socket_obj):
"""
解析用户输入的命令。
:param cmd:用户执行的命令,如:put 上传的文件
:param socket_obj:socket对象发送和接收数据
:return:
"""
cmd_split = cmd.split(" ") # 将字符串命令分割成列表,用于验证命令是否存在
if hasattr(self,cmd_split[0]):
run_fun = getattr(self,cmd_split[0])
run_fun(cmd_split,socket_obj)
else:
print("无效的命令")
...

三、功能演示

测试环境:FTP服务器:虚拟机 centos 7  192.168.10.10

FTP客户端:本机 Windows 7 192.168.1.103

FTP客户端:虚拟机 Windows 7  192.168.10.102

1、注册登录

2、使用help查看帮助

3、文件夹操作(mkdir,rmdir,cd,list)

用户被锁定在FTP家目录(/home/FTP_HOME/用户名)中,无法查看其它用户的文件。

4、上传文件put

文件传输是经过MD5加密的,传输完成后客户端和服务端需要对MD5进行效验。传输完成后可以使用free查看用户空间。

4.1 上传文件(断点续传)

5、文件下载get

5.1 文件下载(断点续传)

将上面下载好的2.exe更名为3.exe。一会和断点续传的2.exe对比大小,两个文件大小一致才对。

6、rmfile 删除文件

7、执行远程命令

socket实现简单的FTP的更多相关文章

  1. SELECTORS模块实现并发简单版FTP

    环境:windows, python 3.5功能:使用SELECTORS模块实现并发简单版FTP允许多用户并发上传下载文件 结构:ftp_client ---| bin ---| start_clie ...

  2. socket.io简单入门(一.实现简单的图表推送)

    引子:随着nodejs蓬勃发展,虽然主要业务系统因为架构健壮性不会选择nodejs座位应用服务器.但是大量的内部系统却可以使用nodejs试水,大量的前端开发人员转入全堆开发也是一个因素. 研究本例主 ...

  3. 从零开始学Python08作业思路:开发简单的FTP

    一,作业要求 开发简单的FTP 1,用户登录 2,上传/下载文件 3,不同用户家目录不同 4,查看当前目录下文件 5,充分使用面向对象 二,程序文件清单 Folder目录:用户上传文件家目录 db目录 ...

  4. socket.io简单说明及在线抽奖demo

    socket.io简单说明及在线抽奖demo socket.io 简介 Socket.IO可以实现实时双向的基于事件的通信. 它适用于各种平台,浏览器或设备,也同样注重可靠性和速度. socket.i ...

  5. 运用socket实现简单的服务器客户端交互

    Socket解释: 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket. Socket的英文原义是“孔”或“插座”.作为BSD UNIX的进程通信机制,取后一种意 ...

  6. java Socket实现简单在线聊天(二)

    接<java Socket实现简单在线聊天(一)>,在单客户端连接的基础上,这里第二步需要实现多客户端的连接,也就需要使用到线程.每当有一个新的客户端连接上来,服务端便需要新启动一个线程进 ...

  7. java Socket实现简单在线聊天(一)

    最近的项目有一个在线网页交流的需求,由于很久以前做过的demo已经忘记的差不多了,因此便重新学习一下. 我计划的大致实现步骤分这样几大步: 1.使用awt组件和socket实现简单的单客户端向服务端持 ...

  8. 转:【专题十二】实现一个简单的FTP服务器

    引言: 休息一个国庆节后好久没有更新文章了,主要是刚开始休息完心态还没有调整过来的, 现在差不多进入状态了, 所以继续和大家分享下网络编程的知识,在本专题中将和大家分享如何自己实现一个简单的FTP服务 ...

  9. socket实现两台FTP服务器指定目录下的文件转移(不依赖第三方jar包)

    通过socket实现两台FTP服务器指定目录下的文件转移,其中包含了基础了ftp文件列表显示.上传和下载.这里仅供学习用,需掌握的点有socket.ftp命令.文件流读取转换等 完整代码如下: Ftp ...

随机推荐

  1. 【算法学习记录-排序题】【PAT A1062】Talent and Virtue

    About 900 years ago, a Chinese philosopher Sima Guang wrote a history book in which he talked about ...

  2. APP 安全测试

    http://www.cnblogs.com/wetest/p/6694529.html

  3. 大白话Web三大组件之一Servlet

    很多学习到Servlet这里的童鞋,听到那么多专业名词解释这个Servlet,相信都是很蒙圈的,在这里我先不跟大家扯Servlet的大概念,先跟大家探讨一下关于Servlet的作用 相信MVC这个概念 ...

  4. ONESHELL

    没有加 .ONESHELL 的时候,片段中的各行 shell 彼此独立. 加了 .ONESHELL 后,各行shell 可以看作一行 shell. <1> .PHONY: all all: ...

  5. P1177排序题解

    这恐怕是一道 坑最多 最经典 的题目了. 这道题有两种解题方法: 1.自己写个排序函数 这里我们用最最最最常用的快速排序: #include <iostream> #define ll l ...

  6. Tex 一些命令

    1. [!htp] 可以使这个内容跟随在前面的内容后面 假如前面是一段文字,后面是一幅图像,不知什么原因跑到其他地方去了.这时加个[!htp]可以使他紧紧跟在后面 ergdsgagdfgdfgfgaf ...

  7. idea中使用Data Source and Drivers时,如果使用自己自定义的jar包

  8. 【转载】Java泛型(一)

    转自:http://www.cnblogs.com/lzq198754/p/5780426.html 1.为什么需要泛型 泛型在Java中有很重要的地位,网上很多文章罗列各种理论,不便于理解,本篇将立 ...

  9. css动画 自动打字,让你的文字飞舞起来

    自动打字的效果 非一般的炫酷 <!DOCTYPE html> <html lang="en"> <head> <meta charset= ...

  10. Windows server 2012文件服务器配置

    文件服务器的管理   Windows server 2012提供了易于使用的管理工具,让系统管理员更有效的管理服务器的资源. 安装文件服务器管理工具 添加角色-安装管理器 安装完成后直接可以在工具中打 ...