目录
- 进程与线程总结
- 服务器模型
- 基础服务端模型
- 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()