标签:current tar 服务器 select person 生产者 sleep 算法 对象
软件开发架构
软件目录结构:
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模块:可以实现多并发
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数据的时候,需要加上‘‘\n‘,表示此次发送的数据结束。
开启进程的两种方式
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()
标签:current tar 服务器 select person 生产者 sleep 算法 对象
原文地址:https://www.cnblogs.com/pythonwl/p/12821107.html