本文链接:https://blog.csdn.net/weixin_42898819/article/details/81502058
FTP协议的C语言实现
https://blog.csdn.net/Zhu_Zhu_2009/article/details/82147473
一、FTP协议简介
FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为“文传协议”。用于Internet上的控制文件的双向传输。同时,它也是一个应用程序(Application)。基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件。在FTP的使用当中,用户经常遇到两个概念:”下载”(Download)和”上传”(Upload)。”下载”文件就是从远程主机拷贝文件至自己的计算机上;”上传”文件就是将文件从自己的计算机中拷贝至远程主机上。用Internet语言来说,用户可通过客户机程序向(从)远程主机上传(下载)文件。
二、FTP Server的用户分类及权限归属
在考虑FTP服务器安全性工作的时候,第一步要考虑的就是谁可以访问FTP服务器。以下有三种客户可以访问FTP Server:
一类是Real帐户。这类用户是指在FTP服务上拥有帐号。当这类用户登录FTP服务器的时候,其默认的主目录就是其帐号命名的目录。但是,其还可以变更到其他目录中去。如系统的主目录等等。
第二类帐户是Guest用户。在FTP服务器中,我们往往会给不同的部门或者某个特定的用户设置一个帐户。但是,这个账户有个特点,就是其只能够访问自己的主目录。服务器通过这种方式来保障FTP服务上其他文件的安全性。拥有这类用户的帐户,只能够访问其主目录下的目录,而不得访问主目录以外的文件。
第三类帐户是Anonymous(匿名)用户,这也是我们通常所说的匿名访问。这类用户是指在FTP服务器中没有指定帐户,但是其仍然可以进行匿名访问某些公开的资源。
三、写一个简单的ftp协议
下面我们写一个简单的匿名用户可以访问的ftp协议,前两种账户的功能我会在后续博客中逐一完善。在写一个ftp协议之前你需要了解以下几点:
- ftp作为一个服务器端和客户端相互传输文件的协议,我们在写的时候就要分别写服务器端程序server_ftp.py和客户端程序client_ftp.py。
-
我们要实现多个客户端可以同时访问服务器端,所以要通过多线程的方式来使得多个客户端访问,在这里我们服务器端通过socketserver来写。
-
最后通过将一个txt文件通过客户端上传到服务器端来验证所写代码的准确性。在下面的代码中我会详细备注每条语句完成的功能。
客户端代码:
import socket #导入socket模块,用来实现socket通信
import os #导入os模块,主要用来调用系统命令,获得路径
import json #导入json模块,将字符串形式的json数据转化为字典,也可以将Python中的字典数据转化为字符串形式的json数据
class FtpClient(object ):
def __init__(self):
self.client=socket.socket() #声明客户端利用socket通信
def help(self): #写一个打印一些指令的帮助信息函数,
msg='''
ls
pwd
cd../..
get filename
put filename
'''
def connect(self,ip,port): #定义一个连接服务器函数,调用client.connect()方法,连接服务器端
self.client.connect((ip,port))
def interactive(self): #定义一个与服务器交互的函数
while True:
cmd=input('>>').strip() #用户在客户端输入指令,strip()去掉用户输入指令的空格和换行符
if len(cmd)==0:continue
cmd_str=cmd.split()[0] #拆分指令的第一个字符赋给cmd_str,永远都是指令help()
#反射
if hasattr(self,'cmd_%s'%cmd_str ): #hasattr() 函数用于判断对象是否包含对应的属性
func=getattr(self,'cmd_%s'%cmd_str) # 函数用于返回一个对象属性值
func(cmd) #取到help中的指令 ls,pwd,get ,put。然后传入后面的 filename,这样后面的函数名字就叫做cmd_put cmd_get...
else:
self.help()
def cmd_put(self,*args): #写一个通过客户端上传文件的函数,*args为了接收更多数据
#上传一个文件
cmd_split= args[0].split() #将传入的第一个参数赋值给cmd_split,变为列表
if len(cmd_split)>1: #这里大于1 因为最后我们输入put filename.txt进行验证,由于存在put所以大于1
filename=cmd_split[1]
if os.path.isfile(filename): #判断要上传的文件是否存在
filesize=os.stat(filename).st_size #获取文件大小
#发送文件大小,文件名,进行的操作(这里我们默认put上传数据)给服务器,所以写成json字典形式,需要扩展直接在这加
msg_dic={
'action':'put',
'filename':filename,
'size':filesize
}
self.client.send(json.dumps(msg_dic).encode('utf-8'))#发给服务器端,json.dumps()字典转换为json格式
server_response=self.client.recv(1024) #等待服务器响应
f=open(filename ,'rb') #打开文件,发送给服务器
for i in f:
self.client.send(i)
else:
print('文件传输完毕')
f.close()
else:
print(filename ,'is not exist')
def cmd_get(self): #定义一个从服务器下载文件函数,这里和上面大同小异,先不写
pass
ftp=FtpClient() #实例化
ftp.connect('localhost',9999) #连接服务器端口
ftp.interactive() #调用和服务器交互函数
服务器端代码
import socketserver #利用socketserver来写
import json ,os
#自己写一个请求处理类,继承BaseRequestHandler
class MyTCPHandler(socketserver.BaseRequestHandler):
def put(self,*args):
#接收客户端文件
cmd_dic=args[0]
filename=cmd_dic['filename'] #获取文件名
filesize=cmd_dic['size'] #获取文件大小
if os.path.isfile(filename ): #如果已经存在上传文件,新建一个文件
f=open(filename+'.new','wb')
else:
#如果不存在 给客户端响应上传
f=open(filename ,'wb')
self.request.send(b'200 ok') #响应客户端
received_size=0
while received_size < filesize : #循环接收文件
data=self.request.recv(1024)
f.write(data)
received_size +=len(data)
else:
print('file[%s] has overload..'%filename ) #文件传输完成
#跟客户端的交互在handle中
def handle(self):
while True :
try:
self.data=self.request.recv(1024).strip()
#format格式化 打印客户端ip地址
print('{}wrote:'.format(self.client_address[0]))
print(self.data)
cmd_dic=json.loads(self.data.decode())#json字符串转为字典
action=cmd_dic['action'] #获取进行的操作 这里默认是put
#反射
if hasattr(self,action ): #判断put操作是否存在
func=getattr(self ,action )
func(cmd_dic)
except ConnectionResetError as e:
print(e)
break
if __name__ =='__main__':
HOST,POST='localhost',9999
# 实例化TCPServer
server=socketserver.ThreadingTCPServer((HOST,POST),MyTCPHandler ) #ThreadingTCPServer:多线程,多个客户端可以请求
#这样此服务器就可以让多个客户端连接
#处理多个请求
server.serve_forever()
运行结果
我们把需要测试的文件lianxiren.txt放在客户端目录下,先运行服务器,在运行客户端,并且输入put lianxiren.txt 可以发现文件传输完成,在服务器的文件夹中会发现传输过去的txt文件。