[python全栈]06.多线程(4)

目录

  1. 进程与线程总结
  2. 服务器模型
  3. 基础服务端模型
  4. FTP文件服务器

1. 进程 vs 线程

进程和线程的区别和联系 :
	1. 两者都是多任务编程方式,都能够使用计算机的多核资源
	2. 进程的创建删除消耗的计算机资源比线程要多
	3. 进程空间相互独立,数据互不干扰,有专门的IPC,线程使用全局变量进行通信
	4. 一个进程可以创建多个线程分支,两者之间存在包含关系
	5. 多个线程公用进程的资源,在资源操作时往往需要同步互斥
	6. 进程线程在系统中都有自己特有的属性,ID,代码段,栈区等资源

使用场景 :
	1. 需要创建较多并发,同时任务关联性比较强时,一般用多线程
	2. 不同的任务模块可能更多使用进程
	3. 使用进程线程需要考虑数据的处理复杂度
			比如进程间通信是否方便,同步互斥是否过于复杂
***进程和线程的区别和联系
***进程间通信方式有哪些?有什么特点
***同步互斥的意义是什么?什么情况下用?
***给一个情形,分析下使用进程还是线程?
***常见概念 :僵尸进程 , 进程状态 , GIL

2. 服务器模型

服务器模型 :

硬件服务器 : 主机	集群
	厂商 : IBM	HP	联想	浪潮
-------------------------------------	
软件服务器 :
	编写的服务端应用程序,在硬件服务器上运行
	一般依托于操作系统,给用户提供一套完整的服务
常见 : 
	httpserver ---> 处理 http 请求
	webserver ---> 网站的后端应用服务器程序
	邮箱服务器 ---> 邮件处理
	ftp文件服务器 ---> 文件的上传下载
功能 :
	网络连接 ; 逻辑处理 ; 数据交互 ; 数据传输 ; 协议的实现
结构 : 
	c/s	客户端服务器模型
	b/s	浏览器服务器模型
服务器目标 :处理速度更快,并发量更高,安全性更强
硬件 : 更高的配置,更好的集成分布技术,更好的网络优化和网络安全技术
软件: 占有资源更少,运行更稳定,算法更优良,安全性更好
		并发性更高,更容易扩展

3. 基础服务端模型

循环模型 :
		循环接收客户端请求,处理请求
		同一时刻只能处理一个请求,处理完毕后再处理下一个
		优点 : 实现简单,占用资源少
		缺点 : 无法同时处理多个客户端任务
		适用情况 : 处理的任务可以短时间完成,不需要建立并发,更适合UDP适用
		
并发模型 : 能够同时处理多个客户端请求
	IO 并发 : 
		IO多路复用 : 监听多个IO事件
			优点 : 资源消耗少,IO处理速度快
			缺点 :不能适用CPU密集型程序
		
	多进程/多线程并发 :
		为每个客户端创建单独的进程线程,执行请求
		优点 : 每个客户端可以长期占有服务器运行程序,能够使用多核资源
				可以处理IO或CPU运算
		缺点 : 消耗系统资源高		
多进程并发模型 ;
使用 fork 实现多进程并发 :
1. 创建套接字,绑定,监听
2. 等待接受客户端请求
3. 创建新的进程处理客户端请求
4. 原有进程继续等待接受新的客户端连接
5. 如果客户端退出则关闭子进程
#fork_server.py
#使用 fork 实现多进程并发

from socket import *
import os,sys
import signal

#客户端处理函数
def client_handler(c):
    print("handle child process requestment:",c.getpeername())
    try:
        while True:
            data = c.recv(1024)
            if not data:
                break
            print(data.decode())
            c.send("recv client requestment".encode())
    except (KeyboardInterrupt,SystemError):
        sys.exit("Client Quit")
    except Exception as e :
        print(e)
    c.close()
    sys.exit(0)


#创建套接字,绑定,监听
HOST = ""
PORT = 8888
ADDR = (HOST,PORT)

s = socket(AF_INET,SOCK_STREAM)
s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
s.bind(ADDR)
s.listen(5)

#2. 等待接受客户端请求
print("process:%d :waiting for client connecting..."
    %os.getpid())
#在父进程中忽略子进程状态改变,子进程退出自动由系统处理
signal.signal(signal.SIGCHLD,signal.SIG_IGN)
while True:
    try:
        c,addr = s.accept()
    except KeyboardInterrupt:
        print("Server Quit")
    except Exception as e :
        print("Error:",e)
        continue
    #为客户端创建新的进程,处理请求
    pid = os.fork()
    if pid < 0:
        print("create a new process failed")
        continue
    #子进程处理具体请求
    elif pid == 0:
        s.close()   #不会影响父进程
        client_handler(c)
    else :
        c.close()   #不会影响子进程
        continue
#client(TCP)
#tcp client.py
from socket import *

#创建套接字
sockfd = socket(AF_INET,SOCK_STREAM)

#发起连接

server_addr = ('127.0.0.1',8888)
sockfd.connect(server_addr) 
while True:
#消息发送接收
    data = input('发送>>>')

    sockfd.send(data.encode())  #转换为bytes格式
    if data ==  '##' :
        break
    data = sockfd.recv(1024)
    print("接收到",data.decode())

#关闭套接字
sockfd.close()

4. FTP文件服务器

FTP文件服务器 :
项目功能 :
*服务端和客户端两部分,要求启动一个服务端,可以同时处理多个客户端请求
*功能 : 
	1. 可以查看服务端文件库中所有的普通文件
	2. 从客户端可以下载文件库的文件到本地
	3. 可以将本地文件上传到服务端文件库
	4. 退出
* 客户端使用 print 在终端打印简单的命令提示,通过命令提示发起请求
1. 技术分析(fork tcp 并发)
2. 每个功能要单独封装,整体功能写在一个类中
3. 如何搭建整体架构,完成网络通讯

功能分析 :
1. 获取文件列表
	客户端 : 
			发送请求
			得到回复判断能否获取列表
			接受文件名称列表
	服务端 : 
			接受请求
			判断请求类型
			判断能否满足请求,回复信息确认
			执行请求,发送文件列表
2.文件下载
	客户端 : 
		发送请求(文件名)
		得到回复判断能否下载
		下载文件
	服务端 :
		接受请求
		判断请求类型
		判断能否满足请求,回复信息确认
		执行请求发送文件
结构 :
.
├── database	(文件库)
│   ├── test	#文件夹
|	├── .test	#隐藏文件
│   ├── test1.txt
│   ├── test2.py
│   ├── test3.txt
│   ├── test4.py
│   └── testjpg.jpg
├── ftp_client.py
└── ftp_server.py

python 查看文件夹内文件
os.listdir('path')
查看路径下的文件列表,返回值 ['','',...]
os.path.isfile('path')
判断是否为普通文件--->True ; 文件夹--->False
os.path.isdir('dir')
判断是否为文件夹
隐藏文件 : 文件名首为 '.'
FTP 文件服务器
'''
    FTP 文件服务器
'''
from socket import *
import os
import sys
import time
import signal

#定义全局变量
FILE_PATH = "./database/"
HOST = "0.0.0.0"
PORT = 8888
ADDR = (HOST,PORT)

#将文件服务器功能写在类中
class FtpServer(object):
    def __init__(self,connfd):
        self.connfd = connfd

    def do_list(self):
        #获取文件列表
        file_list = os.listdir(FILE_PATH)
        if not file_list:
            self.connfd.send("文件库为空".encode()) 
            return
        else:
            self.connfd.send(b"OK")
            time.sleep(0.1)
        files = ''
        for file in file_list:
            if file[0] != '.' and os.path.isfile(FILE_PATH + file):
                files = files + file + '#'
        self.connfd.sendall(files.encode())

    def do_get(self,filename):
        try:
            fd = open(FILE_PATH + filename,'rb')
        except:
            self.connfd.send("文件不存在".encode())
            return
        self.connfd.send(b"OK")
        time.sleep(0.1)
        while True:
            data = fd.read(1024)
            if not data:
                time.sleep(0.1)
                self.connfd.send("##".encode())
                break
            self.connfd.send(data)
        print("文件发送完毕")
        fd.close()

    def do_put(self,filename):
        try:
            fd = open(FILE_PATH +'(上传)'+filename,'wb')
        except Exception as e:
            self.connfd.send(e.encode())
            return
        self.connfd.send(b"OK")
        time.sleep(0.1)
        while True:
            data = self.connfd.recv(1024)
            if data == b"##":
                break
            fd.write(data)
        fd.close()
        print("%s上传完毕\n"%('(上传)'+filename))


#主流程
def main():
    sockfd = socket(AF_INET,SOCK_STREAM)
    sockfd.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    sockfd.bind(ADDR)
    sockfd.listen(5)

    #处理子进程退出
    signal.signal(signal.SIGCHLD,signal.SIG_IGN)
    print("Listen the port 8888....")
    while True:
        try:
            connfd,addr = sockfd.accept()
        except KeyboardInterrupt:
            sockfd.close()
            sys.exit("服务器退出")
        except Exception as e:
            print("服务器异常:",e)
            continue
        print("已连接客户端:",addr)

        #创建子进程
        pid = os.fork()
        if pid == 0:
            sockfd.close()
            ftp = FtpServer(connfd)
            #判断客户端请求
            while True:
                data = connfd.recv(1024).decode()
                if not data or data[0] == "Q":
                    connfd.close()
                    sys.exit("客户端退出")
                elif data[0] == "L":
                    ftp.do_list()
                elif data[0] == "G":
                    filename = data.split(" ")[-1]
                    ftp.do_get(filename)
                elif data[0] == "P":
                    filename = data.split(" ")[-1]
                    print("file",filename,"loading...")
                    ftp.do_put(filename)

        else:
            connfd.close()
            continue


if __name__ == "__main__":
    main()
FTP 客户端
'''
    FTP 客户端
'''
from socket import *
import sys
import time

FILE_PATH = "./database/"

#基本文件操作功能:
class FtpClient(object):
    def __init__(self,sockfd):
        self.sockfd = sockfd

    def do_list(self):
        #发送请求
        self.sockfd.send(b"L")
        #等待回复
        data = self.sockfd.recv(1024).decode()
        if data == "OK":
            data = self.sockfd.recv(4096).decode()
            files = data.split("#")
            for file in files:
                print(file)
            print("文件列表展示完毕\n")
        else:
            #由服务器发送失败原因
            print(data)

    def do_get(self,filename):
        #发送请求
        self.sockfd.send(('G '+filename).encode())
        #等待回复
        data = self.sockfd.recv(1024).decode()
        if data == 'OK':
            fd = open("(copy)"+filename,'wb')
            while True:
                data = self.sockfd.recv(1024)
                if data == b"##":
                    break
                fd.write(data)
            fd.close()
            print("%s 下载完毕\n"%filename)
        else:
            print(data)

    def do_put(self,filename):
        #发送请求
        self.sockfd.send(("P "+filename).encode())
        #等待回复
        data = self.sockfd.recv(1024).decode()
        if data == "OK":
            try :
                fd = open(FILE_PATH + filename,'rb')
            except Exception as e :
                print("open failed :",e)
                return
            while True:
                data = fd.read(1024)
                if not data:
                    time.sleep(0.1)
                    self.sockfd.send("##".encode())
                    break
                self.sockfd.send(data)
            print("文件上传完毕")
            fd.close()
            return

    def do_quit(self):
        self.sockfd.send(b'Q')

#网络连接
def main():
    if len(sys.argv) < 3: #从命令行输入服务器IP和端口
        print("argv is Error")
        return
    HOST = sys.argv[1]
    PORT = int(sys.argv[2])
    ADDR = (HOST,PORT)  #文件服务器地址

    sockfd = socket(AF_INET,SOCK_STREAM)
    try:
        sockfd.connect(ADDR)
    except Exception as e:
        print("Connect Failed,ERROR:",e)
        return

    ftp = FtpClient(sockfd) #生成功能类对象

    while True:
        print("========命令选项========")
        print("****** list     ******")
        print("****** get file ******")
        print("****** put file ******")
        print("****** quit     ******")
        print("======================")

        cmd = input("请输入命令>>> ")
        if cmd.strip() == 'list':
            ftp.do_list()
        elif cmd[:3] == 'get':
            filename = cmd.split(" ")[-1]
            ftp.do_get(filename)
        elif cmd.strip() == "quit":
            ftp.do_quit()
            sockfd.close()
            sys.exit("已退出,谢谢使用")
        elif cmd[:3] == "put":
            filename = input("请输入您要上传的文件名>>>")
            ftp.do_put(filename)
        else:
            print("请输入正确命令")

if __name__ == "__main__":
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值