• 网络编程——知识梳理


    网络并发知识点梳理

    • 软件开发架构

      软件目录结构:

      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()
      
  • 相关阅读:
    字节跳动软开校招岗
    众安保险软开校招岗
    topjui中combobox使用
    easyui中datagrid+layout布局
    jquery/js记录点击事件,单击次数加一,双击清零
    java图片上传及图片回显1
    java格式化
    java中的String整理
    删除window10没用的服务
    修改mysql默认端口
  • 原文地址:https://www.cnblogs.com/pythonwl/p/12821107.html
Copyright © 2020-2023  润新知