python——通信原理,进程与线程

一、网络编程

1.计算机网络

  • 将地理位置不同的具有独立功能地多台计算机及其外部设备,通过通信线路连接起来,在协议的管理和协调下,实现资源共享和信息传递。

网络编程:用来实现网络互连的不同计算机运行程序可以进行数据交换

2.网络模型

计算机网络之间以何种规则进行通信。分为OSI模型和TCP/IP参考模型

2.1OSI:
  • 应用层:电脑屏幕看到的东西,产生数据。
  • 表示层:做数据进行处理,加密与解密,压缩与解压,编码。
  • 会话层:通过传输层建立数据传输的通路,发起会话和接受会话,在传输过程维持会话
  • 传输层:数据连接与传输,用一个寻址机制标识一个特定的应用程序(端口)——分成数据段
  • 网络层:基于网络层地址(ip地址)进行不同网络系统间的路径选择——进行数据的分割与重新组合,形成新的数据包
  • 数据链路层:数据进行mac地址(网卡地址)封装与解封装,使用接收系统的硬件地址和物理地址来寻址,承上启下。——数据帧
  • 物理层:物理设备:光纤,同轴电缆,双绞线,网卡,中继器,集线器等——比特数据
2.2TCP/IP:
  • 应用层——应用层,表示层,会话层合并——http:超文本传输协议,web服务器之间传输数据的协议,ftp:文件传输协议。smtp:简单邮件传输协议,pop:邮件协议,telent:远程登录协议。DNS:域名系统,域名与IP地址的转换。
  • 传输层
  • 网络层
  • 数据链路层——数据链路层,物理层合并

双方要遵守相同的协议,才能进行信息交流

3.传输层

3.1TCP
  • 通过三次握手建立稳定链接。应用数据分割成最合适的数据进行发送
  • 连接可靠,不易出现乱序,丢失等现象
  • 连接和校验需要时间,效率低下

三次握手

  1. 客户端发送SYN报文给服务端,进入SYN——SEND状态
  2. 服务端接收到报文,回应一个SYN ACK报文,进入SYN——RECV状态
  3. 客户端接收到服务端地SYN报文,回应一个ACK报文,进入连接状态。
3.2UDP
  • 一次性发送所有的协议,可能出现数据丢失现象。

4.网络层

有网关,路由器

  • IP地址其实是虚拟地址,会通过ARP协议转换为网卡地址
  • 计算机只能识别二进制的数据,IP是一个二进制数据。IPV4长度为32位,分为4段,十进制标识。
  • IPV6长度是IPV4的四倍,以16进制标识,分为八段,

IP地址:有网络号段和主机号段。127.0.0.1:本机地址,0.0.0.0:任意地址。ping IP地址:测试与该IP地址是否相同

端口是软件的身份证,用来区分不同的软件,最大时2的16次方减一

#服务端配置
import socket
server = socket.socket()
server.bind(('127.0.0.1',8989))	#绑定要运行的服务端的IP地址,端口任意设置
server.listen(10)	#设置最大监听数量
result = server.accept()	#创建连接通道,获取对等套接字,返回的是一个元组,第一个是对等连接套接字,第二个是客户端的IP加上端口
conn,addr = result #拆包
conn.recv(1024)	#设置最大接收字节数
conn.send(b'返回')	#返回给客户端数据

#数据要对等	
conn.close()
server.close()

#客户端配置
import socket
client = socket.socket()
client.connect(('127.0.0.1',8989))	#连接服务器开辟出的端口地址
client.send(b'hello')	#发送字节数据
client.close()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h2okL5dq-1635859188331)(C:\Users\admin\AppData\Roaming\Typora\typora-user-images\image-20211020212829055.png)]

  • 传输的必须是字节数据——字符串转字节bytes(info,encoding=‘utf8’)

二、I/O多路复用

1.数据流

  • 定义:是一组有序的, 有起点和终点的字节的数据序列,包括输入流和输出流。
  • 输入流只能读,不能写,从键盘或文件中获取数据,输出流只能写,不能读,向显示器打印机等写入东西。第一人称是机器。
  • I/O——在信息的交换过程中,对这些流进行数据的收发操作。fd就是文件描述符,一个整数,就是对整数的操作。

2.I/O交互

  • 用户空间——用户程序和应用程序以及代码
  • 内存空间——内核空间,操作系统和驱动程序
  • 硬盘空间

3.基本IO模型

IO操作以何种规则进行操作,分为阻塞io,非阻塞io,多路复用io

3.1阻塞IO模型

阻塞IO就是专心做一件事情,阻塞的时候不做其他事情,做完这一件事情,才会去做其他事情。

  • 优点:进程阻塞挂起,不会消耗cpu资源,能够及时响应每个操作,难度低
  • 缺点:不适合并发量大的应用开发。进程阻塞时不能进行其他操作
#阻塞IO,同时建立两个套接字
import socket

server = socket.socket()
server.bind('127.0.0.1',8989)
server.listen(10)

all_conn = []

for i in range(2):
    conn,addr = server.accept()
    all_conn.append()


for conn in all_conn:
    #得到所有的对等连接套接字
    data = conn.recv(1024)
    if data:
        print(data)
    else:
        
        all_conn.remove(conn)
    conn.close()
server.close()
3.2非阻塞IO模型
  • 优点:在等待线程的时候去做其他任务,效率高。
  • 缺点:消耗大,轮询加延迟,而任务可能就在中间任意时间完成
#非阻塞IO
# -*- coding: utf-8 -*-
# @Time: 2021/10/29 19:19
# @Author: 枫无痕
# @Email: [email protected]
# @File: server01.py
# @Software: PyCharm
import socket
from time import sleep
server = socket.socket()

#设置为非阻塞套接字,需要i在其他操作之前设置
server.setblocking(False)

server.bind('127.0.0.1',8989)
server.listen(10)

# while True:
#     try:
#         conn,addr = server.accept()
#         conn.recv(1024)
#     except BlockingIOError:
#         print("做其他事情")
#         sleep(1)
all_conn = []
while True:
    try:
        conn,addr = server.accept()
        conn.setblocking(False) #将对等套接字设置为非阻塞接字
        all_conn.append(conn)
    except BlockingIOError:
        pass
    except Exception as E:
        print(f"发生了未知异常{E}")

    #接收数据
    for conn in all_conn:
        try:
            data = conn.recv(1024)
            if data:
                print(data)
                conn.send(data)
            else:
                conn.close()
                all_conn.remove(conn)
        except BlockingIOError:
            pass
        except Exception as E:
            print(f"发生了未知异常{E}")
  • 同一时间段发生多个事情叫做并发,同一时刻只有一条指令执行,但是是快速的切换执行,宏观是一起执行的,微观不是同时执行的。
  • 同一时间点发生多件事情叫做并行。连接客户端和接受数据是并发执行的。
3.3IO多路复用模型

雇佣人来查看与有没有上钩

  • 优点:占用资源少,消耗CPU少
  • 缺点:需要两个系统调用

原理:select/epoll这个function会不断轮询所负责的socket,有数据之后就会通知用户。

epoll是最佳的打工仔,是一个惰性事件回调,用户自己去调用,是linux最好的IO多路复用器,但是只有linux有。

#多路复用
import socket,selectors #IO多路复用选择器模块,

epoll_select = selectors.EpollSelector()    #实例化
selectors.DefaultSelector() #默认选择器,自动根据不同操作系统选择
server = socket.socket()
server.bind('127.0.0.1',8989)
server.listen(10)

def f_accept(server):
    conn,addr = server.accept() #生成对等连接套接字
    epoll_select.register(conn, selectors.EVENT_READ, f_recv)


def f_recv(conn):
    data = conn.recv(1024)
    if data:
        print(data)
    else:
        conn.close()

#selectors.EVENT_READ 有事件发生时,调用回调函数,第三个参数
epoll_select.register(server,selectors.EVENT_READ,f_accept)

while True:
    events = epoll_select.select()   #查询,返回的是一个列表,保存一个元组,
    for key,mask in events:
        func = key.data     #函数体
        conn = key.fileobj  #已经注册的套接字
        func(conn)      #调用f_accept方法。

三、进程与线程

1.CPU时间片

  • cpu分配给每个程序的时间,每个线程被分配一个时间段,成为时间片
  • 单核CPU只能实现并发,要想真正实现并行,需要多核CPU

2.进程

  • 正在运行的程序。一个进程产生一块独立的空间,进程之间相互独立,互不影响
  • 时间戳:格林威治时间1970年1月1日0时0分0秒。time.time()——得到时间戳
  • time.localtime(time.time())——得到当前的时间。

单进程:就是平常的执行顺序

多进程:就是如下所示

import time
import multiprocessing

def new_time():
    return time.asctime(time.localtime(time.time()))


def func(x,y):
    print(x+y)
    print('func_start',new_time())
    time.sleep(5)
    print('func_end', new_time())

print('main_start',new_time())
p1 = multiprocessing.Process(target=func,args=(1,2))
p1.start()
time.sleep(5)#模拟耗时操作,
print('main_end',new_time())
#一共耗时5秒钟。
  • target:表示该进程需要执行的任务,并让子进程执行他所需要的任务。args:需要的是元组类型,并进行参数传递。
  • 进程数多于核心数是没有效果的,cpu几核的就是分担几个耗时任务

3.线程

  • 一个程序可以有多个进程,每个进程可以有多个线程,但是至少有一个进程和线程。
  • 线程共享同个地址空间。

多线程——跟多进程模块是差不多的。

import time
import threading

def new_time():
    return time.asctime(time.localtime(time.time()))

def func(x,y):
    print(x+y)
    print('func_start',new_time())
    time.sleep(5)
    print('func_end', new_time())

print('main_start',new_time())
p1 = threading.Thread(target=func,args=(1,2))
p1.start()
time.sleep(5)#模拟耗时操作,
print('main_end',new_time())
  • 多线程不适合解决计算密集型的情形,但是适合IO密集型的场景
  • 因为遇到阻塞会实行自动切换。

GIL全局解释锁——让任何一个进程中只有一个线程去执行。相当于是并发。

4.实现服务器并发

4.1多进程实现并发服务器
import socket
import multiprocessing
'''
主进程左连接客户端,生成对等套接字,通道连接
创建一个子进程,接收客户端
'''
def f_recv(conn):
    while True:
        data = conn.recv(1024)
        if data:
            print(data)
            conn.send(data)
        else:
            conn.close()
            break



def f_accept():
    sever = socket.socket()
    sever.bind(('127.0.0.1',4546))
    sever.listen(10)

    while True:
        conn,addr =sever.accept()
        p1 = multiprocessing.Process(target=f_recv,args=(conn,))
        p1.start()
if __name__=='__main__':
    f_accept()

四、进程与线程补充

1.join方法

  • 进程或者线程添加join方法后,会等待子任务结束,如果没有结束会一直阻塞,直到子任务结束。
  • 一般是放在最后面的。
  • 等待子任务结束,进程与线程都有的
import time
import multiprocessing

def new_time():
    return time.asctime(time.localtime(time.time()))


def func(x,y):
    print(x+y)
    print('func_start',new_time())
    time.sleep(5)
    print('func_end', new_time())

print('main_start',new_time())
p1 = multiprocessing.Process(target=func,args=(1,2))
p1.start()
p1.join()   #等待子任务执行完毕,会进行阻塞
time.sleep(5)#模拟耗时操作,
print('main_end',new_time())

2.获取当前进程

import time
import multiprocessing

def new_time():
    return time.asctime(time.localtime(time.time()))


def func(x,y):
    print(x+y)
    print('func_start',new_time())
    time.sleep(5)
    print('func_end', new_time())
if __name__ == '__main__':
    print(multiprocessing.current_process())    #获取当前进程对象
    print('main_start',new_time())
    p1 = multiprocessing.Process(target=func,args=(1,2))
    p1.start()
    p1.join()   #等待子任务执行完毕,会进行阻塞
    time.sleep(5)#模拟耗时操作,
    print('main_end',new_time())

3.获取子进程的名字

import time
import multiprocessing

def new_time():
    return time.asctime(time.localtime(time.time()))


def func(x,y):
    print(x+y)
    print('func_start',new_time())
    time.sleep(5)
    print('func_end', new_time())
if __name__ == '__main__':
    print('main_start',new_time())
    p1 = multiprocessing.Process(target=func,args=(1,2))
    print(p1.name)
    p1.name='进程一号'
    print(p1.name)
    p1.start()
    time.sleep(5)#模拟耗时操作,
    print('main_end',new_time())

4.终止任务

  • 可以在主进程结束之后,强制结束子进程
  • 但是线程不能终止,只能等待结束。
import time
import multiprocessing

def new_time():
    return time.asctime(time.localtime(time.time()))


def func(x,y):
    print(x+y)
    print('func_start',new_time())
    time.sleep(5)
    print('func_end', new_time())
if __name__ == '__main__':
    print('main_start',new_time())
    p1 = multiprocessing.Process(target=func,args=(1,2))
    p1.start()
    time.sleep(5)#模拟耗时操作,
    
    p1.terminate()  #终止p1子进程
    print('main_end',new_time())

5.进程的PID

  • 在Linux1中,只要进程一创建,系统就会分配一个PID,在运行过程中,PID不会改变。
  • 可以通过PID查看进程对资源的使用情况,也可以控制进程的运行.
  • 进程的pid是不会改变的,只有在start后才能有pid
import time
import multiprocessing

def new_time():
    return time.asctime(time.localtime(time.time()))


def func(x,y):
    print(x+y)
    print('func_start',new_time())
    time.sleep(5)
    print('func_end', new_time())
if __name__ == '__main__':
    print('main_start',new_time())
    p1 = multiprocessing.Process(target=func,args=(1,2))
    print('before start',p1.pid)
    p1.start()
    print('after start', p1.pid)
    time.sleep(5)#模拟耗时操作,
    p1.terminate()  #终止p1子进程
    print('main_end',new_time())

6.线程的ident

  • 类似于操作系统中进程的pid,是python解释器分配的。
  • 进程是操作系统分配的。

7.生存周期

import time
import multiprocessing

def new_time():
    return time.asctime(time.localtime(time.time()))


def func(x,y):
    print(x+y)
    print('func_start',new_time())
    time.sleep(5)
    print('func_end', new_time())
if __name__ == '__main__':
    print('main_start',new_time())
    p1 = multiprocessing.Process(target=func,args=(1,2))
    print(p1.is_alive())
    p1.start()
    print(p1.is_alive())
    p1.join()
    print(p1.is_alive())
    time.sleep(5)#模拟耗时操作,
    print('main_end',new_time())

8.守护模式

  • 开启守护模式之后,主进程结束,子进程自动结束。
import time
import multiprocessing

def new_time():
    return time.asctime(time.localtime(time.time()))


def func(x,y):
    print(x+y)
    print('func_start',new_time())
    time.sleep(5)
    print('func_end', new_time())
if __name__ == '__main__':
    print('main_start',new_time())
    p1 = multiprocessing.Process(target=func,args=(1,2),daemon=True)    #开启守护模式
    p1.start()
    time.sleep(5)#模拟耗时操作,
    print('main_end',new_time())

五、面向对象编程(代码)

  • 自定义进程对象,完成多进程并发操作redis数据库。
  • redis高速存储数据库,并发操作。
import redis
import multiprocessing
"""
自定义:
    通过class关键字定一个类
    要继承继承类
"""
class RedisProcess(multiprocessing.Process):
    def __init__(self,db,key,values):
        super().__init__()#调用父类的方法,创造子进程,实例化创建进程
        self.connect = redis.StrictRedis(db=db) #本地连接,只需要指定一个数据库就可以
        self.key = key
        self.values = values
    def set(self):
        self.connect.set(self.key,self.values)
    def get(self):
        return self.connect.get(self.key)
    def run(self):
        self.set()
        self.get()

你可能感兴趣的:(Python遗忘知识点,python,网络,网络协议)