网络编程——知识梳理

网络并发知识点梳理

  • 软件开发架构

    软件目录结构:

    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数据的时候,需要加上''\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()
    

猜你喜欢

转载自www.cnblogs.com/pythonwl/p/12821107.html
今日推荐