基于多线程多用户的FTP服务器与客户端功能实现

项目介绍:

  1. 用户加密认证
  2. 允许同时多用户登录
  3. 每个用户有自己的家目录 ,且只能访问自己的家目录
  4. 对用户进行磁盘配额,每个用户的可用空间不同
  5. 允许用户在ftp server上随意切换目录
  6. 允许用户查看当前目录下文件
  7. 允许上传和下载文件,保证文件一致性
  8. 文件传输过程中显示进度条

实现的原理:

    服务器端启用端口监听,并对每一连接启用一个线程,对用户登陆密码采用SHA512进行加密并进行匹配,当用户登陆成功后,实例化FTPS,并引导客户端进入主命令模式,

  然后实现FTP的上传功能、下载功能、新建目录、删除文件或目录、切换目录等实例化操作,同时对相关上传下载进行进度条显示,服务器端显示下载或上传文件的大小等

    客户端与服务器协商建立连接后,进行用户身份登陆,登陆成功接收服务器指令,转入命令输入窗口,同时对put 与 get命令进行判断,实现特定的上传与下载功能

核心代码实现如下:

  服务器端

  main.py

#!/usr/bin/env python3.5
# -*-coding:utf8-*-
import os,sys,socket,pickle
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR)
from conf import setting
from core import file_handler
from core import db_handler
import select,hashlib
import threading
def login(username,password):
    """
    FTP登陆验证函数
    :param username:
    :param password:
    :return:

    # testDict ={"username":"jjb","password":"123456","file_dir":"E:\python","file_size":500}
    # file = ‘jjb.pkl‘
    # fp = open(file,‘wb‘)
    # pickle.dump(testDict,fp)
    # fp.close()
    f = open("jjb.pkl","rb")
    data = pickle.loads(f.read())
    f.close()
    print(data)
    """
    #实例化加密函数
    hash = hashlib.sha512()
    db= db_handler.handler(setting.DATABASE,username)
    if os.path.isfile(db):
        f = open(db,"rb")
        data = pickle.loads(f.read())
        f.close()
        if username == data["name"]:
            hash.update(bytes(data["password"],"utf8"))
            hash_pwd = hash.hexdigest()
            if hash_pwd == password:
                filedir = data["file_dir"]
                filesize = data["file_size"]
                return "True|%s|%s"%(filedir,filesize)
            else:
                return "False||"
        else:
            return "False||"
    else:
        return "False||"
def process(conn,addr):
    flage = "False"
    # 接收客户端连接请求信息
    info = conn.recv(1000)
    if info.decode() == "connect":
        conn.send(bytes("login","utf8"))
    # 接收用户及密码信息
    while flage =="False":
        user_check =conn.recv(8000)
        # 分割用户名及密码
        username,password = str(user_check.decode()).split("|")
        # 调用登陆验证函数
        login_ack = login(username,password)
        flage,home,size = str(login_ack).split("|")
        # print(flage,home,size)
        # print("user_input:",username,"user_pass:",password)
        if flage =="True":
            # 登陆成功发送登陆确认信息给客户端
            conn.send(bytes("login_ack","utf8"))
            # 实例化FTPserver
            ftp = file_handler.FTPs(username,conn,home,size) # 登陆用户,数据连接,工作目录,磁盘配额
            ftp.run()
            break
        else:
            # 登陆失败,发送给客户端重新验证
            conn.send(bytes("登陆失败!","utf8"))

def ftp_server():
    ‘‘‘
    启动FTP服务器端,开启线程监听
    :return:
    ‘‘‘
    server = socket.socket()
    server.bind((setting.IP_PORT["host"],setting.IP_PORT["port"]))
    server.listen(10)
    while True:
        r,w,e = select.select([server,], [], [], 1)
        for i,server in enumerate(r):
            conn,addr = server.accept()
            # 创建线程
            t = threading.Thread(target=process, args=(conn, addr))
            # 启动线程
            t.start()
    server.close()
def run():
    ftp_server()

if __name__ =="__main__":
    run()

  file_handler.py

#!/usr/bin/env python3.5
# -*-coding:utf8-*-
import os,sys
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR)
import re
from core import db_handler
from conf import setting
import pickle
class FTPs(object):
    ‘‘‘
    ftp操作命令方法:
    ‘‘‘
    def __init__(self,username,conn,home,total_size):
        ‘‘‘
        初始化参数
        :param username: 操作用户名
        :param conn: sock连接
        :param home: 用户根目录
        :param total_size: 磁盘配额
        :return:
        ‘‘‘
        self.username = username
        self.conn = conn
        self.root = home
        self.home = self.root
        self.total_size = int(total_size)
        self.cmd_file = None  # 文件指令
        self.psize = 4096 # 文件分片
    def getdirsize(self,space):
        ‘‘‘
        计算磁盘空间大小
        :return:
        ‘‘‘
        self.dirsize = 0
        for root,dirs,files in os.walk(space):
            self.dirsize += (sum([os.path.getsize(os.path.join(root,name))for name in files])/1024)
        return int(self.dirsize)
    def put(self):
        ‘‘‘
        上传文件
        :return:
        ‘‘‘
        if self.cmd_file:
            self.user_space = int(self.getdirsize(self.root)/1024)
            # 组合接收字符串
            self.file_root = ‘%s\\%s‘% (self.home,self.cmd_file)
            # # 获取文件名
            self.f =os.path.basename(self.file_root)
            if os.path.isdir(self.home):
                os.chdir(self.home)
            else:
                os.makedirs(self.home)
                os.chdir(self.home)
            try:
                self.conn.send(bytes("f_ack","utf8"))
                self.size = str(self.conn.recv(1024).decode()).split("|")
                if self.size[0]== "fsize":
                    self.fss = int(self.size[1])
                    self.f_total_size = int(self.user_space + (self.fss/1024/1024))
                    if self.f_total_size < self.total_size:  # 判断空间是否超额
                        self.conn.send(bytes("f_ack_ready","utf8"))
                        self.bsize = 0
                        print("需要上传文件大小:",self.fss)
                        # 打开文件
                        f=open(self.f,‘wb‘)
                        while self.bsize < self.fss:
                            data = self.conn.recv(self.psize)
                            self.bsize += len(data)
                            f.write(data)
                        self.conn.send(bytes("ok","utf8"))
                        print("实际已上传文件大小:",self.bsize)
                    else:
                        self.conn.send(bytes("上传空间不足!无法上传,你当前磁盘配额为%sM"%self.total_size,"utf8"))

            except Exception as ex:
                self.conn.send(bytes(ex,"utf8"))
        else:
            self.conn.send(bytes("请上传文件,文件不能为空","utf8"))
    def get(self):
        ‘‘‘
        下载文件
        :return:
        ‘‘‘
        if self.cmd_file:
            os.chdir(self.home) # 进入用户根目录
            self.file = os.getcwd()+"\\"+ self.cmd_file
            if os.path.isfile(self.file):
                f = open(self.file, ‘rb‘)
                self.fsize = os.path.getsize(self.file) # 获取要发送文件的大小
                self.conn.send(bytes("f_ack_read","utf8"))
                self.conn.recv(1000)
                print("需发送文件大小:",self.fsize)
                self.conn.send(bytes("fsize|%s"%self.fsize,"utf8")) # 发送文件大小及要发送准备完毕指令
                if self.conn.recv(1000).decode() == "f_ack":  # 接收对方是否准备就绪
                    self.fsize = int(self.fsize)
                    self.size = 0
                    ack =""
                    while self.size < self.fsize and ack !="ok":
                        data = f.read(self.fsize)  # 一次读取分片大小4096
                        self.conn.send(data)
                        self.size += len(data)
                    print("实际发送文件大小:",self.size)
                    ack = self.conn.recv(1000).decode() # 接收客户端是否下载完指令
                    self.conn.send(bytes("成功","utf8"))
                else:
                    self.conn.send(bytes("接收失败","utf8"))
            else:
                self.conn.send(bytes("文件不存在","utf8"))
        else:
            self.conn.send(bytes("请输入文件名","utf8"))
    def dir(self):
        ‘‘‘
        查看文件
        :return:
        ‘‘‘
        self.current_space =int(self.getdirsize(self.home))
        # 文件列表
        self.li = ""
        # 目录列表
        self.dl = ""
        try:
            os.chdir(self.home)
        except:
            os.makedirs(self.home)
            os.chdir(self.home)
        try:
            if os.listdir(os.getcwd()):
                for self.i in os.listdir(os.getcwd()):
                    self.file = os.getcwd()+‘\\‘+self.i
                    if os.path.isfile(self.file):
                        # 获取文件大小
                        self.fsize = int(os.path.getsize(self.file)/1024)
                        if self.fsize < 1:
                            self.fsize = 4
                        else:
                            self.fsize +=4
                        self.li += ‘%s -rw-rw-rw- 占用大小:%skb\r\n‘% (self.i,self.fsize)
                    else:
                        self.dl += ‘%s\r\n‘%self.i
                self.conn.send(bytes("目录:\r\n\r\n%s 文件:\r\n%s\r\n \r\n当前目录空间大小:%skb"%(self.dl,self.li,self.current_space),"utf8"))
            else:
                self.conn.send(bytes("当前目录为:%s"%(self.home),"utf8"))
        except Exception as ex:
            self.conn.send(bytes(ex,"utf8"))
    def cd(self):
        ‘‘‘
        进入目录
        :return:
        ‘‘‘

        if self.cmd_file:
            os.chdir(self.home)  # 先进入到工作目录
            self.dir_change = os.path.abspath(os.path.join(self.home,"%s\%s"%(self.home,self.cmd_file)))
            if self.root in self.dir_change:
                try:
                    os.chdir(self.dir_change)
                    self.home = self.dir_change
                    self.conn.send(bytes("当前工作目录为:%s"%self.home,"utf8"))
                except:
                    os.makedirs(self.dir_change)
                    os.chdir(self.dir_change)
                    self.home = self.dir_change
                    self.conn.send(bytes("当前工作目录为:%s"%self.home,"utf8"))
            else:
                    self.conn.send(bytes("当前工作目录为:%s    更改失败!"%self.home,"utf8"))
        else:
            os.chdir(self.home)
            self.conn.send(bytes("当前工作目录为:%s"%self.home,"utf8"))
    def mkd(self):
        ‘‘‘
        创建目录
        :return:
        ‘‘‘
        if self.cmd_file:
            try:
                os.makedirs(self.cmd_file)
                self.conn.send(bytes("创建目录成功!","utf8"))
            except Exception as ex:
                self.conn.send(bytes("创建目录失败!原因:%s"%ex,"utf8"))
        else:
            self.conn.send(bytes("请输入文件夹名!","utf8"))
    def delete(self):
        ‘‘‘
        删除文件
        :return:
        ‘‘‘
        os.chdir(self.home) # 进入用户根目录
        try:
            self.file = self.home+‘\\‘+ self.cmd_file
            if os.path.isfile(self.file):
                os.remove(self.cmd_file)
                self.conn.send(bytes("文件:%s删除成功!"%self.cmd_file,"utf8"))
            else:
                os.removedirs(self.cmd_file)
                self.conn.send(bytes("目录删除成功!","utf8"))
                os.chdir(self.root)
        except Exception:
            if os.path.isdir(self.root):
                self.conn.send(bytes("删除失败!","utf8"))
            else:
                os.makedirs(self.root)
                self.conn.send(bytes("删除失败!","utf8"))

    def help(self):
        ‘‘‘
        FTP帮助信息
        :return:
        ‘‘‘
        self.conn.send(bytes("""
        FTP服务器操作方法有: put------>上传文件至服务器
                             get------>从服务器上下载文件
                             dir------>查看服务器文件列表
                             cd------->进入指定文件夹
                             delete--->删除文件
                             mkd ----->创建目录
                             help----->帮助信息
                             q ------->退出

        ""","utf8"))
    def run(self):

        while True:
            # try:
             # # 接收客户端发来的命令信息
            self.cmd = self.conn.recv(1000)
            self.cmd_action = str(self.cmd.decode())
            # 判断命令是否含有空格
            self.fg = re.search("\s","%s"%self.cmd_action)
            if self.fg:
                self.cmd_action,self.cmd_file = str(self.cmd_action).split(" ")
            else:
                self.cmd_file =None
            # print("cmd_action:",self.cmd_action,"cmd_file:",self.cmd_file)
            if hasattr(FTPs,self.cmd_action):
                func = getattr(self,self.cmd_action)
                func()
                continue
            else:
                self.conn.send(b‘command is not found!‘)
                continue
            # except Exception as ex:
            #     print("系统异常:%s"%ex)

  客户端

  client.py

#!/usr/bin/env python3.5
# -*-coding:utf8-*-
import sys,os,re
import socket,hashlib
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR)
from core import file_handler
from conf import setting
def login():
    hash = hashlib.sha512()
    while True:
        user_input = input("请输入用户名:").strip()
        pass_input = input("请输入密码:").strip()
        if len(user_input) !=0 and len(pass_input) != 0:
            hash.update(bytes(pass_input,"utf8"))
            sha_pwd = hash.hexdigest()
            user = "%s|%s"% (user_input,sha_pwd)
            return user
            break
def ftp_client():
    sk = socket.socket()
    sk.connect((setting.IP_PORT["host"],setting.IP_PORT["port"]))
    while True:
        flage = False
        sk.send(bytes("connect","utf8"))
        msg = sk.recv(100)
        print("欢迎访问FTP服务器,请根据提示进行操作")
        if msg.decode() == "login":
            while flage == False:
                login_user =login()
                username,password = str(login_user).split("|")
                sk.send(bytes(login_user,"utf8"))
                user_info = sk.recv(1000)
                if user_info.decode() == "login_ack":
                    print("登陆成功!")
                    flage = True
                    break
                print(user_info.decode())
            while flage:
                cmd_action = input("请输入操作命令如:get fy.py or help :").strip()
                if len(cmd_action) == 0:continue
                if cmd_action == "q":
                    sys.exit()
                # 判断命令是否含有空格
                fg = re.search("\s","%s"%cmd_action)
                if fg:
                    cmd,cmd_file = str(cmd_action).split(" ")
                    ftp = file_handler.ftpc(sk,username,cmd_action,setting.DATABASE["local"])
                    if hasattr(ftp,cmd):
                        func = getattr(ftp,cmd)
                        func()
                        continue
                else:
                    cmd_file =None
                sk.send(bytes(cmd_action,"utf8"))
                rec_msg = sk.recv(8000)
                print(rec_msg.decode())
            if flage == "False":
                sk.send(bytes("connect","utf8"))
    sk.close()
def run():
    ftp_client()
if __name__ == "__main__":
    run()

  file_handler.py

#!/usr/bin/env python3.5
# -*-coding:utf8-*-
import sys,os,re
import socket
BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(BASEDIR)
class ftpc(object):
    def __init__(self,sk,username,cmd_action,home):
        self.sk = sk
        self.username = username
        self.cmd_action = cmd_action
        self.home = home
    def put(self):
        ‘‘‘
        上传文件

        :return:
        ‘‘‘
        try:
            os.chdir(self.home)
        except:
            os.makedirs(self.home)
            os.chdir(self.home)
        # 判断命令是否含有空格
        fg = re.search("\s","%s"%self.cmd_action)
        if fg:
            self.cmd,self.cmd_file = str(self.cmd_action).split(" ")
            if os.path.isfile(os.getcwd()+"\\"+self.cmd_file):
                self.sk.send(bytes(self.cmd_action,"utf8"))  # 发送动作命令
                rec_msg = self.sk.recv(8000)
                if rec_msg.decode() == "f_ack":
                    f = open(self.cmd_file, ‘rb‘)
                    self.fsize = os.path.getsize(self.cmd_file) # 获取要发送文件的大小
                    self.sk.send(bytes("fsize|%s"%self.fsize,"utf8")) # 发送文件大小
                    self.ack = self.sk.recv(1000)
                    if self.ack.decode() =="f_ack_ready":
                        self.fsize = int(self.fsize)
                        self.size = 0
                        ack =""
                        while self.size < self.fsize and ack !="ok":
                            data = f.read(4095)  # 一次读取分片大小4095
                            self.sk.send(data)
                            self.size += len(data)
                            count = int(self.size/self.fsize*100)
                            print(‘#‘*count,"->",(count),"%")
                        ack = self.sk.recv(1000).decode()
                        if ack =="ok":
                            print("上传成功")
                        else:
                            print("上传失败")
                    else:
                        print(self.ack.decode())
                else:
                    print("上传文件失败:%s"%rec_msg.decode())
            else:
                print("上传文件失败,请输入正确的文件名!")
        else:
            print("上传文件失败,请输入正确的文件名!")

    def get(self):
        ‘‘‘
        下载文件
        :return:
        ‘‘‘
        try:
            os.chdir(self.home)
        except:
            os.makedirs(self.home)
            os.chdir(self.home)
        # 判断命令是否含有空格
        fg = re.search("\s","%s"%self.cmd_action)
        if fg:
            self.cmd,self.cmd_file = str(self.cmd_action).split(" ")
        else:
            self.cmd_file =None
        self.sk.send(bytes(self.cmd_action,"utf8"))
        rec_msg = self.sk.recv(8000)
        if rec_msg.decode() == "f_ack_read":
            self.rec = self.sk.send(bytes("ok","utf8"))
            self.rec_size = self.sk.recv(2048)
            self.ack_rec= str(self.rec_size.decode()).split("|")
            self.sk.send(bytes("f_ack","utf8"))
            self.ack_s =int(self.ack_rec[1])
            print(self.ack_s)
            self.re_s = 0
            f = open(self.cmd_file,"wb")

            while self.re_s < self.ack_s:
                xx = self.re_s/self.ack_s*100
                data = self.sk.recv(4096)
                self.re_s += len(data)
                # print(data.decode("gbk"))
                f.write(data)
                count = int(xx)
                print(‘#‘*count,"->",(count+1),"%")
            self.sk.send(bytes("ok","utf8"))
            print(self.re_s)
            self.ack_ok = self.sk.recv(1024)
            print("接收文件:%s"%self.ack_ok.decode())
        else:
            print("接收文件失败:%s"%rec_msg.decode())

如下是重要模块进行收藏:

  OS模块

os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
os.curdir  返回当前目录: (‘.‘)
os.pardir  获取当前目录的父目录字符串名:(‘..‘)
os.makedirs(‘dirname1/dirname2‘)    可生成多层递归目录
os.removedirs(‘dirname1‘)    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir(‘dirname‘)    生成单级目录;相当于shell中mkdir dirname
os.rmdir(‘dirname‘)    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir(‘dirname‘)    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印
os.remove()  删除一个文件
os.rename("oldname","newname")  重命名文件/目录
os.stat(‘path/filename‘)  获取文件/目录信息
os.sep    输出操作系统特定的路径分隔符,win下为"\\",Linux下为"/"
os.linesep    输出当前平台使用的行终止符,win下为"\t\n",Linux下为"\n"
os.pathsep    输出用于分割文件路径的字符串
os.name    输出字符串指示当前使用平台。win->‘nt‘; Linux->‘posix‘
os.system("bash command")  运行shell命令,直接显示
os.environ  获取系统环境变量
os.path.abspath(path)  返回path规范化的绝对路径
os.path.split(path)  将path分割成目录和文件名二元组返回
os.path.dirname(path)  返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path)  返回path最后的文件名。如何path以/或\结尾,那么就会返回空值。即os.path.split(path)的第二个元素
os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path)  如果path是绝对路径,返回True
os.path.isfile(path)  如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path)  如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path)  返回path所指向的文件或者目录的最后存取时间
os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间

  sys模块

sys.argv           命令行参数List,第一个元素是程序本身路径
sys.exit(n)        退出程序,正常退出时exit(0)
sys.version        获取Python解释程序的版本信息
sys.maxint         最大的Int值
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称
sys.stdout.write(‘please:‘)
val = sys.stdin.readline()[:-1]

  re 模块

  

匹配格式

模式    描述
^    匹配字符串的开头
$    匹配字符串的末尾。
.    匹配任意字符,除了换行符,当re.DOTALL标记被指定时,则可以匹配包括换行符的任意字符。
[...]    用来表示一组字符,单独列出:[amk] 匹配 ‘a‘,‘m‘或‘k‘
[^...]    不在[]中的字符:[^abc] 匹配除了a,b,c之外的字符。
re*    匹配0个或多个的表达式。
re+    匹配1个或多个的表达式。
re?    匹配0个或1个由前面的正则表达式定义的片段,非贪婪方式
re{ n}
re{ n,}    精确匹配n个前面表达式。
re{ n, m}    匹配 n 到 m 次由前面的正则表达式定义的片段,贪婪方式
a| b    匹配a或b
(re)    G匹配括号内的表达式,也表示一个组
(?imx)    正则表达式包含三种可选标志:i, m, 或 x 。只影响括号中的区域。
(?-imx)    正则表达式关闭 i, m, 或 x 可选标志。只影响括号中的区域。
(?: re)    类似 (...), 但是不表示一个组
(?imx: re)    在括号中使用i, m, 或 x 可选标志
(?-imx: re)    在括号中不使用i, m, 或 x 可选标志
(?#...)    注释.
(?= re)    前向肯定界定符。如果所含正则表达式,以 ... 表示,在当前位置成功匹配时成功,否则失败。但一旦所含表达式已经尝试,匹配引擎根本没有提高;模式的剩余部分还要尝试界定符的右边。
(?! re)    前向否定界定符。与肯定界定符相反;当所含表达式不能在字符串当前位置匹配时成功
(?> re)    匹配的独立模式,省去回溯。
\w    匹配字母数字
\W    匹配非字母数字
\s    匹配任意空白字符,等价于 [\t\n\r\f].
\S    匹配任意非空字符
\d    匹配任意数字,等价于 [0-9].
\D    匹配任意非数字
\A    匹配字符串开始
\Z    匹配字符串结束,如果是存在换行,只匹配到换行前的结束字符串。c
\z    匹配字符串结束
\G    匹配最后匹配完成的位置。
\b    匹配一个单词边界,也就是指单词和空格间的位置。例如, ‘er\b‘ 可以匹配"never" 中的 ‘er‘,但不能匹配 "verb" 中的 ‘er‘。
\B    匹配非单词边界。‘er\B‘ 能匹配 "verb" 中的 ‘er‘,但不能匹配 "never" 中的 ‘er‘。
\n, \t, 等.    匹配一个换行符。匹配一个制表符。等
\1...\9    匹配第n个分组的子表达式。
\10    匹配第n个分组的子表达式,如果它经匹配。否则指的是八进制字符码的表达式。
  

正则表达式常用5种操作

re.match(pattern, string)     # 从头匹配

re.search(pattern, string)    # 匹配整个字符串,直到找到一个匹配

re.split()            # 将匹配到的格式当做分割点对字符串分割成列表

1
2
>>>m = re.split("[0-9]", "alex1rain2jack3helen rachel8")
>>>print(m)
输出: [‘alex‘, ‘rain‘, ‘jack‘, ‘helen rachel‘, ‘‘]

re.findall()          # 找到所有要匹配的字符并返回列表格式

1
2
>>>m = re.findall("[0-9]", "alex1rain2jack3helen rachel8")
>>>print(m)<br>
输出:[‘1‘, ‘2‘, ‘3‘, ‘8‘]

re.sub(pattern, repl, string, count,flag)    # 替换匹配到的字符

1
2
m=re.sub("[0-9]","|", "alex1rain2jack3helen rachel8",count=2 )
print(m)
输出:alex|rain|jack3helen rachel8  

正则表达式实例
字符匹配
实例    描述
python    匹配 "python".
字符类
实例    描述
[Pp]ython    匹配 "Python" 或 "python"
rub[ye]    匹配 "ruby" 或 "rube"
[aeiou]    匹配中括号内的任意一个字母
[0-9]    匹配任何数字。类似于 [0123456789]
[a-z]    匹配任何小写字母
[A-Z]    匹配任何大写字母
[a-zA-Z0-9]    匹配任何字母及数字
[^aeiou]    除了aeiou字母以外的所有字符
[^0-9]    匹配除了数字外的字符
特殊字符类
实例    描述
.    匹配除 "\n" 之外的任何单个字符。要匹配包括 ‘\n‘ 在内的任何字符,请使用象 ‘[.\n]‘ 的模式。
\d    匹配一个数字字符。等价于 [0-9]。
\D    匹配一个非数字字符。等价于 [^0-9]。
\s    匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S    匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。
\w    匹配包括下划线的任何单词字符。等价于‘[A-Za-z0-9_]‘。
\W    匹配任何非单词字符。等价于 ‘[^A-Za-z0-9_]‘。

re.match与re.search的区别
re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。

时间: 2024-08-08 21:48:45

基于多线程多用户的FTP服务器与客户端功能实现的相关文章

基于asp.net MVC 的服务器和客户端的交互(二)之获取Oauth 2.0认证权限

基本Web API的ASP.NET的Oauth2认证 增加Token额外字段 增加Scope授权字段 持久化Token 设计Token的时间间隔 刷新Token后失效老的Token 自定义验证[重启IIS池Token失效,验证权限] Oauth2 认证的流程 客户端发送口令(grant_type,client_id,client_secret)到服务端请求,认证返回access token ,然后客户端跟据获得的access token,根据Access Token获得权限去访问Web API.

基于asp.net MVC 的服务器和客户端的交互(三)之客户端请求响应

一.分析 WEB API 中HTTP 请求方式的四个主要方法 (GET, PUT, POST, DELETE), 按照下列方式映射为 CURD 操作: GET 用于获取 URI 资源的进行展示,GET 操作不应对服务端有任何影响,用于数据集合的Select查询: PUT 用于更新 URI 上的一个资源,如果服务端允许,PUT 也可以用于新建一个资源,用于相关的Insert插入操作: POST 用于新建资源,服务端在指定的URI 上创建一个新的对象,将新资源的地址作为响应消息的一部分返回,用于相关

FTP服务器使用客户端可以正常下载,但是在java代码中,无法读取到文件,用的是org.apache.commons.net.ftp

http://book.zhulang.com/302338/99012.html http://book.zhulang.com/302338/99013.html http://book.zhulang.com/302338/99014.html http://book.zhulang.com/302338/99015.html http://book.zhulang.com/302338/99016.html http://book.zhulang.com/302338/99017.htm

linux学习笔记之ftp服务器

基于TCP C/S 数据连接20号端口 控制连接21号端口 数据  主动/被动连接 bbs.lijinya.com  80  192.169.10.2 www.lijinya.com  80  192.168.10.2 ip一样 基于IP的虚拟ftp服务器 一个端口对应一个服务 本地用户登陆.... 关闭防火墙 username   passwd    登陆入加目录 ftp  anoymous  匿名用户  不用密码 匿名用户 虚拟用户 user_list zhangsan 123456 /va

Centos7搭建FTP服务器

1.简介 FTP 是File Transfer Protocol(文件传输协议)的英文简称,而中文简称为"文传协议".用于Internet上的控制文件的双向传输.同时,它也是一个应用程序.基于不同的操作系统有不同的FTP应用程序,而所有这些应用程序都遵守同一种协议以传输文件.一般用于共享文件,主要是上传文件到ftp服务器或者下载ftp服务器上的文件 2.部署环境 操作系统:CentOS Linux release 7.3.1611 (Core) IP:10.0.1.16 3.部署 3.

Centos 7使用vsftpd搭建FTP服务器

FTP,即:文件传输协议(File Transfer Protocol),基于客户端/服务器模式,默认使用20.21端口号,其中端口20(数据端口)用于进行数据传输,端口21(命令端口)用于接受客户端发出的相关FTP命令与参数.FTP服务器普遍部署于局域网中,具有容易搭建.方便管理的特点.而且有些FTP客户端工具还可以支持文件的多点下载以及断点续传技术,因此FTP服务得到了广大用户的青睐.FTP协议有以下两种工作模式: 主动模式(PORT):FTP服务器主动向客户端发起连接请求. 被动模式(PA

FTP服务器原理

21.1 FTP服务器原理   使用明码传输方式,且有相当多的安全危机历史.因此一般使用功能较少的vsftpd这个软件. 21.1.1 FTP功能简介 有以下功能 文档传输与管理 不同等级的用户身份:user,guest,anonymous 命令记录与登录文件记录 限制用户活动的目录:(change root,简称 chroot) 即将登录用户的家目录变成根目录 21.1.2 FTP的运作流程与使用用的端口号 FTP的传输使用的是TCP封包协议. FTP服务器使用了两个联机,分别是: 命令信道

每日一记:文件服务器之FTP服务器

FTP(File transfer protocol):最主要的功能是在服务器与客户端之间进行档案的传输,使用的是明码传输方式 FTP服务器软件提供的不同等级的用户身份:user.guest.anonymous三种身份的权限和功能有差异FTP可以利用系统的syslogd来进行数据的记录,记录的数据包括用户曾经下达过的命令与用户传输数据的记录FTP可以限制用户仅能在自己的家目录当中活动,登入FTP后,显示的 [根目录]就是自己家目录的内容,这种环境称为change root,简称chrootFTP

FTP-Linux中ftp服务器搭建

一.FTP工作原理 (1)FTP使用端口 [[email protected] ~]# cat /etc/services | grep ftp ftp-data 20/tcp #数据链路:端口20 ftp 21/tcp #控制链路:端口21 (2)FTP连接方式     支持两种连接模式:主动模式(Port)和被动模式(Pasv),这两种模式都是针对数据链路进行的,与控制链路无关. 主动模式工作过程: 1.客户端从自己的一个任意端口(N > 1024)和FTP服务器的21端口建立控制链路2.然