socketserver模块、操作系统、操作系统的发展史
【一】socketserver模块
【1】简介
-
socketserver中包含了两种类,
-
- 一种为服务类(server class):前者提供了许多方法
-
-
- 像绑定,监听,运行…… (也就是建立连接的过程) 。
-
-
- 一种为请求处理类(request handle class)
-
-
- 专注于如何处理用户所发送的数据(也就是事务逻辑)。
-
-
一般情况下,所有的服务,都是先建立连接,也就是建立一个服务类的实例,然后开始处理用户请求,也就是建立一个请求处理类的实例。
【2】socketserver模块中的类
- BaseServer:实现服务器的基本类。
- TCPServer:处理 TCP 连接的服务器。
- UDPServer:处理 UDP 连接的服务器。
- UnixStreamServer:类似于TCPServer提供面向数据流的套接字连接,但是旨在UNIX平台上可用。
- UnixDatagramServer:类似于UDPServer提供面向数据报的套接字连接,但是旨在UNIX平台上可用。
- ForkingMixIn:实现了核心的进程化功能,用于与服务器类进行混合,提供异步特性。
- ThreadingMixIn:实现了核心的线程化功能,用于与服务器类进行混合,异步特性。
- ForkingTCPServer:每个请求创建一个新进程的 TCP 服务器。ForkingMixIn和TCPServer`组合。
- ForkingUDPServer:每个请求创建一个新进程的 UDP 服务器。ForkingMixIn和UDPServer组合。
- ThreadingTCPServer:在单独的线程中处理每个请求的 TCP 服务器。ThreadingMixIn和TCPServer组合。
- ThreadingUDPServer:在单独的线程中处理每个请求的 UDP 服务器。ThreadingMixIn和UDPServer组合。
- BaseRequestHandler:用于定制Handler类型,自定义的Handler类型只要继承自BaseRequestHandler,并覆盖写入它的handle() 方法即可。
- StreamRequestHandler:TCP请求处理类的一个实现。
- DataStreamRequestHandler:UDP请求处理类的一个实现。
import socket
import struct
import json
import os
# 定义处理器类
class MYTCPClient:
# 定义套接字家族
address_family = socket.AF_INET
# 定义协议类型
socket_type = socket.SOCK_STREAM
# 定义拒绝地址(黑名单)
allow_reuse_address = False
# 最大传输包大小
max_packet_size = 8192
# 默认编码格式
coding = 'utf-8'
# 最大请求次数
request_queue_size = 5
def __init__(self, server_address, connect=True):
# 获取服务端地址
self.server_address = server_address
# 创建 socket 对象
self.socket = socket.socket(self.address_family,
self.socket_type)
# 判断当前是否链接
if connect:
try:
self.client_connect()
except:
self.client_close()
raise
# 定义连接方法
def client_connect(self):
# 连接指定服务端
self.socket.connect(self.server_address)
# 关闭连接
def client_close(self):
self.socket.close()
# 启动函数
def run(self):
# 多次循环
while True:
# 输入指令 put 文件路径
inp = input(">>: ").strip()
if not inp: continue
# 切分指令
l = inp.split() # ['put','文件路径']
# 获取cmd命令
cmd = l[0]
# 判断自己当前是否含有该方法
if hasattr(self, cmd):
func = getattr(self, cmd)
func(l)
# 定义上传函数
def put(self, args):
# ['put','文件路径']
cmd = args[0] # 'put'
filename = args[1] # '文件路径'
if not os.path.isfile(filename):
print('file:%s is not exists' % filename)
return
else:
filesize = os.path.getsize(filename)
# 粘包处理 --- 打包
head_dic = {'cmd': cmd, 'filename': os.path.basename(filename), 'filesize': filesize}
print(head_dic)
head_json = json.dumps(head_dic)
head_json_bytes = bytes(head_json, encoding=self.coding)
# 打包
head_struct = struct.pack('i', len(head_json_bytes))
# 传输包长度
self.socket.send(head_struct)
# 传输打包数据
self.socket.send(head_json_bytes)
send_size = 0
# 打开文件,上传文件
with open(filename, 'rb') as f:
for line in f:
self.socket.send(line)
send_size += len(line)
print(send_size)
else:
print('upload successful')
client = MYTCPClient(('127.0.0.1', 8080))
client.run()
import socketserver
import struct
import json
import os
# 创建服务类,继承了 socketserver.BaseRequestHandler , 基本的处理类
class FtpServer(socketserver.BaseRequestHandler):
# 定义编码格式
coding = 'utf-8'
# 定义服务文件路径
server_dir = 'file_upload'
# 定义一次性最大传输
max_packet_size = 1024
# 定义根路径
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
# 文件处理函数
def handle(self):
# 查看自己的request请求
print(self.request)
# 创建循环
while True:
# 调用自己的请求对象接收数据
data = self.request.recv(4)
# 数据解包
data_len = struct.unpack('i', data)[0]
# 取出打好的头
head_json = self.request.recv(data_len).decode(self.coding)
# 转数据格式
head_dic = json.loads(head_json)
# {'cmd': cmd, 'filename': os.path.basename(filename), 'filesize': filesize}
# print(head_dic)
# 获取命令
cmd = head_dic['cmd']
# 判断当前自己是否具有该方法
if hasattr(self, cmd):
func = getattr(self, cmd)
func(head_dic)
def _create_filename(self, path):
if not os.path.exists(path):
os.mkdir(path)
# 定义上传处理函数
def put(self, args):
# 定义文件路径
file_path = os.path.normpath(os.path.join(
self.BASE_DIR,
self.server_dir,
args['filename']
))
# 创建上传文件存储路径 ..\file_upload\
self._create_filename(os.path.join(self.BASE_DIR, self.server_dir))
# 取出文件大小
filesize = args['filesize']
# 定义起始文件接收量
recv_size = 0
print('----->', file_path)
# 打开文件,保存文件数据
with open(file_path, 'wb') as f:
while recv_size < filesize:
recv_data = self.request.recv(self.max_packet_size)
f.write(recv_data)
recv_size += len(recv_data)
print('recvsize:%s filesize:%s' % (recv_size, filesize))
ftpserver = socketserver.ThreadingTCPServer(('127.0.0.1', 8080), FtpServer)
ftpserver.serve_forever()
'''
# data : 总的二进制数据
for line in data:
with opem(file,'wb') as fp:
fp.write(line)
# data : 总的二进制数据
with opem(file,'wb') as fp:
for line in data:
fp.write(line)
'''
【二】操作系统
【1】操作系统介绍
-
顾名思义,进程即正在执行的一个过程。
-
- 进程是对正在运行程序的一个抽象。
-
进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一。
-
- 操作系统的其他所有内容都是围绕进程的概念展开的。
-
所以想要真正了解进程,必须事先了解操作系统
【2】为什么要有操作系统
-
现代的计算机系统主要是由一个或者多个处理器,主存,硬盘,键盘,鼠标,显示器,打印机,网络接口及其他输入输出设备组成。
-
- 一般而言,现代计算机系统是一个复杂的系统。
(1)开发效率慢
- 如果每个程序员都必须掌握该系统所有的细节,那就不可能再编写代码了(严重影响了程序员的开发效率:全部掌握这些细节可能需要一万年...)
(2)优化操作行为
- 管理部件并加以优化使用,是一件极富挑战性的工作,于是,计算机安装了一层软件(系统软件),称为操作系统。
- 它的任务就是为用户程序提供一个更好、更简单、更清晰的计算机模型,并管理刚才提到的所有设备。
【3】什么是操作系统
- 操作系统就是一个协调、管理和控制计算机硬件资源和软件资源的控制程序
- 位于系统硬件和应用程序之间的中间介质
【4】操作系统的功能
(1)隐藏硬件接口
- 隐藏了丑陋的硬件调用接口,为应用程序员提供调用硬件资源的更好,更简单,更清晰的模型(系统调用接口)。
- 应用程序员有了这些接口后,就不用再考虑操作硬件的细节,专心开发自己的应用程序即可。
(2)资源有序化
- 将应用程序对硬件资源的竞争和请求变得有序化
(3)对资源进行合理的调度和分配
- 记录每个应用程序需要什么资源
- 对请求的资源进行分配
- 中和每一个应用之间的请求
【5】如何实现资源有序化
- 每一个电脑上有一个操作系统的基本流程:多路复用
(1)时间上的复用
- 当一个资源在时间上复用时,不同的程序或用户轮流使用它,第一个程序获取该资源使用结束后,在轮到第二个。。。第三个。。。
# 【1】时间上的复用
# 一个资源在时间上复用时,多个程序可以轮流使用这块时间
# 两个程序 : 程序一 5s 执行程序二 10s 程序三 5s
# (1)方案一: 先执行程序一,在执行程序二,在程序三 =---> 20s
# (2)方案二: 执行程序一的同时执行程序二同时执行程序三 =---> 10s
# CPU ---> 这个CPU是指你硬件CPU上面执行的哪个CPU 10核20线程 --> 10个CPU
# 如果你的电脑上只有一个 CPU : 方案一
# 如果你的电脑上多个 CPU : 现在你的电脑的效果就是方案二 效果
(2)空间上的复用
- 每个客户都获取了一个大的资源中的一小部分资源,从而减少了排队等待资源的时间。
# 【2】空间上的复用
# 内存条 : 8G --> 16G内存
# 内存是用来临时存储数据和CPU进行交互的
# 开设进程 ---> 启动应用程序 ---> 占用内存空间 ---> 如果做图形设计 --> 内存条建议高一些 ---> 缓存在内存中
# 没有内存空间看到的效果就是电脑非常卡 ---> 如果你的CPU带不起来太高的内存
# 开设进程 ---> 启动应用程序 ---> 占用内存空间
# 谁用就先把内存空间给他用
# 为什么你看到的效果是 既能用QQ又能用微信 ---> CPU切换的足够快
(3)时间上的复用 + 空间上的复用 ---》多路复用
【6】操作系统和普通软件的区别
-
主要区别:
-
- 你不想用暴风影音了你可以选择用迅雷播放器或者干脆自己写一个
- 但是你无法写一个属于操作系统一部分的程序(时钟中断处理程序)
- 操作系统由硬件保护,不能被用户修改。
-
操作系统与用户程序的差异并不在于二者所处的地位。
- 特别的,操作系统是一个大型、复杂、长寿的软件。
【三】操作系统发展史
【1】第一代计算机(1940-1955)
- 真空管和穿孔卡片
(1)产生背景
- 第一代之前人类是想用机械取代人力
- 第一代计算机的产生是计算机由机械时代进入电子时代的标志
- 在第二次世界大战,数字计算机的建造几乎没有什么进展
- 第二次世界大战刺激了有关计算机研究的爆炸性进展。
(2)工作过程
- 程序员在墙上的机时表预约一段时间
- 然后程序员拿着他的插件版到机房里
- 将自己的插件板街道计算机里
- 这几个小时内他独享整个计算机资源
- 后面的一批人都得等着(两万多个真空管经常会有被烧坏的情况出现)。
- 后来出现了穿孔卡片,可以将程序写在卡片上,然后读入机而不用插件板
(3)优缺点
- 优点
- 程序员在申请的时间段独享整个资源
- 可以即时地调试自己的程序(有bug可以立刻处理)
- 缺点
- 浪费计算机资源,一个时间段内只有一个人用。注意:同一时刻只有一个程序在内存中,被 cpu 调用执行,比方说10个程序的执行,是串行的。
【2】第二代计算机 - 第四代计算机(以及各代的背景介绍、总结)
# 【二】第二代计算机
# 晶体管和批处理系统
# 当时的计算机非常昂贵:自然而然的想要减少资源的浪费
# ● 设计人员、生产人员、操作人员、程序人员和维护人员直接有了明确的分工
# ● 计算机被锁在专用空调房间中 : 现在的服务器很像
# ● 由专业操作人员运行,这便是‘大型机’。
# 出现了磁带:复读机 --> 次代种包含了电流信息
# 【三】第三代计算
# 集成电路芯片和多道程序设计
# ● IBM公司试图通过引入system/360系列来同时满足科学计算和商业计算
# ○ 360系列低档机与1401相当,高档机比7094功能强很多
# ○ 不同的性能卖不同的价格
# ● 360是第一个采用了(小规模)芯片(集成电路)的主流机型
# ○ 与采用晶体管的第二代计算机相比,性价比有了很大的提高。
# ○ 这些计算机的后代仍在大型的计算机中心里使用,此乃现在服务器的前身
# ○ 这些服务器每秒处理不小于千次的请求。
# 如何解决第二代计算机的问题1
# ● 卡片被拿到机房后能够很快的将作业从卡片读入磁盘,于是任何时刻当一个作业结束时,操作系统就能将一个作业从磁带读出,装进空出来的内存区域运行,这种技术叫做 同时的外部设备联机操作:SPOOLING
# ● 该技术同时用于输出。
# ● 当采用了这种技术后,就不在需要IBM1401机了,也不必将磁带搬来搬去了(中间俩小人不再需要)
# 如何解决第二代计算机的问题2
# ● 第三代计算机的操作系统广泛应用了第二代计算机的操作系统没有的关键技术:
# ○ 多道技术
# ● cpu在执行一个任务的过程中
# ○ 若需要操作硬盘,则发送操作硬盘的指令
# ○ 指令一旦发出,硬盘上的机械手臂滑动读取数据到内存中
# ○ 这一段时间,cpu需要等待,时间可能很短
# ○ 但对于cpu来说已经很长很长,长到可以让cpu做很多其他的任务
# ○ 如果我们让cpu在这段时间内切换到去做其他的任务
# ○ 这样cpu不就充分利用了吗。这正是多道技术产生的技术背景
# 【第四代计算机】个人计算机
# 有了单独的CPU和内存
# 每个部分负责响应的功能
# 【六】小结
# 【1】操作系统的作用
# ● 隐藏丑陋复杂的硬件接口,提供良好的抽象接口
# ● 管理、调度进程,并且将多个进程对硬件的竞争变得有序
# 【2】多道技术
# (1)产生背景:针对单核,实现并发
# ● 现在的主机一般是多核,那么每个核都会利用多道技术 有4个cpu,运行于cpu1的某个程序遇到io阻塞,会等到io结束再重新调度,会被调度到4个cpu中的任意一个,具体由操作系统调度算法决定。
# (2)空间上的复用:如内存中同时有多道程序
# ● 空间上的复用指的是在内存中同时运行多个程序,这样可以有效地利用内存资源。
# (3)时间上的复用:复用一个cpu的时间片
# ● 指共享同一台机器的多个进程可以轮流使用CPU,从而避免了长时间等待的情况发生。
# ● 强调:遇到io切,占用cpu时间过长也切,核心在于切之前将进程的状态保存下来,这样才能保证下次切换回来时,能基于上次切走的位置继续运行
# 【3】操作系统发展史
# ● 第一代计算机(1940~1955):真空管和穿孔卡片
# ● 第二代计算机(1955~1965):晶体管和批处理系统
# ● 第三代计算机(1965~1980):集成电路芯片和多道程序设计
# ● 第四代计算机(1980~至今):个人计算机