网络编程——知识梳理
Posted pythonwl
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程——知识梳理相关的知识,希望对你有一定的参考价值。
网络并发知识点梳理
-
软件开发架构
软件目录结构:
bin -start 启动 conf -settings 配置 core -src 用户视图层 interface -interface 逻辑接口层 db -dbhandler 数据处理层 -models 类 在类的select方法,查找,返回数据,可以写成类方法。 -各种数据 lib -common 公共接口 log -日志文件 README -软件说明 注意:start文件可以写在外面,顶级目录的下面,这样就可以不用添加顶级目录到环境变量了,软件运行的环境。
-
互联网协议
OSI七层协议: 应用层:应用自身规定的协议:报头 + 数据;比如:http 这个是浏览器的应用协议 表示层 会话层 传输层:将信息加上 端口号 来确定计算机上的某个软件。比如:TCP(安全可靠) UDP(非安全可靠) 网络层:将信息加上 IP 来确定全球的某台计算机。 数据链路层:以太网协议,将信息加上报头,其中包括比如:Mac地址;与其配套使用的是:ARP协议 物理层:将信息转换为二进制,用电平信号,传输出去,所以说,是广播。
-
网路通信过程
1 要获得: ip地址 :确定计算机位置 子网掩码 :确定是否在一个局域网内 网关ip :用于互联网的通信,非局域网的通信 dns ip :将网址解析为ip地址 2 子网掩码和ip地址做按位与看是否相同,来判断是否是一个局域网的计算机。如果是,就直接通过以太网协议,ARP协议,ARP协议用来获得目标ip地址的Mac地址。然后发送。 注意:ip是可以理解成自动分配的,是随着不同的局域网变化的。而Mac地址是生产网卡的时候,就被厂商规定好的,唯一不变的物理地址。(网卡就是能够进行网络通信的硬件) 3 如果不是一个局域网的计算机,那就要将信息发给网关了,网关也是一个ip地址,也要通过Mac地址发送,ARP协议,然后,再让网关,和其它的局域网网关之间,进行通信,找到计算机,然后通信。 ARP协议:通过ip,获得目标地址的Mac地址。 4 网址:ip加端口 因为网址容易被人记住,而ip,不容易记住,所以dns就是帮你把网址解析成ip的,你只需要输入网址,就能上网了,dns服务器全球13台。本地的dns找不到,然后,再到dns服务器找。
-
TCP协议:三次握手四次挥手
图片
-
socket——套接字
用来实现网络通信。
socket,是一个位于应用层,和tcp/ip等层的,中间层。是一个抽象层。将各种协议封装起来,供应用层调用。
TCP 服务端:
import socket server = socket.socket(socket.AF_INET,socket.SOCK_STREAM) server.bind((‘123.0.0.1‘,7777)) server.listen(5) # 半连接池 conn,addr = server.accept() # 等待创建连接 conn.recv(1024) conn.send(data.encode(‘utf-8‘)) conn.close() server.close() 注意:TCP协议的服务端,流式协议。Windows系统的客户端非法断开连接,服务端会报错(异常处理,判断是否断开),Unix系统等,会接收到空。流式协议,一次接收不完的信息会在缓存中,下次继续接收,是安全可靠的协议,但是,也不知道什么时候结束,每一次发送你都不知道,信息有多少,所以,会出现粘包问题,解决:用协议的概念,加报头,先发送信息长度。
TCP 客户端:
import socket client = socket.socket(socket.AF_INET,socket.STREAM) client.connect((‘127.0.0.1‘,7777)) client.send(data) client.recv(1024) client.close()
UDP 服务端:
import socket server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) server.bind((‘127.0.0.1‘,7777)) data, addr = server.recvfrom(1024) server.sendto(data,addr) server.close() 注意:UDP协议,数据报协议。不安全可靠,一次发送接收到的信息,收到多少就是多少,没收到的就丢失了,所以,不会出现粘包问题。不用建立链接,也没有半连接池的概念。
UDP 客户端:
import socket client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) client.sendto(data,(‘127.0.0.1‘,7777)) data, addr = client.recvfrom(1024) client.close()
-
TCP黏包问题 定制固定长度的报头
TCP协议,流式协议,存在粘包问题。 解决方式:利用协议的概念:报头 + 数据 先发送,数据的长度 然后,开始发送数据,循环接收,直到接收到固定字节长度的信息。 有时候,报头较长,以字典的形式,可以将报头json,然后发送,这时候,报头长度又不确定了,所以,先发送报头的长度,然后接收报头,然后,从报头中获得数据相关信息,比如,数据的长度。 其中,TCP协议,传输涉及nagle算法:将数据量小,时间间隔短的结合为一个数据块进行接收。 struct模块:将一个数字转为固定长度的bytes类型。比如,把数据的长度,转为四个字节。 import struct struct.pack(‘i‘,111) # i 模式,参数,将 111 转为 4 个字节,当然别的参数可以改变字节数 struct.unpack(‘i‘,x) # 将四个字节的bytes,转为数字
实现并发的方式:socketserver模块,多进程,多线程(协程)
实现了网络通信,但是我们发现,只能一个人连接,,,
所以要实现多个人,并发连接。
-
socketserver模块
socketserver模块:可以实现多并发
import socketserver class Myserver(socketserver.BaseRequestHandler): def handle(self): pass # 这个方法必须实现,代码块可用的变量: # self.request 连接对象:conn # self.client_address 客户端地址 # self.server 服务器 server = socketserver.ThreadingTCPServer((‘127.0.0.1‘,7777),Myserver) server.serve_forever()
请求处理的基类是BaseRequestHandler,其中一般需要重写的方法就是handle方法,主要就是如何处理接下来的请求,在这个类里,主要有三个方法,一个是setup,handle和finish方法,在调用这个类的时候,先调用setup进行一些初始化的工作,然后调用handle方法进行处理请求,然后调用finish方法,做一些关闭连接什么的;在这个里面最主要的参数self.request,也就是请求的socket对象,其中可以发送消息sendall或者send,接收消息的recv
在进行sendall数据的时候,需要加上‘‘ ‘,表示此次发送的数据结束。
-
开启进程的两种方式
from multiprocessing import Process import os def task(): pass if __name__ == ‘__main__‘: p = Process(target=task,args=(,)) p.daemon = True # 守护进程 p.start() p.join() print(p.pid) # 查看进程pid print(os.getpid()) print(os.getppid())
from multiprocessing import Process class Myprocess(Process): def __init__(self,person): self.name = person super().__init__() def run(self): # 必须实现这个run方法,进行操作 pass p = Myprocess(‘egon‘) p.start() p.terminate() # 关闭进程不是立即关闭,需要一个过程 print(p.is_alive()) # 直接执行这个语句查看,是否运行,True,过一会进程关闭,False
-
互斥锁
from multiprocessing import Lock mutex = Lock() mutex.acquire() mutex.release()
-
生产者消费者模型
from multiprocessing import Process,JoinableQueue # 生产者和消费者之间传递信息需要一个媒介,比如队列。 生产者 + 消息队列 + 消费者 JoinableQueue 可以 q.join() # 等队列为空再往后执行
-
开启线程的两种方式
开启线程不需要在main下面执行代码 直接书写就可以
但是我们还是习惯性的将启动命令写在main下面
why:
因为开启线程是在当前进程下开启,不进行开辟内存空间,所以不会重新导入代码,也就不会像线程一样重新运行
from threading import Thread, active_count, current_thread def task(): pass if __name__ == ‘__main__‘: t = Thread(target=task) t.daemon = True t.start() t.join() print(active_count()) # 当前活跃的线程数 print(os.getpid()) # 当前进程的pid号 print(current_thread().name) # 获取线程名字
from threading import Thread class Mythread(Thread): def __init__(self,name): self.name = name super().__init__() def run(self): pass if __name__ == ‘__main__‘: t = Mythread(‘egon‘) t.start()
-
信号量和Event时间
from threading import Semphroe,Event from multiprocessing import Semphroe,Event sm = Semphroe(5) # 相当于锁最多可以有五个人用 sm.acquire() sm.release() event = Event() event.set() event.wait()
-
进程池线程池
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor pool = ProcessPoolExecutor(5) # 进程池为5 def task(): pass def call_back(n): # 该方法在本例中,作为参数传入,相当于对象方法,第一个参数默认self print(n.result()) # n : task if __name__ == ‘__main__‘: for i in range(20): res = pool.submit(task,x,y,z).add_done_callback(call_back) pool.shutdown() 注意:回调函数:异步提交任务,是为了主进程能继续执行下去,不用得到结果,回调函数,就是用来处理主进程的结果的。
-
协程的概念
单线程实现并发,多道技术的思想。 当遇到IO,就在代码层面实现切换。可以一直运行自己的进程,占用CPU,提高执行效率。 切换 + 保存状态(yield) 对于纯计算来说,并不能提升效率。 非协程:1.23 协程:1.12 def func1(): while True: 1 + 1 yield def func2(): g = func1() for i in range(100000): 1 + 1 next(g) func2() from gevent import monkey;monkey.patch_all() from gevent import spawn import time def func1(): print(1) time.sleep(1) print(2) def func2(): print(3) time.sleep(2) print(4) def func3(): print(5) time.sleep(3) print(6) g1 = spawn(func1) g2 = spawn(func2) g3 = spawn(func3) g1.join() g2.join() g3.join()
以上是关于网络编程——知识梳理的主要内容,如果未能解决你的问题,请参考以下文章