网络并发编程总结
网络编程
一.
1.网卡:
电脑中有网卡,网卡中有mac地址.
2.IP:
插上网线,路由器或交换机中的DHCP服务会自动分配IP地址.
IP:192.168.13.150
IPv4:
00000000.00000000.00000000.00000000
0-255 0-255 0-255 0-255
IPv6:
00000000.00000000.00000000.00000000.00000000.00000000
3.子网掩码:255.255.255.0
IP:192.168.13.150
子网掩码:255.255.255.0
4.网关:路由器中连接交换机的口
网关IP:第一个IP地址
5.DNS:
网络连接:
1.域名解析:
Windows电脑:先去本地的hosts文件中找IP
C:Windows\System32\drivers\etc\hosts
Linux/Mac电脑:
/etc/hosts
2.连接:
sk = socket.socket()
sk.connect(("127.0.0.1",8000))
3.如果创业:
1.租一个服务器+公网IP
2.租域名:域名+IP解析
总结:
IP:4个点分的十进制表示
DHCP:自动为局域网内中的电脑分配IP
网关:路由器中连接交换机的口
子网掩码:将挡住的IP位数作为网段,未挡住的部分作为可变的值
IP:192.168.13.15.
子网掩码:255.255.255.0
局域网,城域网,广域网
arp协议:通过目标的IP地址,查找目标的mac地址
DNS
二.
1.OSI模型:
7层模型:
应用层:使用软件
表示层:看到数据,如图片,视频
会话层:保持登录或链接状态
传输层:TCP/UDP
网络层:IP
数据链路层:MAC
物理层:将数据转换成电信号发送
5层模型:
应用层:应用层,表示层,会话层
传输层
网络层
数据链路层
物理层
4层:
应用层:应用层,表示层,会话层
传输层
网络层
物理层L数据链路层,物理层
2.三次握手四次挥手
#三次握手:
1.客户端(Client)向服务端(Server)发送一次请求
2.服务端确认并回复客户端
3.客户端检验确认请求,建立连接
#四次挥手:
1.客户端向服务端发一次请求
2.服务端回复客户端(断开客户端-->服务端)
3.服务端再次向客户端发请求(告诉客户端可以断开了)
4.客户端确认请求,连接断开
断开连接时,反映到代码上就是抛出异常或发送空内容
三.
1.BS(Browser/Server)和CS(Client/Server)架构
BS:浏览器/服务器
CS:客户端/服务器
a.CS架构是建立在局域网的基础上的,BS架构是建立在广域网的基础上的
b.CS一般比BS安全
c.CS更加注重流程,BS更加注重访问速度
d.BS比CS能更好的重用
e.CS比BS系统维护周期长,开销大
f.CS多是建立在Windiws平台上,BS建立在浏览器上.
四.
1.socket代码:
1 import socket 2 服务端: 3 server = socket.socket() 4 server.bind(("127.0.0.1",8000)) 5 server.listen(5) 6 conn,addr = server.accept() 7 8 客户端: 9 client = socket.socket() 10 client.connect(("127.0.0.1",8000))
2.黏包:
1.发送数据时,每次发送的包小,因为系统进行优化算法,就将两次的包放在一起发送,减少了资源的重复占用,而客户端接收时,会一次全部接收
2.接收数据时,多次接收,第一次接收的数据量小,导致数据还没接收完,就停下了,剩下的数据会缓存在内存中,然后等到下次接收时和下一波数据一起接收.
解决方案:
1.先发送一个字节总大小,然后接收端循环接收所有数据.
2.使用time模块
3.先使用struct模块发送4个字节的文件大小,然后再发送数据.
3.协议:
自定义协议:{"code":1001,"data":{...}}
HTTP协议:GET /swd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n
并发编程
1.线程,进程,协程的区别:
2.进程:
1 import multiprocessing 2 3 def task(arg): 4 print(arg) 5 6 def run(): 7 p = multiprocessing.Process(target=task,args=(1,)) 8 p.start() 9 10 if __name__ == '__main__': 11 run()
获取当前进程:name = multiprocessing.current_process()
进程间的数据共享:
1 1.Queue 2 3 import multiprocessing 4 5 q = multiprocessing.Queue() 6 7 def task(arg,q): 8 q.put(arg) 9 10 def run(): 11 for i in range(10): 12 p = multiprocessing.Process(target=task,args=(i,q)) 13 p.start() 14 15 while True: 16 v = q.get() 17 print(v) 18 19 2.Manager 20 21 import multiprocessing 22 23 def task(arg,dic): 24 dic[arg] = 100 25 26 if __name__ == '__main__': 27 m = multiprocessing.Manager() 28 dic = m.dict() 29 process_list = [] 30 31 for i in range(10): 32 p = multiprocessing.Process(target=taskargs=(i,dic)) 33 p.start() 34 process_list.append(p) 35 36 while True: 37 count = 0 38 for p in process_list: 39 if not p.is_alive(): 40 count += 1 41 if count == len(process_list): 42 break 43 print(dic)
进程锁:
1 import time 2 import multiprocessing 3 4 lock = multiprocessing.RLock() 5 6 def task(arg): 7 print(111) 8 lock.acquire() 9 print(arg) 10 lock.release() 11 12 if __name__ == '__main__': 13 p1 = multiprocessing.Process(target=task,args=(1,)) 14 p1.start() 15 16 p2 = multiprocessing.Process(target=task,args=(2,)) 17 p2.start()
进程池
1 from concurrent.futures import ProcessPoolExcutor 2 3 def task(arg): 4 print(arg) 5 6 if __name__ == '__main__': 7 pool = ProcessPoolExcutor(5) 8 for i in range(10): 9 pool.submit(task,i)
3.线程
1 import threading 2 3 def task(arg): 4 print(arg) 5 6 for i in range(10): 7 t = threading.Thread(target=task,args=(i,)) 8 t.start() 9 10 获取当前线程 11 ct = threading.current_thread() 12 13 获取当前线程名字: 14 name = threading.getName()
线程锁:
1 Lock:一次放一个,只能锁一次 2 RLock:一次放一个,可以锁多次 3 BoundedSemaphore(n):一次放n个 4 Condition:一次放指定个 5 Event:一次放所有 6 7 import threading 8 9 lock = threading.RLock() 10 11 def task(arg): 12 lock.acquire() 13 print(arg) 14 lock.release() 15 16 for i in range(10): 17 t = threading.Thread(target=task,args=(i,)) 18 t.start()
线程池
1 from concurrent.futures import ThreadPoolExcutor 2 3 def task(arg): 4 print(arg) 5 6 pool = ThreadPoolExcutor(5) 7 for i in range(10): 8 pool.submit(task,i)
4.协程
1 import greenlet 2 3 def f1(): 4 print(111) 5 g2.switch() 6 print(222) 7 g2.switch() 8 9 def f2(): 10 print(333) 11 g1.switch() 12 print(444) 13 g1.switch() 14 15 g1 = greenlet.greenlet(f1) 16 g2 = greenlet.greenlet(f2) 17 g1.switch()
协程+IO
1 from gevent import monkey 2 monkey.patch_all() 3 import requests 4 import gevent 5 6 def get_page1(url): 7 ret = requests.get(url) 8 print(ret.content) 9 10 def get_page2(url): 11 ret = requests.get(url) 12 print(ret.content) 13 14 def get_page3(url): 15 ret = requests.get(url) 16 print(ret.content) 17 18 gevent.joinall( 19 [ 20 gevent.spwan(get_page1,'https://www.python.org/'), 21 gevent.spwan(get_page1,'https://www.yahoo.com/'), 22 gevent.spwan(get_page1,'https://github.com/'), 23 24 25 ] 26 27 28 )
threading.local的作用:为每个线程开辟一个空间进行数据存储,自己通过字典创建一个类似于threading.loacl
requests模块模拟浏览器发送请求:
本质:
requests.get(url)
创建socket客户端
连接,阻塞
发送请求
接收请求,阻塞
断开连接
同步异步:
同步过程中进程触发IO操作并等待或者轮询的去查看IO操作是否 完成
异步过程中进程触发IO操作后直接返回做其他事情
阻塞非阻塞:
应用请求IO操作时,需要等待就是阻塞,请求立即返回就是非阻塞.
阻塞 -> 非阻塞:
client.setblocking(False)
会报错,需要try
try:
pass
except BlockingIOError as e:
pass
什么是异步非阻塞?
非阻塞:不等待,比如创建socket对某个地址进行connect,获取接收数据recv默认都会等待(连接成功或接收到数据),才执行后续操作.如果设置setblocking(False),以上两个过程就不会再等待,但是会报BlockingIOError错误,只要捕获即可
异步:通知,执行完成之后自动执行回调函数或自动执行某些操作.
什么是同步阻塞?
阻塞:等待
同步:按照顺序逐步执行.
IO多路复用:
IO多路复用分为时间上的复用和空间上的复用
空间上的复用是将内存分为几部分,每一部分放一个程序,这样同一时间内存中就有多道程序.
时间上的复用是指多个程序在一个CPU上运行,不同的程序轮流使用CPU.
当某个程序运行时间过长或者遇到IO操作,操作系统会把CPU分配给下一个程序,保证CPU处于高使用率,实现伪并发.
IO多路复用作用:检测socket是否已经发生变化(是否已经连接成功/是否已经获取到数据)(可读/可写)
操作系统检测侧socket是否发生变化有三种模式:
select:最多1024个socket,循环去检测
pool:不限制监听socket个数,循环去检测(水平触发)
epool:不限制监听个数,回调方式(边缘触发)