使用Python做个FTP服务器(超简单)

思路
  1. 首先客户端与服务端都要先生成socket对象,创建socket实例。
  2. 然后服务端通过bind函数绑定端口,并进行监听,服务端再使用accept方法进入阻塞状态,等待一个连接的到来
  3. 客户端使用connect函数通过以元组形式传入一个IP地址与端口号,与服务端建立连接
  4. 客户端读取用户的输入,我们可以判断一下,如果这串字符以get开头,那么就把get后面的内容也就是文件名赋值给filename
  5. 客户端把接收到的文件名发送给服务端。
  6. 服务端接收到文件名后,先使用os.isfile(path)函数判断一下是否有这个文件,如果有再使用os.stat()函数获取这个文件的大小,然后把这个文件的大小发送给客户端。接下来要创建这个文件的md5对象
  7. 服务端要把这个文件打开,使用一个for循环进行读取文件的内容,每次读取的时候都要进行md5检验值的更新
  8. 客户端收到服务端发过来的文件大小之后,接下来使用while循环来接收服务端发送过来的数据,并把这些数据写入到一个新文件里面,这样就实现了文件的发送与接收
注意事项

编写代码时特别容易犯错的以下几点,我就犯了几次细节的错误,大家注意

  • 调用md5对象的update方法进行校验码的更新的时候记得参数必须传bytes类型(二进制字节流)
  • 要考虑粘包的问题,所以必须要在两次send()里面加上一个recv()接收ack信息,这样吧两次send()分开,可以避免粘包的出现
  • 为了防止出现粘包的现象,在循环接收的时候,最后一次接收中进行判断,看还剩多少数据没收到的,剩多少就收多少。
实现

ftp server

  1. 读取文件名
  2. 检测文件是否存在
  3. 打开文件
  4. 检测文件大小
  5. 发送文件大小给客户端
  6. 等客户端确认
  7. 开始边读边发数据
  8. 发送md5

代码:

import hashlib
import socket ,os,time
server = socket.socket()
server.bind(('0.0.0.0',9999) )
server.listen()
while True:
    conn, addr = server.accept()
    print("new conn:",addr)
    while True:
        print("等待新指令")
        data = conn.recv(1024)
        if not data:
            print("客户端已断开")
            break
        cmd,filename = data.decode().split()
        print(filename)
        if os.path.isfile(filename):
           f = open(filename,"rb")
           m = hashlib.md5()
           file_size = os.stat(filename).st_size
           conn.send( str(file_size).encode() ) #send file size
           conn.recv(1024) #wait for ack
           for line in f:
              m.update(line)
              conn.send(line)
           print("file md5", m.hexdigest())
           f.close()
           conn.send(m.hexdigest().encode()) #send md5
        print("send done")

server.close()

ftp client

import socket
import hashlib

client = socket.socket()

client.connect(('localhost', 9999))

while True:
    cmd = input(">>:").strip()
    if len(cmd) == 0: continue
    if cmd.startswith("get"):
        client.send(cmd.encode())
        server_response = client.recv(1024)  # 接收文件的size
        print("servr response:", server_response)
        client.send(b"ready to recv file")
        file_total_size = int(server_response.decode())
        received_size = 0
        filename = cmd.split()[1]
        f = open(filename + ".new", "wb")
        m = hashlib.md5()

        while received_size < file_total_size:
            if file_total_size - received_size > 1024:  # 要收不止一次
                size = 1024
            else:  # 最后一次了,剩多少收多少
                size = file_total_size - received_size
                print("last receive:", size)

            data = client.recv(size)
            received_size += len(data)
            m.update(data)
            f.write(data)
            # print(file_total_size,received_size)
        else:
            new_file_md5 = m.hexdigest()
            print("file recv done", received_size, file_total_size)
            f.close()
        server_file_md5 = client.recv(1024)
        print("server file md5:", server_file_md5)
        print("client file md5:", new_file_md5)

client.close()

参考:
alex的博客
廖雪峰的Python教程

使用socketserver
import socketserver
import json,os
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 uploaded..." % filename)

    def handle(self):
        while True:
            try:
                self.data = self.request.recv(1024).strip()
                print("{} wrote:".format(self.client_address[0]))
                print(self.data)
                cmd_dic = json.loads(self.data.decode())
                action = cmd_dic["action"]
                if hasattr(self,action):
                    func = getattr(self,action)
                    func(cmd_dic)

            except ConnectionResetError as e:
                print("err",e)
                break
if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    # Create the server, binding to localhost on port 9999
    server = socketserver.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值