Ⅰ socketserver
【一】引入
-
socket并不能多并发,只能支持一个用户
-
socketserver 模块是 Python 中用于创建网络服务器的模块,提供了一种简单而一致的接口。
-
- 它是构建网络服务器的框架,处理了创建、维护和关闭连接的许多底层细节
- socketserver是socket的再封装
【二】socketserver介绍
【1】简介
-
socketserver在python2中为SocketServer
-
- 在python3种取消了首字母大写,改名为socketserver。
-
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请求处理类的一个实现。
【三】socket 模块与 socketserver 关系
-
socket 和 socketserver 是两个不同的 Python 模块,都用于网络编程。
-
socket 模块提供了通用的套接字编程接口,可用于创建客户端和服务器端程序。
-
- 它涵盖了与网络通信相关的底层细节
- 如创建套接字、绑定地址、监听请求、接受连接、发送数据和接收数据。
-
socketserver 模块是 socket 模块的一个封装
-
- 它抽象了服务器端网络编程的复杂度,使您能够快速编写服务器端程序。
- 它提供了多种服务器类型
- 如多线程服务器、多进程服务器和单进程服务器,以满足不同的网络编程需求。
- 此外,它还提供了简单的面向对象编程模型,允许您扩展基础类并定制服务器行为。
-
小结
-
- 如果您需要快速编写简单的服务器端程序,那么 socketserver 模块可能是您的最佳选择
- 如果您需要更多的灵活性和细节控制,则可以使用 socket 模块
【四】socketserver使用
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) 会依次写入内容
'''
Ⅱ 操作系统
【一】进程
- 进程其实就是在电脑上正在执行的一个过程
- 进程来源于操作系统,是操作系统最核新的概念,是一个抽象的概念
【二】为什么要有操作系统
- 现在的计算机的组成硬件都有CPU、硬盘、内存条、显卡、主板 ....
- 统筹所有的硬件就有了操作系统这个概念
【1】一开始
- 要统筹所有的操作硬件,必须掌握硬件每一个细节
- 很繁琐,开发效率很慢
【2】于是为了优化
- 将一部分功能和硬件封装起来 ---> 进行优化
- 面条版 ---> 函数 ---> 类
【3】诞生了操作系统
- 将细节的操作功能和操作步骤封装起来,而是暴露给用户一些可以操作的接口和步骤
- 用户不需要了解自己的硬件细节就可以直接操作硬件
【三】什么是操作系统
- 操作系统就是一个协调、管理和控制计算机硬件资源和软件资源的控制程序
- 位于系统硬件和应用程序之间的中间介质
【四】操作系统的功能
【1】隐藏硬件接口
- 将调用硬件的详细步骤隐藏起来,只会给你一些点点的操作步骤
- 根据这些接口自己高度定制自己的程序而不需要考虑硬件细节
【2】资源有序化
- 将应用恒旭对硬件资源的竞争和请求变得有序化
【3】对资源进行合理的调度和分配
- 记录哪个应用程序需要什么资源
- 队请求的资源进行分配
- 中和每一个应用之间的请求
【五】如何实现资源有序化
- 每一个电脑上有一个操作系统的基本流程:多路复用
【1】时间上的复用
- 一个资源在时间上复用时,多个程序可以轮流使用这块时间
- 两个程序 : 程序一 5s 执行程序二 10s 程序三 5s\
(1)方案一:
- 先执行程序一,在执行程序二,在程序三 =---> 20s
(2)方案二:
- 执行程序一的同时执行程序二同时执行程序三 =---> 10s
- CPU ---> 这个CPU是指你硬件CPU上面执行的哪个CPU 10核20线程 --> 10个CPU
如果你的电脑上只有一个 CPU : 方案一
如果你的电脑上多个 CPU : 现在你的电脑的效果就是方案二 效果
【2】空间上的复用
- 内存条 : 8G --> 16G内存
- 内存是用来临时存储数据和CPU进行交互的
- 开设进程 ---> 启动应用程序 ---> 占用内存空间 ---> 如果做图形设计 --> 内存条建议高一些 ---> 缓存在内存中
- 没有内存空间看到的效果就是电脑非常卡 ---> 如果你的CPU带不起来太高的内存
- 开设进程 ---> 启动应用程序 ---> 占用内存空间\
- 谁用就先把内存空间给他用
- 为什么你看到的效果是 既能用QQ又能用微信 ---> CPU切换的足够快
【3】时间上的复用 + 空间上的复用 ---> 多路复用
- 现在电脑里面对于进程的处理技术
【六】操作系统和普通软件的区别
-
操作系统:
- 操作系统是普通软件运行的载体
- 操作系统比较大 --> 4 - 20 G
- 操作系统寿命比较长 ---> 开发出一个完整的系统你就会一直用
-
普通软件:一天之内就诞生了 --> 不断的版本迭代 --> 一年之内可能会迭代出 20 个版本
Ⅲ 操作系统发展史
学习并发编程其实就是在学习操作系统 理论居多 实战少 都是封装的代码
【一】第一代计算机(1940~1955):真空管和穿孔卡片
【1】特点
- 没有操作系统的概念 所有的程序设计都是直接操控硬件
【2】优点
- 程序员在申请的时间段内独享整个资源
- 可以即时地调试自己的程序(有bug可以立刻处理)
【3】缺点
- CPU利用率极低
- 浪费计算机资源,一个时间段内只有一个人用。注意:同一时刻只有一个程序在内存中,被cpu调用执行,比方说10个程序的执行,是串行的
【二】第二代计算机(1955~1965):晶体管和批处理系统
【1】特点
- 设计人员、生产人员、操作人员、程序人员和维护人员直接有了明确的分工
- 计算机被锁在专用空调房间中
- 由专业操作人员运行,这便是‘大型机’。
【2】联机批处理系统优点
- 一次性可以录入多个用户指令,缩短了CPU等待的时间,提高了CPU的利用率
【三】第三代计算机(1965~1980):集成电路芯片和多道程序设计
# 如何解决第二代计算机的问题1
卡片被拿到机房后能够很快的将作业从卡片读入磁盘,于是任何时刻当一个作业结束时,操作系统就能将一个作业从磁带读出,装进空出来的内存区域运行,这种技术叫做 同时的外部设备联机操作:SPOOLING
该技术同时用于输出。
当采用了这种技术后,就不在需要IBM1401机了,也不必将磁带搬来搬去了(中间俩小人不再需要)
# 如何解决第二代计算机的问题2
第三代计算机的操作系统广泛应用了第二代计算机的操作系统没有的关键技术:
多道技术
cpu在执行一个任务的过程中
若需要操作硬盘,则发送操作硬盘的指令
指令一旦发出,硬盘上的机械手臂滑动读取数据到内存中
这一段时间,cpu需要等待,时间可能很短
但对于cpu来说已经很长很长,长到可以让cpu做很多其他的任务
如果我们让cpu在这段时间内切换到去做其他的任务
这样cpu不就充分利用了吗。这正是多道技术产生的技术背景
【1】多道技术
-
多道技术中的多道指的是多个程序
-
多道技术的实现是为了解决多个程序竞争或者说共享同一个资源(比如cpu)的有序调度问题
-
解决方式即多路复用
-
- 多路复用分为时间上的复用和空间上的复用。
(1)空间上的复用
- 将内存分为几部分,每个部分放入一个程序,这样,同一时间内存中就有了多道程序。
(2)时间上的复用
- 当一个程序在等待I/O时,另一个程序可以使用cpu
- 如果内存中可以同时存放足够多的作业,则cpu的利用率可以接近100%
- 类似于我们小学数学所学的统筹方法。
(3)详解:
-
空间上的复用最大的问题是:
-
- 程序之间的内存必须分割
- 这种分割需要在硬件层面实现,由操作系统控制。
- 如果内存彼此不分割,则一个程序可以访问另外一个程序的内存,
-
首先丧失的是安全性
-
- 比如你的qq程序可以访问操作系统的内存
- 这意味着你的qq可以拿到操作系统的所有权限。
-
其次丧失的是稳定性
-
- 某个程序崩溃时有可能把别的程序的内存也给回收了
- 比方说把操作系统的内存给回收了,则操作系统崩溃。
第三代计算机的操作系统仍然是批处理
-
许多程序员怀念第一代独享的计算机
-
- 可以即时调试自己的程序。
- 为了满足程序员们很快可以得到响应,出现了分时操作系统
# 如何解决第二代计算机的问题3
分时操作系统: 多个联机终端+多道技术
20个客户端同时加载到内存
有17在思考,3个在运行
cpu就采用多道的方式处理内存中的这3个程序
由于客户提交的一般都是简短的指令而且很少有耗时长的
索引计算机能够为许多用户提供快速的交互式服务
所有的用户都以为自己独享了计算机资源
CTTS:
麻省理工(MIT)在一台改装过的7094机上开发成功的
CTSS兼容分时系统,第三代计算机广泛采用了必须的保护硬件(程序之间的内存彼此隔离)之后,分时系统才开始流行
MIT:
贝尔实验室和通用电气在CTTS成功研制后决定开发能够同时支持上百终端的MULTICS(其设计者着眼于建造满足波士顿地区所有用户计算需求的一台机器)
很明显真是要上天啊,最后摔死了。
后来
一位参加过MULTICS研制的贝尔实验室计算机科学家Ken Thompson开发了一个简易的,单用户版本的MULTICS
这就是后来的UNIX系统。
基于它衍生了很多其他的Unix版本,为了使程序能在任何版本的unix上运行,IEEE提出了一个unix标准,即posix(可移植的操作系统接口Portable Operating System Interface)
后来
在1987年,出现了一个UNIX的小型克隆,即minix,用于教学使用。
芬兰学生Linus Torvalds基于它编写了Linux
【四】第四代计算机(1980~至今):个人计算机
- 有了单独的CPU和内存
- 每个部分负责响应的功能
【五】发展史概览
计算机发展阶段 | 时间 | 组成 | 改进 | 缺陷 |
---|---|---|---|---|
第一代计算机 | 1940~1955 | 真空管和穿孔卡片 | 没有操作系统的概念 所有的程序设计都是直接操控硬件 | |
第二代计算机 | 1955~1965 | 晶体管和批处理系统 | 批处理,节省机时 | 全程需要人参与,仍是串行,无法及时调试程序 |
第三代计算机 | 1965~1980 | 集成电路芯片和多道程序设计 | 运用多道技术 | |
第四代计算机 | 1980至今 | 个人计算机 | 支持(伪)并发的能力 |
【六】总结
- 操作系统的发展史其实就是提升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~至今):个人计算机