首页 > 其他分享 >网络并发总结

网络并发总结

时间:2022-11-20 14:58:31浏览次数:49  
标签:总结 __ task name 网络 并发 print 进程 服务端

周总结

计算机网络编程理论

针对软件开发 目前市面上的主流软件 都基本上会使用网络进行通信 传输各种数据作为软件开发人员 掌握网络编程理论和了解网络编程开发架构是必须的

软件开发架构

ATM 和选课系统都是用了三层架构的方式 第一层是用户层 就是客服端 第二层和第三层 是接口层和数据处理层 可以看成是服务端

C/S架构

​ Client:客户端

​ eg:计算机上面下载的各种app

​ Server:服务端

​ 软件的核心数据都在他们的服务端中 核心的处理数据的方式也都在服务端的程序中

B/S架构

​ Browser:浏览器

​ eg:浏览器可以直接访问的网页版的淘宝

​ Server:服务端

​ ''' 浏览器可以充当所有服务端的客户端'''

C/S架构和B/S架构的区别:

C/S架构:

​ 优势:是不同的互联网公司独立开发的 可以高度定制客户端功能

​ 劣势:需要下载才能使用

B/S架构:

​ 优势:不用下载可以直接使用

​ 劣势:不可以高度定制客户端 并且要遵循很多规则

计算机网络专业名词

工作方式

​ 边缘部分:用户直接使用C/S等方式通信

​ 核心部分:为用户服务 包括网络通路 交换机 路由器等等设备

交换机

​ 能够将所有接入交换机的计算机彼此连接起来

广播

当某服务器首次想要与某一台服务器连接时 就会向每一台服务器发送相同的请求 不管你遇需不需要

单播

首次被查找的服务器会回应查找他的计算机 会把自己的mac地址也传过去

广播风暴

接入同一台交换机的多台计算机同时发广播

局域网

是指在某一区域内由多台计算机互联成的计算机组 可以使用mac地址通信

广域网

范围更大的广域网

互联网

由所局域网和广域网连接到一起形成的网络

网络编程协议简介

OSI七层协议

​ 规定了所有计算机再进行交互的时候都必须经过相同的处理流程 在制造过程中必须拥有相同的功能硬件

1.应用层

2.表示层

3.会话层

4.传输层

5.网络层

6.数据链路层

7.物理连接层

在进行发送数据的时候都会进行打包 每一层接收到的就是上一层打包的总和

在接收数据的时候会进行解包 每一层解包都会更加还原原本数据

五层协议

应用层 表示层 会话层 可以都看成应用层

OSI七层协议之物理层

物理层要确保计算机之间的物理连接介质 接收到的数据(bytes类型)

OSI七层协议之数据链路层

1.规定了电信号的分组方式

2.以太网协议

​ 规定了计算机在出厂的时候有一块网卡 就是他的身份证 且独一无二一直不会改变

​ 数字特征:12位16进制数 前6位是厂商编号 后6位是流水线号

​ 以太网也被称为MAC地址

OSI七层协议之网络层

​ IP协议:规定了所有接入互联网的计算机都必须有一个ip地址 类似于身份证号

​ mac地址是永远无法改变

​ ip地址是动态分配的 不同的场所ip地址不同

​ ip地址分为:

​ IPV4:点分十进制

​ 0.0.0.0

​ 225.225.225.225

​ IPV6:很多

​ ps:ip地址可以标识世界上独一无二的一台计算机

OSI七层协议之传输层

PORT协议

用来表示一台计算机上的某一个应用程序

范围0-65535

特征:也是动态分配 但是一些端口会固定分配给一些常见的软件

为了避免冲突我们所写的软件端口号最好是8000以后

0-1024 系统默认使用的一些端口号
1024-8000 常见软件的端口号
8000 不常见的 

URL:统一资源定位符(网址)

网址的本质:IP:PORT (之间用:连接)

网址能够定位世界上独一无二的一台计算机上面的某一个应用程序

域名解析:可以将往之解析成ip+port

网络编程之socke模块

传输层之TCP与UDP协议

TUP和UDP都是用来规定通信方式的

TCP协议

又称可靠协议(数据不容易丢失)

三次握手

客户端发送连接请求 服务端接收到了并反馈 可以接受 建立从客服端到服务端的通道 与此同时 服务端也会给客户端发送一个连接请求 让客户端同意服务端向客服端建立通道 这样就建立了双向通道

造成数据不易流失的原因不是因为有通道 而是因为有反馈机制

在向对方发送消息的时候 自己会保留一个副本 直到对方回应 我收到消息了 才会删除本地数据 否则会在一定时间内反复发送

洪水攻击

同一时间有大量客户端请求建立连接 会导致服务端一致处于SYN_RCVD状态

服务端如何区分客服端建立连接的请求

可以对请求做唯一表示、如他们的ip地址

四次挥手

想要关闭应用程序 客户端向服务端发送关闭程序请求 客户端回应可以关闭 那么客服端与服务端连接的通道就会关闭 但是 服务端与客户端的通道仍然存在

一段时间以后 服务端也会向客户端发送关闭请求 客户端同意 通道断开

TIME WAIT在这个期间 服务端仍然可以向客服端发送数据

UDP协议

也成为不可靠协议 没有反馈机制 一段发送消息以后就不用惯了 不用等到对方回复就可以发送第二次消息

socket模块

socket是应用层与TCP/IP协议族通信的中间全歼抽象层 它是一组接口 在设计模式中 socket其实就是一个门面模式 他把复杂的TCP/IP协议族隐藏在sockrt接口后面 对用户来说 一组简单的接口就是全部 让socket去组织数据 以符合指定的协议

简而言之就是 socket帮我们把传输层及以下的处理全做了 调这个模块就能帮助我们通过网络完成两个程序之间的通讯

socket模块方法总结

socket():产生一个socket对象并指定采用的通信版本和协议

bind((ip,port)):绑定一个固定的ip和端口号 服务端必备

listen()设立半连接池 容量为5

connect((ip,port)):向服务端发送链接请求 并接受握手回馈信号(三次握手)

accept():等待客户端的连接请求 并发出握手回馈信号 (三次握手)

返回一个双向通道来send和recv 因为只有这个通道有客户端的地址信息

send(二进制数据):可以让建立协议的两端互相发送消息

recv(字节数):可以让建立协议的两端互相接收消息

代码优化

1.聊天内容自定义

​ 针对消息采用input获取

2.让聊天循环起来

​ 将聊天的部分用循环包起来

3.用户输入的信息不能为空

​ 本质其实就是两边不能都是recv或者cend 一定是一方收一方发

4.服务端多次重启可能会报错

​ Address already in use 主要是mac电脑汇报

​ 方式1:改端口号

​ 方式2:博客里面拷贝代码

5.当客户端异常断开的情况下 如何让服务端继续服务其他客人

​ windows服务端会直接报错

​ mac服务端会有一段时间反复接收空消息延迟报错

​ 异常处理、空消息判断

半连接池

server.listen(5)

当有多个客户端来链接的情况下 我们可以设置等待数量(不考虑并发)

假设服务端只有一个人的情况下

在测试半连接池的时候 可以不用input获取消息 直接把消息写死即可

TCP协议的黏包现象

黏包现象

当我们从客户端连续发送send内容 从执行recv来接收时 会发现 应该分三次发来的消息 全部被recv一次性接受了 并且黏在了一起 这就是粘包现象

粘包现象的原因

​ 流式协议

​ 流式协议就像水一样传输 没有间隔

​ 实际上 TCP 协议会针对数据量较小 且发送时间间隔较短的多条数据一次性合并打包发送

避免黏包现象的核心思路

​ 如何明确即将接收的数据具体有多大

黏包解决方案

可以在传输之前先确定文件的大小 这样接受放就知道在核实停止接收

需要用struct模块

struct模块:报头

​ 文件大小可以通过struct模块来固定长度 将这个固定长度有的数据发送给接收方 接收放可以提前准备

​ 借助struct模块 我们知道长度数字可以转换成一个转准的4字节的数字(也有固定别的长度的) 因此可以利用这个特点来预先发送数据长度

发送时 接收时
先发送struct转换好的数据长度4字节 先接受4个字节使用struct准换成数字来获取接收的数据长度
在发送数据 按照长度接收数据

TPC代码实现

 服务端
import json
import socket
import struct

server = socket.socket()
server.bind(('127.0.0.1', 8888))
server.listen(5)
sock, addr = server.accept()

length = struct.unpack('i', sock.recv(4))  # 得到文件信息长度
file_infos = json.loads(sock.recv(length[0]))  # 得到文件信息二进制并反序列化为数据
total_size = file_infos.get('file_size')  # 拿到数据大小
with open('现在是我的学习资料', 'wb') as f:
    recv_size = 0  # 初始接收了0字节的数据
    while recv_size < total_size:  # 当接收量不在小于文件总大小,则停止接收
        data = sock.recv(1024)  # 每次接收1024个字节
        f.write(data)  # 写入本地
        recv_size += len(data)  # 每次接收后,将接收到的数据量记录下来

# 客户端
import json
import os
import socket
import struct

client = socket.socket()
client.connect(('127.0.0.1', 8888))

file_size = os.path.getsize('学习资料')
file_infos = {
    'file_name': '电脑配件',
    'file_size': file_size,
}
bytes_infos = json.dumps(file_infos).encode('utf8')  # 数据先变成json再编码为二进制以便还原
client.send(struct.pack('i', len(bytes_infos)))  # 先发送文件信息的长度
client.send(bytes_infos)  # 再发送文件信息
with open('学习资料', 'rb') as f:
    for line in f:  # 流式协议,所以分几次发都是粘着的(但是这样可以减少发送端的内存压力)
        client.send(line)  # 最终发送文件本身

UDP代码实现

# 服务端
import socket

server = socket.socket(type=socket.SOCK_DGRAM)  # 建立了UDP协议的连接
server.bind(('127.0.0.1', 8080))  # 依然可以绑定某个端口号
while True:
    data, addr = server.recvfrom(1024)  # recvfrom接收udp协议的消息,返回信息和发送端的地址
    print(f'来自{addr}的消息:{data.decode("utf8")}')
    server.sendto('收到了'.encode('utf8'), addr)  # sendto(信息,接收端地址)
# 客户端
import socket

conn = socket.socket(type=socket.SOCK_DGRAM)  # 建立了UDP协议的连接

conn.sendto('我发送了个消息,你收到了吗'.encode(), ('127.0.0.1', 8080))
data, addr = conn.recvfrom(1024)
print(f'来自{addr}的回复:{data.decode("utf8")}')

操作系统的发展

1.穿孔卡片

​ 计算机很庞大 使用很麻烦 一次只能给一个人使用 期间很多计算机都不工作

2.联机批处理系统

​ 提前使用磁带一次性录入多个程序员编写的程序然后交给计算机执行

​ cpu工作效率提升 不用反复等到程序录入

3.脱机批处理系统

​ 极大的提升了cpu的利用率

多道技术实现方式

​ 1.切换

​ 计算机的cup在两种情况下会切换(不让你用 给别人用)

​ 1.程序有IO操作

​ 输出/输入

​ input、time.sleep、read、write

​ 2.程序长时间占用cup

​ 我们的雨露均沾 让多个程序都能被cpu运行一下

​ 2.保存状态

​ cpu每次切换走之前抖需要保存当前操作的状态 下次切换回来基于上次的进度继续执行

进程理论

进程与程序的区别

​ 程序:一对死代码(还没有被运行起来)

​ 进程:正在运行的程序(被运行起来了)

进程的调度算法

​ 进程的调度算法指操作系统面对多个进程时 如何让分配cpu时间的策略

​ 1.先来先服务的算法

​ 简单的策略 哪个进程先进来 就先执行完哪个进程

​ 缺点很明显 当遇到一个长时间的进程时 对短进程不友好

​ 2.短作业优先调度法

​ 也很粗暴 哪个进程执行的快就执行哪个进程 堆场进程又不友好

​ 3.时间片轮转法

​ 每次分配cpu的一个小时间段给某一个进程 到时间就保存当前进程状态和切 换另外一个进程

​ 这样分配 所有程序都不会等太久

​ 4.多级反馈队列

​ 在时间片轮转的基础上 这个程序如果每次都是因为时间片到时间了而不是IO被剥夺使用权 那就每次分配长一点的时间给这个程序

这样可以减少轮转的频率 也可以额昂各种程序都执行的相对连贯

进程的三种状态

就绪态:

​ 每当程序开始运行编程进程 都会进入这个状态 是等待cpu的使用权的状态

运行态:

​ 正被cpu执行的状态

​ 当程序结束会进入终止态

​ 当程序遇到IO会进入运行态

​ 当时间片到就会返回就绪态(cpu要分配给其他进程)

阻塞态:

​ 正在执行IO操作时 这时cpu就不会去选择执行这段进程

​ 当阻塞态结束会重新回到就绪态

创建态和终止态:进程开始和结束的状态

同步与非同步

在多进程并发中 我们认为 所有的今晨给都是有异步属性的 及当一个进程遇到IO等操作 这个进程不会进入到一个忙等待的状态(占用CPU却又等待IO的返回值)而是直接交出cpu的执行权给其他进程 让cpu去执行其他任务

同步就是指要等待IO或者其他的结果进程才会继续向前 类似于一个接一个的串行执行 大亨多个任务的在某种节点上的同步

阻塞与非阻塞

阻塞就是进程进入阻塞态 非阻塞就是程序处于就绪态和运行态

当一个任务因调用或IO要获取某个结果 阻塞调用必须等待调用结果 非阻塞调用则可以不必等待这个结果而使进程是中处于就绪态和运行态

异步非阻塞

当我们说一个程序是异步非阻塞的程序 实际上就是说这个程序很吃cpu的利用率 同时效率也是最高 因为这样的程序高并发 且有多个进程始终处于就绪态和运行态 所以cpu执行这个程序始终处于就绪态和运行态 所以cpu执行这个程序中的进程的时间就会比较多 执行速度就会很快 这种方式常应用于游戏行业

进程通信

进程之间的数据默认是隔离的 每个进程间的数据变量都存在于各自的名称空间中 不会互相影响

我们可以通过消息队列和文件操作等方式实现进程间的通信 让不同的进程能够对同一份数据进行操作

但是这样又可能出现数据错乱的问题

互斥锁

为了防止数据错乱的情况我们应该将操作数据变成一种同步状态 即当一个进程A操作数据时 另一个进程B就不应该操作这份数据了 而应该等着当前进程A处理完数据进程B再去操作这个数据 这样可以让我们的数据变得安全

lock.acquire() #进入区
读取文件中的数据进行操作 #临界区
lock.release() #退出区

进入区:所有需要操作数据的进程 都需要到这个区来等待数据的操 作权

​ 当判断没有进程进入临界区的时候 就取就最先进入的进程 进入临界区

​ 当判断已经有进程在临界区内 就要在进入区等待

临界区:同时只有一个进程可以进入临界区做一些处理数据的操作

退出区:是进程处理完数据 释放互斥锁的过程

代码创建进程的方式

同步与异步的区别

创建方式1:

from multiprocessing import Process
import time


def task(name):
    print('task is running', name)
    time.sleep(3)
    print('task is over', name)

if __name__ == '__main__':
    p1 = Process(target=task, args=('leethon',))  # 位置参数,用元组形式传
    # p1 = Process(target=task, kwargs={name:'leethon',})  # 关键字参数,用字典形式
    p1.start()  # 异步 告诉操作系统创建一个新的进程,并在该进程中执行task函数
    # task()  # 同步
    time.sleep(1)
    print('主进程')  # 当前进程继续执行它的程序

同步方式下 task和我们的主进程串行 必须等task执行完毕后 主进程才会执行:

# 同步运行结果:
task is running leethon
task is over leethon
主进程

异步方式下 task和我们的主进程并行 且大量时间处于阻塞态 所以可以看做task和主进程是并发的 同时运行的

# 异步运行结果
task is running leethon  # task起
主进程  # 一秒后主进程打印
task is over leethon  # 3秒后task结束

创建方式2:

from multiprocessing import Process
import time


class MyProcess(Process):  # 继承Process类
    def __init__(self, name, age):  # 如果要传参,则通过这种方式,调用类名时触发
        super().__init__()  # 派生原本的方法
        self.name = name  # 创建完一个进程后,添加新的属性(或者导致修改原有属性)
        self.age = age
        

    def run(self):  # 这就是方式1中的task,start时会运行其中的大爱吗
        print('run is running', self.name, self.age)  
        time.sleep(3)
        print('run is over', self.name, self.age)


if __name__ == '__main__':
    obj = MyProcess('leethon', 18)
    obj.start()  # 启动子进程
    time.sleep(1)
    print('主进程')
    
    
# 运行结果
run is running leethon 18
主进程
run is over leethon 18

进程join方法(异步转同步)

基本使用方式

from multiprocessing import Process
import time


def task(name, n):
    print('%s is running' % name)
    time.sleep(n)
    print('%s is over' % name)
    
if __name__ == '__main__':
    p = Process(target=task, args=('leethon1', 1))
	p.start()  # 异步
    p.join()  # 主进程代码等待子进程代码运行结束再执行
    print('主')

子进程的join方法是让主进程等待这个子进程结束后再继续执行后续操作 相当于将异步并发又变回了同步串行

from multiprocessing import Process
import time


def task(name, n):  # 传入几就停几秒
    print('%s is running' % name)
    time.sleep(n)
    print('%s is over' % name)


if __name__ == '__main__':
    p1 = Process(target=task, args=('leethon1', 1))
    p2 = Process(target=task, args=('leethon2', 2))
    p3 = Process(target=task, args=('leethon3', 3))
    start_time = time.time()  # 标记开始时间
	"""统计两种代码的运行时间"""
    print(time.time() - start_time)  # 得到总执行时间
    
##1  每启动一个进程后紧挨着join
    p1.start()
    p1.join()
    p2.start()
    p2.join()
    p3.start()
    p3.join()
    """运行结果:6s多,因为p1结束后才启动p2,相当于三个进程串行,所以一共1+2+3 = s"""
##2 先启动三个进程再最后统一join
	p1.start()
    p2.start()
    p3.start()
    p1.join()
    p2.join()
    p3.join()   
    """运行结果:3s多,p1和p2、p3几乎同时启动,那么join结束后统计的时间就是取决于最长的那一个"""

进程间数据隔离

from multiprocessing import Process
import time

money = 1000


def task():
    global money
    money = 666
    print('子进程的task函数查看money', money)


if __name__ == '__main__':
    p1 = Process(target=task)
    p1.start()  # 创建子进程
    time.sleep(3)  # 主进程代码等待3秒
    print(money)  # 100

上述程序中 我们在子进程看似更改了全局变量的money实际上我们联系底层 task的进程是通过导入模块的方式启动的 所以其实他改动的是他所在的模块的全局money而不会影响主进程的任何数据

这就是进程间的数据隔离

进程间通讯—IPC机制

IPC就是跨进程通信 为了能使多进程协调工作而让彼此隔离的进程有沟通的能力

消息队列

存储数据的地方 所有的主进程和子进程都能对队列进行存取

基础使用

from multiprocessing import Queue


q = Queue(3)  # 括号内可以指定存储数据的个数
# 往消息队列中存放数据
q.put(111)
print(q.full())  # 判断队列是否已满  # False
q.put(222)
q.put(333)
print(q.full())  # 判断队列是否已满  # True
# q.put(444)  # 程序卡在这里
"""实际上put会等待队列有空位,如果队列为满,那就等有一个数据被取走后,再往队列里放"""
# 从消息队列中取出数据
print(q.get())
print(q.get())
# print(q.empty())  # 判断队列是否为空  # False
print(q.get())
# print(q.empty())  # 判断队列是否为空  # True
# print(q.get())  # 队列为空时,等待队列有数据再取
print(q.get_nowait())  # 不等待,立刻取数据,没有就报错

"""
full() empty() 在多进程中都不能使用!!!因为可能出现判断失误的情况
"""

多进程中使用

from multiprocessing import Process, Queue


def product(q):  # 生产者
    q.put('子进程p添加的数据') 

def consumer(q):  # 消费者
    print('子进程获取队列中的数据', q.get())


if __name__ == '__main__':
    q = Queue()
    # 主进程往队列中添加数据
    # q.put('我是主进程添加的数据')
    p1 = Process(target=consumer, args=(q,))  # 将队列也作为参数传入
    p2 = Process(target=product, args=(q,))
    p1.start()
    p2.start()
    print('主')
# 执行结果
主
子进程获取队列中的数据

进程对象的其他方法

查看进程号、终止进程、判断进程存活

# 1.如何查看进程号
from multiprocessing import Process, current_process
current_process()  # <_MainProcess name='MainProcess' parent=None started
current_process().pid  # 18792
import os  # os 模块也可以查到
os.getpid()  # 18792
os.getppid()  # 2020  # 父进程的pid号
# 2.终止进程
p1.terminate()
'''
ps:计算机操作系统都有对应的命令可以直接杀死进程
如windows可以通过taskkill指令来杀死进程,通过help 指令名 可以查看win指令的详细用法
'''
3.判断进程是否存活
p1.is_alive()

守护进程

守护进程会随着守护的进程结束而立刻结束

代码中 提现为父进程结束后 那么开启的子进程也不会继续执行 直接结束

from multiprocessing import Process
import time


def task(name):
    print('子进程开始:%s' % name)
    time.sleep(3)
    print('子进程结束:%s' % name)


if __name__ == '__main__':
    p1 = Process(target=task, args=('leethon',))
    p1.daemon = True  # 放在子进程开启之前,表示守护主进程
    p1.start()
    time.sleep(1)
    print('主进程结束')
    
# 运行结果
子进程开始:leethon
主进程结束
# 没有子进程结束的打印

僵尸进程和孤儿进程

僵尸进程

进程执行完毕后不会立刻销毁所有的数据 会有一些信息短暂的保留下来

比如:进程号、进程执行时间、进程消耗功率等给父进程看

ps:所有的进程都会编程僵尸进程

孤儿进程

子进程正常运行 父进程意外死亡 操作系统针对孤儿进程会派遣'福利院'

标签:总结,__,task,name,网络,并发,print,进程,服务端
From: https://www.cnblogs.com/lzy199911/p/16908465.html

相关文章

  • Http2Bean——基于OkHttp与Gson的网络请求工具
    publicclassHttp2Bean<T>{privatefinalOkHttpClientclient=newOkHttpClient();privatefinalClass<T>clazz;privatefinalRequest.Builderreq......
  • java——多线程——并发与并行的了解以及区别
                     多线程的概念想要设计一个程序,边打游戏边听歌,怎么设计?得使用多进程或者多线程来解决.并发与并行并发:指两......
  • 实验四:神经网络算法实验
    【实验目的】理解神经网络原理,掌握神经网络前向推理和后向传播方法;掌握神经网络模型的编程实现方法。【实验内容】1.1981年生物学家格若根(W.Grogan)和维什(W.Wirth)发现了......
  • 第一届研究生网络安全大赛web部分writeup
    webBabyQL给了一个jar包,idea查看源码。publicclassAppController{publicAppController(){}@RequestMapping({"/"})publicStringindex(){......
  • 2022-2023-1 20221313《计算机基础与程序设计》第十二周学习总结
    2022-2023-120221313《计算机基础与程序设计》第十二周学习总结作业信息作业课程https://edu.cnblogs.com/campus/besti/2022-2023-1-CFAP作业要求https://ww......
  • 实验四:神经网络算法实验
    实验四:神经网络算法实验【实验目的】理解神经网络原理,掌握神经网络前向推理和后向传播方法;掌握神经网络模型的编程实现方法。【实验内容】1.1981年生物学家格若根(W......
  • 实验四:神经网络算法实验
    【实验目的】理解神经网络原理,掌握神经网络前向推理和后向传播方法;掌握神经网络模型的编程实现方法。【实验内容】1.1981年生物学家格若根(W.Grogan)和维什(W.Wirth)发现了......
  • 网络编程知识回顾
    软件开发架构C/S架构客户端与服务端作为服务端必备的条件; 24小时不间断提供服务 固定的IP地址 能够同时服务多个人 B/S架构网页端与服务器端两个架构的优劣......
  • 神经网络算法实验
    【实验目的】理解神经网络原理,掌握神经网络前向推理和后向传播方法;掌握神经网络模型的编程实现方法。【实验内容】1.1981年生物学家格若根(W.Grogan)和维什(W.Wirth)发现......
  • 实验四:神经网络算法
             ......