引言 本篇文件研究的网络编程是指基于网络编写代码 能够实现数据的远程交互。实现数据的远程交互必备的基础条件是物理连接介质 比如网卡、网线、电话线。
一、OSI七层协议
OSI (Open System Interconnect ,开放式系统互连)七层协议:规定了所有的计算机在远程数据交互的时候必须经过相同的处理流程、在制造过程中必须拥有相同的功能硬件。接受网络消息 数据由下往上传递、发送网络消息 数据由上往下传递。OSI七层协议口诀为:应、表、会、传、网、数、物,常见的是整合之后的五层或四层。
应用层、表示层、会话层、传输层、网络层、数据链路层、物理链接层(七层)
应用层、传输层、网络层、数据链路层、物理链接层(五层) (核心)
应用层、传输层、网络层、网络接口层(四层)
# 网络相关专业名词(基础储备知识)
'交换机':能够将所有接入交换机的计算机彼此互联起来
'广播':首次查找接入同一个交换机的其他计算机需要朝交换机里面吼一嗓子
'单播':首次被查找的计算机回应查找它的计算机并附带自己的mac地址
'广播风暴':接入同一台交换机的多态计算机同时发广播
'局域网':有单个交换机组成的网络 在局域网内可以直接使用mac地址通信
'广域网':可以理解为范围更大的局域网
'互联网':由局域网、广域网连接到一起形成的网络
'路由器':用来链接不同的局域网计算机的介质
1.OSI七层协议之物理链接层
建立物理链接介质
2.OSI七层协议之数据链路层
# 规定了电信号的分组方式
# 以太网协议
规定了计算机在出厂的时候都必须有一块网卡 网卡上有一串数字
该数字相当于是计算机的身份证号码是独一无二的
该数字的特征:12位16进制数据(前6位产商编号 后6位流水线号)
该数字也称为:以太网地址/MAC地址
3.OSI七层协议之网络层
IP协议:规定了所有接入互联网的计算机都必须有一个IP地址 类似于身份证号
mac地址是物理地址可以看成永远无法修改
IP地址是动态分配的不同的场所IP是不同的、可以用来标识全世界独一无二的一台计算机、可以跨局域网传输
IP地址特征:
IPV4:点分十进制
0.0.0.0
255.255.255.255
IPV6:能够给地球上每一粒沙分一个IP地址
4.OSI七层协议之传输层
TCP与UDP都是用来规定通信方式的
通信的时候可以随心所欲的聊 也可以遵循一些协议符合要求的聊
随性所欲的聊:文字 图片 视频 小油腻话 你侬我侬
遵循一些协议:开头带尊称 首行空两格 只准用官话 不能打情骂俏
1.TCP协议(重要)
三次握手建链接
TCP协议也称为可靠协议(数据不容易丢失)
造成数据不容易丢失的原因不是因为有双向通道 而是因为有反馈机制
给对方发消息之后会保留一个副本 直到对方回应消息收到了才会删除
否则会在一定的时间内反复发送
洪水攻击同一时间有大量的客户端请求建立链接 会导致服务端一致处于SYN_RCVD状态
服务端如何区分客户端建立链接的请求 可以对请求做唯一标识
四次挥手断链接
四次不能合并为三次 因为中间需要确认消息是否发完(TIME_WAIT)
2.UDP协议
也称之为数据报协议、不可靠协议
早期的QQ使用的是纯生的(没有加任何额外功能)UDP协议
现在的QQ自己添加了很多技术和功能
使用UDP的原因就是因为很简单 快捷 粗暴 只要指定对方的地址就可以发消息了
# PORT协议(端口协议)
用来标识一台计算机上面的某一个应用程序
范围:0-65535
特征:动态分配(洗浴中心号码牌)
建议:
0-1024 系统默认需要使用
1024-8000 常见软件的端口号
8000之后的
URL:统一资源定位符(网址)
网址本质是有IP和PORT组成的!!!
IP+PORT:能够定位全世界独一无二的一台计算机上面的某一个应用程序
域名解析:将网址解析成IP+PORT
我们之所以不直接使用IP+PORT的原因是太难记 所以发明了域名(网址)
IP:PORT 实际使用冒号连接
114.55.205.139:80
5.OSI七层协议之会话层
会话层提供的服务是应用建立和维持会话,并能使会话获得同步。会话层使用校验点可使通信会话在通信失效时从校验点继续恢复通信。这种能力对于传送大的文件极为重要。会话层,表示层,应用层构成开放系统的高3层,面向应用进程提供分布处理、对话管理、信息表示、检查和恢复与语义上下文有关的传送差错等。为给两个对等会话服务用户建立一个会话连接。
6.OSI七层协议之表示层
表示层的作用之一是为异种机通信提供一种公共语言,以便能进行互操作。这种类型的服务之所以需要,是因为不同的计算机体系结构使用的数据表示法不同。例如,IBM主机使用EBCDIC编码,而大部分PC机使用的是ASCII码。在这种情况下,便需要会话层来完成这种转换。通过前面的介绍,我们可以看出,会话层以下5层完成了端到端的数据传送,并且是可靠的、无差错的传送。但是数据传送只是手段而不是目的,最终是要实现对数据的使用。由于各种系统对数据的定义并不完全相同,最易明白的例子是键盘——其上的某些键的含义在许多系统中都有差异。这自然给利用其它系统的数据造成了障碍。表示层和应用层就担负了消除这种障碍的任务。
7.OSI七层协议之应用层
应用层相当于是程序员自己写的应用程序 里面的协议非常的多
常见的有:HTTP、HTTPS、FTP
二、socket模块
如果我们需要编写基于网络进行数据交互的程序,意味着需要自己通过代码来控制之前所学的OSI七层协议。但是过程很繁琐、操作非常复杂、就相当于自己编写操作系统。所以socket模块也叫套接字出场了,它类似于操作系统,封装了丑陋的复杂接口提供了快捷的接口。
基于文件类型的套接字家族(单机) AF_UNIX;
基于网络类型的套接字家族(联网) AF_INET;
# 服务端
import socket
# 1.首先要产生socket对象并指定采用的通信版本和协议,括号内空默认是TCP协议
server = socket.socket()
# 2.绑定一个固定的地址(服务端必备的条件),第一个参数为本机回环地址只能本机才能访问
server.bind(('127.0.0.1', 8080))
# 3.设立板连接池
server.listen(5)
# 4.等待接客 三次握手 sock是双向通道 addr是客户端地址
sock, addr = server.accept()
print(sock, addr)
# 5.服务客人 一次性接受1024字节 发送的信息必须是bytes类型
data = sock.recv(1024)
print(data.decode('utf8'))
sock.send('Hello,what can do for you?'.encode('utf8'))
# 6.关闭双向通道 就相当于门店打烊了 四次挥手
sock.close()
# 7.关闭服务端 就相当于门店倒闭了
server.close()
# 客户端
import socket
# 1.首先生成socket对象指定类型和协议
client = socket.socket()
# 2.通过服务端的地址链接服务端
client.connect(('127.0.0.1', 8080))
# 3.直接给服务端发送信息
client.send('Hi,I need ur help'.encode('utf8'))
# 4.接受服务端发送的信息
data = client.recv(1024)
print(data.decode('utf8'))
# 5.断开与服务端的链接
client.close()
三、并发编程理论
1.操作系统发展史(可以说CPU的利用率发展史)
一、穿孔卡片;计算机很庞大 使用很麻烦 一次只能给一个人使用 期间很多时候计算机都不工作。这样好处就是程序员独占计算机为所欲为、反而坏处就是计算机利用率降低 浪费资源。
二、联机批处理系统;提前使用磁带一次性录入多个程序员的程序然后交给计算机处理,这样CPU工作效率提升了不用反复等待程序录入。
三、脱机批处理系统;极大的提升了CPU的利用率,总体而言,整个发展过程只做一件事情,那就是不断想办法提升CPU的利用率。所以毫无疑问,计算机中真正工作的部分是CPU。
2.多道技术
研究躲到技术前,我们先说一下单道技术(排队执行);即上面所讨论过,所有的程序排序执行过程中不能重合,然而我们的多道技术利用计算机空闲时间提前准备其他数据最大化提升CPU的利用率。接下来咱们详细讨论一下多道技术(并发效果)。
一、切换;计算机的CPU在两种情况下会切换,程序有IO操作即输入输出,input、time.sleep、read、write。我们尽可能的让CPU同时运行多个程序。
二、保存状态;CPU每次切换走之前都需要保存当前的操作状态下次切换回来基于上次记录继续执行
3.进程理论
程序:一堆死代码(还没被运行)
进程:正在运行的程序(被运行的代码) 是资源单位表示一块内存空间
线程:是执行单位 表示真正的代码指令
进程的调度算法
一、FCFS(first come first serve,先来先服务);对短作业不友好
二、短作业优先调度;对长作业不友好
三、时间片轮转法加多级反馈队列(目前还在使用);将时间均分然后根据继承时间的长短再分多个等级,等级越靠下表示耗时越长、每次分到的时间越多但是优先级越低。
4.进程的并行与并发
并行;多个进程同时执行必须要多个CPU参与单个CPU无法实现并行
并发;多个进程看上去像同时执行单个CPU可以实现多个CPU也肯定可以
5.进程的三个状态、同步异步、阻塞与非阻塞、综合使用
一、就绪态;所有的进程在被CPU执行之前都必须先进入就绪状态等待
二、运行态;CPU正在运行
三、阻塞态;进程运行过程中出现了IO操作阻塞态无法直接进入运行态需要先进入就绪态
同步;提交完成任务之后原地等待任务的返回结果期间不做任何事情
异步;提交完成任务之后不会原地等待返回结果直接去做其他事情有结果自动通知
阻塞;阻塞态
非阻塞;就绪态、运行态
综合使用;有四种状态,同步阻塞、同步非阻塞、异步阻塞、异步非阻塞(效率最高)
6.创建进程的两种方式
"""
1.鼠标双击软件图标
2.Python代码创建进程
"""
from multiprocessing import Process
import time
class MyProcess(Process):
def __init__(self, name, age):
super().__init__()
self.name = name
self.age = age
def run(self):
print('run is running', self.name, self.age)
time.sleep(3)
print('run is over', self.name. self.age)
if __name__ == '__main__':
obj = MyProcess('almira', 123)
obj.start()
print('主')
7.进程对象的多种方法
from multiprocessing import Process, current_procsee
import os
import time
# 1.查看进程号
current_process()
current_process().pid
os.getpid()
os.getppid()
# 2.结束进程
p1.terminate()
# 3.判断进程是否存活
p1.is_alive()
# 4.守护进程
def task(name):
print('进程名:%s' % name)
time.sleep(3)
print('进程名:%s' % name)
if __name__ == '__main__':
p1 = Process(target=task, args=('米热',))
p1.daemon = True
p1.start()
time.sleep(1)
print('复习使我快乐')
8.多进程数据错乱问题 模拟抢票软件
from multiprocessing import Process
import time
import json
import random
# 查票
def search(name):
with open(r'data.json', 'r', encoding='utf8') as f:
data = json.load(f)
print('%s在查票 当前余票为:%s' % (name, data.get('ticket_num')))
# 买票
def buy(name):
# 再次确认票
with open(r'data.json', 'r', encoding='utf8') as f:
data = json.load(f)
# 模拟网络延迟
time.sleep(random.randint(1, 3))
# 判断是否有票 有就买
if data.get('ticket_num') > 0:
data['ticket_num'] -= 1
with open(r'data.json', 'w', encoding='utf8') as f:
json.dump(data, f)
print('%s买票成功' % name)
else:
print('%s很倒霉 没有抢到票' % name)
def run(name):
search(name)
buy(name)
if __name__ == '__main__':
for i in range(10):
p = Process(target=run, args=('用户%s'%i, ))
p.start()
9.进程池与线程池
进程和线程都不可以无限制的创建 因为硬件的发展速度赶不上软件 有物理极限 如果在编写代码的过程中无限制的创建进程或线程可能会导致计算机崩溃。
一、池;降低程序的执行效率 但是保证了计算机硬件的安全
二、进程池;提前创建好固定数量的进程供后续程序的调用超出则等待
三、线程池;提前创建好固定数量的线程供后续程序的调用超出则等待
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
import os
import time
import random
from threading import current_thread
# 1.产生含有固定数量线程的线程池
# pool = ThreadPoolExecutor(10)
pool = ProcessPoolExecutor(5)
def task(n):
print('task is running')
# time.sleep(random.randint(1, 3))
# print('task is over', n, current_thread().name)
# print('task is over', os.getpid())
return '我是task函数的返回值'
def func(*args, **kwargs):
print('from func')
if __name__ == '__main__':
# 2.将任务提交给线程池即可
for i in range(20):
# res = pool.submit(task, 123) # 朝线程池提交任务
# print(res.result()) # 不能直接获取
# pool.submit(task, 123).add_done_callback(func)
10.协成
进程;资源单位
线程;执行单位
协成;单线程下实现并发(效率极高)
下面咱细说一下协成吧!在代码层面欺骗CPU 让CPU觉得我们的代码里面没有IO操作、实际上IO操作被我们自己写的代码检测一旦有立刻代码执行别的程序;该技术完全是程序员自己弄出来的名字也是程序自己起的,核心:自己写代码完成切换+保存状态
'协成代码实现'
import time
from gevent import monkey;
monkey.patch_all() # 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn
def func1():
print('func1 running')
time.sleep(3)
print('func1 over')
def func2():
print('func2 running')
time.sleep(5)
print('func2 over')
if __name__ == '__main__':
start_time = time.time()
# func1()
# func2()
s1 = spawn(func1) # 检测代码 一旦有IO自动切换(执行没有io的操作 变向的等待io结束)
s2 = spawn(func2)
s1.join()
s2.join()
print(time.time() - start_time) # 8.01237154006958 协程 5.015487432479858
'协成实现并发'
import socket
from gevent import monkey;monkey.patch_all() # 固定编写 用于检测所有的IO操作(猴子补丁)
from gevent import spawn
def communication(sock):
while True:
data = sock.recv(1024)
print(data.decode('utf8'))
sock.send(data.upper())
def get_server():
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
while True:
sock, addr = server.accept() # IO操作
spawn(communication, sock)
s1 = spawn(get_server)
s1.join()