zoukankan      html  css  js  c++  java
  • Socketserver

    上周的作业写了单线程的服务端,这周晋级一下,写个多线程的客户端。

    主要使用到的就是socketserver,这个模块存在的主要功能就是简化网络服务器的书写。

    使用它的主要步骤有:

    1.根据BaseRequestHandler创建一个类,并且需要重新定义父类的handler方法

    class MyTCPHandler(socketserver.BaseRequetHandler)

    def handler(self)主要用于获取用户名字,验证登录,然后根据用户的需求,执行相应的方法

    2.实例化TCPServer, 将server ip, port 还有上面上面创建的请求处理类传递给TCPServer

    server = socketserver.ThreadingTCPServer(('localhost',9999),MyTCPHandler)

    3.调用server.serve_forever()#处理多个请求,或者server.server_request()#处理一个请求

    4.关闭server, server.close()

    虽然不是很难,但是要写一个传递,一个收取然后在发送,这个还是有些绕的。不多说,还是先上流程图看起来比较清晰。

    有了流程图,思路就清楚多了。然后就是小心细心的把server和client写好了。感觉这个程序写起来,也不是很难,传来传去,别弄晕了就好。

    下面是server端的代码:

    #__author__ = 'little hunter'
    #!usr/bin/env/Python
    # -*- coding: utf-8 -*-
    import json
    import os
    import socketserver
    import sys
    import hashlib
    sys.path.append('..')
    from conf import setting
    server_response = setting.Server_Response
    client_size = setting.ClientSize
    basepath = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) #ftp_server
    db_path = os.path.join(basepath,'db')
    client_catalogue_path = os.path.join(basepath,'client_catalogue')
    #继承父类,然后重写父类中的handle方法。注意handle方法 和客户端所有的交互都是在handle中写的
    #每一个客户端的请求过来,都会实例化
    
    class MyTCPHandler(socketserver.BaseRequestHandler):
    
       #需要询问客户到那个目录下
        def get_dir(self,clientname):
            client_path = os.path.join(client_catalogue_path,clientname)
            dir_list = os.listdir(client_path)
            dir_dict = {0:'home'}
            for item in dir_list:
                if os.path.isdir(os.path.join(client_path,item)):
                    dir_dict[dir_list.index(item)+1]=item
            print(dir_dict)
            self.request.send(json.dumps(dir_dict).encode()) #将服务器端有的目录发给客户端,询问是要上传到那个目录下
            dir = self.request.recv(1024).decode()
            if dir == 'home':
                client_dir_path = client_path
            else:
                client_dir_path = os.path.join(client_path,dir)
            return client_dir_path
    
    
        """接受客户端上传文件"""
        def upload(self,*args):
            """接受客户端的文件"""
            upload_flag = True
            while upload_flag:
                cmd_dic = args[0]
                clientname = cmd_dic['username']
                filename = cmd_dic['filename']
                filesize = int(cmd_dic['filesize'])
                client_dir_path = self.get_dir(clientname)
                client_file_path = os.path.join(client_dir_path,filename)
                client_db_path = os.path.join(db_path,clientname)
                with open(client_db_path,'r') as f:
                    for line in f:
                        content_list = line.split(',')
                        client_usedsize = int(content_list[3])
                client_totalsize = int(self.find_client_totalsize(clientname))
                if client_totalsize - client_usedsize-filesize >= 0 :
                    self.request.send(server_response[200].encode()) #send b'ok' to client, and update the space that the client can use in the server
                    client_usedsize += filesize
                    content_list[3] = str(client_usedsize)
                    print('updated client used size',client_usedsize)
                    res = ",".join(content_list)
                    with open(client_db_path,'w') as f:
                        f.write(res)
                else:
                    self.request.send(server_response[100].encode()) #send b'not enough space' to client
                    upload_flag = False
                    break
                if os.path.isfile(client_file_path):
                    f = open(client_file_path + '.new','wb')
                else:
                    f = open(client_file_path,'wb')
                received_size = 0
                m = hashlib.md5()
                while received_size < filesize:
                    if filesize - received_size >=1024:
                        size = 1024
                    else:
                        size = filesize - received_size
                    data = self.request.recv(size)
                    f.write(data)
                    m.update(data)
                    received_size += len(data)
                    self.request.send(str(received_size).encode('utf-8'))
                else:
                    sent_file_md5 = self.request.recv(1024)
                    print("The md5 of the uploaded file is:",sent_file_md5)
                    print('The md5 of the received file is:',m.hexdigest())
                    if sent_file_md5.decode() == m.hexdigest():
                        print('33[32;0mfile [%s] has uploaded completely33[1m'%filename)
                        self.request.send(b'file has uploaded completely')
                    else:
                        print('33[31;0mAttention,file [%s] has uploaded partially33[1m'%filename)
                        self.request.send(b'Attention, file has uploaded partially')
                    f.close()
                break
            return upload_flag,filename
    
        """接受客户端要求下载文件"""
        def download(self,*args):
            download_flag = True
            while download_flag:
                cmd_dic = args[0]
                clientname = cmd_dic['username']
                print(clientname)
                client_dir_path = self.get_dir(clientname) #询问客户是在要去哪个目录下,并且打印该目录下的文件
                client_dir_file_list = []
                for item in os.listdir(client_dir_path):
                    if os.path.isdir(os.path.join(client_dir_path,item)):
                        pass
                    else:
                        client_dir_file_list.append(item)
                print(client_dir_file_list)
                client_dir_file_dict = {}
                for item in client_dir_file_list:
                    client_dir_file_dict[client_dir_file_list.index(item)] = item
                print(client_dir_file_dict)
                self.request.send(json.dumps(client_dir_file_dict).encode('utf-8')) #将服务器端的客户的目录下的文件发送给客户
                filename = self.request.recv(1024).decode()
                client_file_path = os.path.join(client_dir_path,filename)
                if os.path.isfile(client_file_path):
                    filesize = os.stat(client_file_path).st_size
                    print('The file size is',filesize)
                    self.request.send(str(filesize).encode()) #发送文件大小给客户端
                    client_response= self.request.recv(1024).decode() #收到客户端的响应
                else:
                    print('The file requested to download is not exist')
                    download_flag = False
                    break
                m = hashlib.md5()
                with open (client_file_path,'rb') as f:
                    for line in f:
                        self.request.send(line)
                print('File is sent to client')
                self.request.send(m.hexdigest().encode())
                break
            return download_flag,filename
    
    
        """查找配置文件中客户的磁盘大小,最大数据级别为GB"""
        def find_client_totalsize(self,clientname):
            temp = client_size[clientname].split() #temp[0] is number, temp[1] is the type
            if temp[1] == 'B':
                cof = 1
            elif temp[1]  == 'KB':
                cof = 1024
            elif temp[1] == 'MB':
                cof = 1024*1024
            elif temp[1] == 'GB':
                cof = 1024*1024*1024
            else:
                cof = 0
            return int(temp[0])*cof
    
    
        """根据db文件中的客户存储的md5值验证是否登录成功"""
        def verify_user(self,data,clientname):
            authenticated_flag = False
            client_db_path = os.path.join(db_path,clientname)
            with open(client_db_path,'r') as f:
                for line in f:
                    user_info_list = line.strip().split(',')
                    if user_info_list[2] == data.decode():
                        authenticated_flag = True
            return authenticated_flag
    
    
        def handle(self):
            while True:
                try:
                    clientname = self.request.recv(1024).decode() #这里的self.data是用户名字
                    print("{} wrote:".format(self.client_address[0]))
                    print('Received request from the client:',clientname)
                    #接受的加密后的用户名,密码
                    self.data = self.request.recv(1024)
                    authenticated_flag = self.verify_user(self.data,clientname)
                    self.request.send(str(authenticated_flag).encode()) #True登录成功,反之,失败
                    finish_flag = False
                    while not finish_flag:
                        self.data = self.request.recv(1024) #上传客户端的指定字典
                        cmd_dic = json.loads(self.data.decode())
                        action = cmd_dic['action']
                        if hasattr(self,action):
                            func = getattr(self,action)
                            res,filename = func(cmd_dic)
                        if res == 'True':
                            finish_flag = True
                        print(cmd_dic['action'],filename,res)
                except Exception as e:
                    print('err,',e)
                    break
    
    
    # server = socketserver.TCPServer((HOST,PORT),MyTCPHandler) #不能支持并发,只能一个线程占线
    def run():
        HOST, PORT = 'localhost',9999
        server = socketserver.ThreadingTCPServer((HOST,PORT),MyTCPHandler) #每来一个请求都会来一个线程
        server.serve_forever()
        server.close()
    

     之后是Client端的代码:

    #__author__ = 'little hunter'
    #!usr/bin/env/Python
    # -*- coding: utf-8 -*-
    import socket
    import hashlib
    import os
    import json
    import sys
    homepath = os.path.dirname(os.path.abspath(__file__))
    
    class Myclient(object):
    
        def __init__(self):
            self.client = socket.socket()
        def connect(self,ip,port):
            self.client.connect((ip,port))
    
        def  interactive(self):
            authenticated_state, username =self.authenticated()
            while authenticated_state == 'True':
                cmd = input(">>:").strip() #用户输入 upload test
                if len(cmd) == 0:continue
                cmd_str = cmd.split()[0] #将指令和文件名分隔开来
                if hasattr(self,"cmd_%s"%cmd_str):
                    func = getattr(self,"cmd_%s"%cmd_str)
                    func(cmd,username)
                else:
                    self.help()
            else:
                print('User is not authenticated')
    
    
        def cmd_upload(self,*args):
            upload_flag = True
            while upload_flag:
                cmd_split = args[0].split() #是一个列表,动作 + 文件名
                username = args[1]
                if len(cmd_split) >1:
                    userpath = os.path.join(homepath,username)
                    filename = cmd_split[1]
                    filepath = os.path.join(userpath,filename)
                    if os.path.isfile(filepath):
                        filesize = os.stat(filepath).st_size
                        msg_dic = {
                            "username":username,
                            "action":cmd_split[0],
                            "filename":filename,
                            "filesize":filesize
                        }
                        self.client.send(json.dumps(msg_dic).encode('utf-8'))
                        #接受服务端的询问,是上传到哪个目录下
                        dir_dict = json.loads(self.client.recv(1024).decode())
                        print(dir_dict)
                        target_dir_index = input('Which catalogue do you want to upload? Enter the index').strip()
                        target_dir = dir_dict[target_dir_index]
                        self.client.send(target_dir.encode())
    
                        server_response = self.client.recv(1024).decode() #接受客户的端的响应,看是不是能上传了(空间是不是够),可能接受到的是字典
                        m = hashlib.md5()
                        if server_response == 'OK':
                            f = open(filepath,'rb')
                            for line in f:
                                self.client.send(line)
                                m.update(line)
                                sent_size = int(self.client.recv(1024).decode())
                                process = sent_size/filesize*100
                                print("%d%%"%int(process))
                            else:
                                print('file in sent')
                                self.client.send(m.hexdigest().encode()) #上传是,客户端向服务器端发送md5
                                f.close()
                                print(self.client.recv(1024).decode())
                                break
                        else:
                            print(server_response)
                            upload_flag = False
                            break
                    else:
                        print(filename,'is not exit')
    
        def cmd_download(self,*args):
            #先给客户打印服务器端的目录
            download_flag = True
            while download_flag:
                cmd = args[0]
                username = args[1]
                userpath = os.path.join(homepath,username)
                msg_dic = {
                    "username":username,
                    "action":cmd,
                }
                self.client.send(json.dumps(msg_dic).encode('utf-8')) #向服务器端发送下载请求
                dir_dict = json.loads(self.client.recv(1024).decode())
                print(dir_dict)
                target_dir_index = input('Please enter the index of the catalogue you would like enter into:').strip()
                target_dir = dir_dict[target_dir_index]
                self.client.send(target_dir.encode()) #将需要进入的目录发送给服务器端
                dir_file_dict = json.loads(self.client.recv(1024).decode())
                print('Files presented in this catalogues are:')
                print(dir_file_dict)
                filename_index = input('Please enter filename to download:').strip()
                filename = dir_file_dict[filename_index]
                self.client.send(filename.encode())
                filesize = int(self.client.recv(1024).decode())
                print('filesize',filesize)
                self.client.send(b'Ready to receive file')
                client_file_path = os.path.join(userpath,filename)
                m = hashlib.md5()
                if os.path.isfile(client_file_path):
                    f = open(client_file_path + '.new','wb')
                else:
                    f= open(client_file_path,'wb')
                received_size = 0
                while received_size < filesize:
                    if filesize - received_size >=1024:
                        size = 1024
                    else:
                        size = filesize - received_size
                    data = self.client.recv(size)
                    f.write(data)
                    received_size += len(data)
                    process = received_size/filesize*100
                    print("%d%%"%int(process))
                else:
                    sent_file_md5 = self.client.recv(1024)
                    print("The md5 of the download file is:",sent_file_md5)
                    print('The md5 of the received file is:',m.hexdigest())
                    if sent_file_md5.decode() == m.hexdigest():
                        print('33[32;0mfile [%s] has downloaded completely33[1m'%filename)
                    else:
                        print('33[31;0mAttention,file [%s] has downloaded partially33[1m'%filename)
                    f.close()
                break
    
        def help(self):
            msg = """
            dir home:进入主目录
            upload filename:上传文件
            download filename:下载文件
            """
        def authenticated(self): #登录成功之后返回用户名
            inp = input("Pleae enter your username,password:").strip() #用户名,密码:liqing,1234
            username,pwd = inp.split(',')
            self.client.send(username.encode())
            m = hashlib.md5()
            m.update(inp.encode()) #hashlib 支持二进制
            self.client.send(m.hexdigest().encode()) #给服务器段发送用户名和密码
            res = self.client.recv(1024).decode()#接受服务器端发回来的相应,看是否能登陆成功
            print('Authentication is ',res)
            return res,username
    
    ftp = Myclient()
    ftp.connect('localhost',9999)
    ftp.interactive()
    
  • 相关阅读:
    MySQL数据丢失讨论
    分布式系统之Quorum (NRW)算法
    阿里巴巴-OS事业群-OS手机事业部-系统服务部门招聘Java开发工程师,有意者请进来
    EQueue
    ENode 2.0
    关于MySQL的在线扩容
    我收藏的技术知识图(每张都是大图)
    关于实现一个基于文件持久化的EventStore的核心构思
    Actor的原理
    OAuth 2.0 授权原理
  • 原文地址:https://www.cnblogs.com/little-hunter/p/6459492.html
Copyright © 2011-2022 走看看