• 网络编程


    一、软件开发的架构

    1. C/S架构:
    Client(客户端)与Server(服务端)架构这种架构也是从用户层面(物理层面)划分的,
    客户端泛指客户应用程序EXE,对用户的电脑操作系统环境依赖较大

    2. B/S架构:
    Browser(浏览器端)与Server端架构,它隶属于C/S架构,Browser也是一种CLient,
    但不需要安装什么应用程序,只需要在浏览器上通过HTTP请求服务器端相关的资源(网页资源)

    3. B/S架构有点:
    统一了应用的入口(是一个趋势)
    网络编程基础

    二、通信

    1. 同一台电脑两个py文件通信:——文件 a.py———t.txt———b.py

    2. 早期: 联机(两台电脑用一根网线连接)

    3. 以太网:局域网与交换机(几十台电脑通信)

    1) 交换机通信:
    - 广播: 主机间‘一对所有’的通讯模式(吼一嗓子)
    - 单播: 方向固定的向某一个主机发送
    - 组播: 向某一部分或某一组主机发送

    2) 同一局域网在同一网段(192.168.12.xx)
    - 一个网段最多放256台电脑(0--255)
    - 局域网是指在某一区域内由多台计算机互联成的计算机组,一般是方圆几千米内

    3) ip地址与ip协议:
    - 规定网络地址的协议叫IP协议,他定义的地址称之为ip地址,广泛采用ipv4,他规定网络地址由32位2进制表示
    - 范围: 0.0.0.0----255.255.255.255
    一个IP地址通常写成四位点分十进制数字

    4) mac地址:
    - Ethernet规定接入Internet的设备都必须具备网卡,发送端和接收端的地址便是值网卡的地址,即mac地址
    - 每块网卡出厂时都被烧制上一个全世界唯一的mac地址,(物理地址)长度为48位2进制,通常由12位16进制数表示(前六位是厂商编号,后六位是流水线号)

    5) arp协议:
    - 地址解析协议,是根据IP地址获取物理地址的一个TCP/IP协议

    6) 子网掩码:
    - 计算机根据IP地址产生的表示网络特征的一个参数,形式上等同于IP地址,他的网络部分全部为1,主机部分全部为0.(255.255.255.0)

    7) 网段:
    - IP地址和子网掩码进行 &(与) 计算
    ip: 192. 168. 12. 84
    11000000. 101010000. 00001100. 01010100

             

    ===============> 11000000. 10101000. 00001100. 00000000(与0得0)
    ===========>网段:      192.         168.       12.         0
     


    子网: 255. 255. 255. 0
    11111111. 11111111. 11111111. 00000000



    8) 查看地址命令: ipconfig - all

    4. 多个局域网之间通信: 广域网: 交换机 + 路由器 + 代理ip
    1) 路由器(网关设备):
    - 连接因特网中各局域网,广域网的设备,路由器通过路由表解析去判断网络地址和选择ip路径,
    只接收源站或其他路由器的信息,一个路由器可以组成一个局域网
    - 访问百度: 主机找路由器 ——> 找代理ip ——> 通过代理ip找到百度的路由器

    三、TCP协议和UDP协议(发送数据的协议)

    - TCP应用: web浏览器、电子邮件、 文件传输
    - UDP应用: 域名系统(DNS)、视频流、IP语音

    1. 端口:
    - 计算机每一个程序启动都有一个随机且唯一的端口号(port)
    - 端口号范围(0-65535)
    - IP地址 + port ——> 唯一确定某个电脑上的某一程序。允许开发人员使用的端口号范围从8000开始(8000--10000)

    2. TCP协议:
    - 当应用程序希望通过TCP与另一个应用程序通信是,它会发送一个通信请求,这个请求必须被送到一个确切的地址,在双方“握手”
    之后,TCP将在两个应用程序之间建立一个全双工的通信,TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端

    — 建立连接三次握手 —— 数据传输 —— 断开连接四次挥手

    1) 三次握手:

    ① 客户端发送SYN(SEQ = x)报文给服务器端,进入SYN-SEND状态
    ② 服务器端收到SYN报文,回应一个SYN(SEQ=y)ACK(ACK=x+1)报文,进入SYN-RECV状态
    ③ 客户端收到服务期短的报文,回应一个ACK(ACK=y+1)报文,进入Established状态

    2) 四次挥手:(终止连接需四次挥手,这由TCP的半关闭造成)

    ① 某个应用程序首先调用close,称该端执行‘主动关闭’,该端的TCP于是发送一个FIN分节,表示数据发送完毕
    ② 接收到这个FIN的对端执行‘被动关闭’,这个FIN由TCP确认
    ③ 一段时间后,接收到这个文件结束符的应用进程将调用close关闭他的套接字,他的TCP也发送一个FIN
    ④ 接受这个最终FIN的原发送端TCP(执行主动关闭的那一端)确认这个FIN,

    - TCP三次握手 / 四次挥手
    
                socket客户端向服务端发起连接请求:三次握手
    
                            client.connect((......))
                        ---------------------------------------
                            客户端                  服务端
                               |                      |
                            能睡你吗
                                                    来啊
                            好的,我来了
                        ----------------------------------------
                            client.send("发送数据")
                            收发数据                收发数据
    
                客户端和服务端断开连接: 四次挥手
    
                                client.close() 或 conn.close()
                        ---------------------------------------
                            客户端                     服务端
                               |                         |
                            我要断开连接
                                                    断开就断开,等我处理一些事
                                                    。。。。
                                                    我处理完了,断开吧
                            拜拜
    
                补充:断开连接时,反映到代码上: 抛出异常/发送空内容
    
    
                

    3. UDP协议:
    - 当应用程序希望通过UDP与另一个应用程序通信时,传输数据之前源端和终端不建立连接,当它想传送时就简单的
    去抓取来自应用程序的数据,并尽可能快的把它扔到网络上
    4. 对比:
    - TCP:
    ① 传输控制协议,提供的是面向连接,可靠的字节流服务
    ② 建立全双工的通信(谁先通信都可以)
    ③ 不允许在同一时间点同时和多个客户端连接通信
    - UDP:
    ① 用户数据报协议,是面向数据报的传输层协议,不可靠
    ② 只能客户端先通信,服务器端才能知道地址找到客户端
    ③ 允许在同一时间点同时和多个客户端连接通信
    ④ 没有超时重发等机制,不用建连接,所以传输速度很快

    四、互联网协议与osi模型

    - 按功能不同分为osi七层模型或tcp/ip五层或tcp/ip四层
    - OSI七层模型

    7层:
    自己写的代码: 自己代码 + 框架

    - 应用层:py文件,https,http,使用软件
    - 表示层:看到数据,如图片和视频
    - 会话层:保持登录或链接状态

    socket模块:
    - 传输层:TCP/UDP协议,端口
    - 网络层:IP协议,IP地址,路由器,三层交换机
    - 数据链路层:MAC,arp协议,网卡,二层交换机
    - 物理层:网卡,电信号,hub集线器,将数据转换成电信号发送

    5层:
    - 应用层
    - 应用层 - 表示层
    - 会话层

    - 传输层
    - 网络层
    - 数据链路层
    - 物理层

    4层:
    - 应用层
    - 应用层 - 表示层
    - 会话层

    - 传输层
    - 网络层
    - 物理层 - 数据链路层
    - 物理层

    五、套接字(socket)

    - socket 位于应用层和传输层之间的虚拟层的接口,socket是应用层与TCP/IP协议簇通信的中间软件抽象层,
    它是一组接口,其实socket就是一个模块

    - 分类:
    - 基于文件类型的套接字家族,AF_UNIX(unix里一切皆文件)
    - 基于网络类型的套接字家族:AF_INET,现只使用网络的socket里的family参数

    六、套接字初使用  

      1)TCP
        - server端:

    import socket
    
    # 创建服务端socket对象
    server = socket.socket()
    
    # 绑定IP和端口
    server.bind(('127.0.0.1',8000))
    
    # 后面可以再等5个人
    server.listen()
    
    #等待客户端连接,如果没人来就一直死等着
    # conn是客户端和服务端连接的对象(伞),服务端以后要通过该对象进行收发数据
    # addr是客户端的地址信息
    conn,addr = server.accept()
    
    # 通过对象去获取(通过伞给我发送的消息)
    # 1024表示:服务端通过对象(伞)获取数据时,一次性做多拿1024字节
    data = conn.recv(1024)
    print(data)
    
    # 服务端通过连接对象(伞)给客户端回复了一条消息
    conn.send(b'stop')
    
    #与服务端断开连接(断开伞)
    conn.close()
    
    #关闭服务器的服务
    server.close()

        - client端

     
    import socket
    
    # 创建套接字
    client = socket.socket()
    
    # 向服务端发阿松连接请求(递伞)
    # 阻塞,去连接,直到连接成功才继续往下走
    client.connect(('127.0.0.1',8000))
    
    # 连接上服务端以后,向服务端发送消息
    client.send(b'love you')
    
    data = client.recv(1024)
    print(data)
    
    # 关闭自己
    client.close()


      2)UDP
        - server端:

    
    
    import socket
    sk = socket.socket()
    sk.bind(('127.0.0.1',8000))
    msg,addr = sk.recvfrom(1024)
    print(msg.decode('utf-8'))
    sk.sendto(b'hi',addr)
    sk.close()

        - client端:
    
    
    import socket
    
    ip_port = ('127.0.0.1',8000)
    
    sk = socket.socket(type=socket.SOCK_DGRAM)
    
    sk.sendto(b'hello',ip_port)
    
    msg,addr = sk.recvfrom(1024)
    
    print(msg.decode('utf-8'),addr)
    
    sk.close()
    
    
    
      3)解决UDP协议反复代码问题

    from socket import *
    
    class My_socket(socket):
    
        def __init__(self,coding='utf-8'):
            self.coding = coding
            super(My_socket,self).__init__(type=SOCK_DGRAM)
        
        def my_recv(self,num):
            msg,addr = self.recvfrom(num)
            return msg.decode(self.coding),addr
        
        def my_send(self,msg,addr):
            return self.sendto(msg.encoding(self.coding),addr)
    
    
    sk = My_socket()
    msg,addr = sk.my_recv(1024)
    
    
    

    七、远程执行命令

    import subprocess
    
    r = subprocess.Popen(
        'dir',shell=True,           # 告诉系统把我要执行的命令当坐系统命令来执行
        stdout = subprocess.PIPE,    # 正确的结果
        stderr = subprocess.PIPE)    # 错误的结果
    
    print(r.stderr.read().decode('gbk'))    # 读一次就没了,以后取不到了,
    print(r.stderr.read().decode('gbk'))    # 若想重复使用,需赋值给一个变量

    八、黏包(只有TCP有黏包,UDP永远不会黏包
      1. 成因:
        1)TCP协议的拆包机制:
          - 当发送端缓冲区的长度大雨网卡的MTU时,TCP会将这次发送的数据拆成几个数据包发送出去,MTU是网络上传送的最大数据包,
           单位是字节,大部分网络设备MTU是1500,如果本机的MTU比网关的MTU大,大的数据包就会被拆开来传送,这样会产生很多数据
           包碎片,增加丢包率,降低网速
        2)面向流的通信特点和Nagle算法
          - 发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法,(Nagle算法),将多次间隔较小且数据量小的数据,
           合并成一个大的数据块,然后进行封包,而接收端必须要有科学的拆包机制才能分辨,否则会黏包。
      2.现象:
        ① client连续发送少量数据,时间间隔短,Nagle算法进行合包,server端不知要接收多少,造成数据混乱

        ② client直接发送大量数据,但server端接收的长度不够造成数据混乱
      
      3. TCP和UDP区别:
        ① TCP基于数据流的,收发的消息不能为空,而UDP可以为空,会封装一个(ip+port)的消息头发送过去
        ② UDP最大发送数据长度为:65535 - IP头(20) - UDP(头)(8):65507字节。若大于此长度,会报错,而TCP不限制大小
      
      4. 黏包的解决方案
        - 发送端在发送数据之前,把自己将要发送的字节流总大小让接收端知晓,然后接收端死循环接受完所有数据
        - struct模块: 把一个类型,如数字,转成固定长度的bytes。
          - 发送时: 先发送struct转换好的数据,长度4字节,再发送数据
          - 接收时: 先接收4个字节使用struct.unpack转换成数字来获取要接收的长度,再按照长度接受数据

    b1 = struct.pack('i',123456)        # 4字节bytes长度
    
    b1 = struct.ubpack('i',b1)          # 转换回来
    
    
    

    九、socket的常用方法

    sk.bind()                       # 绑定到套接字
    sk.listen()                     # 监听
    sk.accept()                     # 阻塞等待连接
    sk.connect()                    # 主动初始化TCP服务器的连接
    sk.connect_ex()                 # connect()扩展版,出错时返回出错码,不报错
    sk.recv()                       # 接收TCP数据
    sk.send()                       # 发送TCP数据
    sk.sendall()                    # 发送TCP数据
    sk.recvfrom()                   # 接收UDP数据
    sk.sendto()                     # 发送UDP数据
    sk.getpeername()                # 连接到当前套接字的远端的地址
    sk.getsockname()                # 当前套接字的地址
    sk.getsockopt()                 # 返回指定套接字的参数
    sk.setsockopt()                 # 设置指定套接字的参数
    sk.close()                      # 关闭套接字
    sk.setblocking(True或False)     # 设置阻塞或非阻塞模式
    sk.settimeout()                 # 设置阻塞套接字操作的超时时间
    sk.gettimeout()                 # 得到()
    sk.fileno()                     # 套接字的文件描述符
    sk.makefile()                   # 创建一个与该套接字有关的文件
           
    十、验证客户端连接的合法性


    import hmac                             # 相当于md5
    
    name = '光头'
    
    pwd = b'taibai'
    
    md5_v = hmac.new(name.encode('utf-8'),pwd)
    
    r = md5_v.digest()                      # bytes类型
    
    

      - 将服务器加完盐的密文与客户端的密文进行一致性验证

    十一、socketserver


    - server端:

    import socketserver
    class Myserver(socketserver.BaseRequestHandler):
        def handlf(self):
            pass
            # 用self.request进行数据的收发
    
    if __name__ == '__main__':
        HOST,PORT = '127.0.0.1',9999
        # 设置allow_reuse_address允许服务器重用地址
    
        socketserver.TCPServer.allow_reuse_address = True
        server = socketserver.TCPServer((HOST,PORT),Myserver)
        server.serve_forever()
        # 让server永远执行,除非强制停止
    
    
     
    - client端:
     
    import socket
    
    HOST, PORT = "127.0.0.1", 9999
    data = "hello"
    
    # 创建一个socket链接,SOCK_STREAM代表使用TCP协议
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
    
        sock.connect((HOST, PORT))                         # 链接到客户端
        sock.sendall(bytes(data + "
    ", "utf-8"))        # 向服务端发送数据
        received = str(sock.recv(1024), "utf-8")         # 从服务端接收数据
    
    print("Sent:     {}".format(data))
    print("Received: {}".format(received))
    
    
     
  • 相关阅读:
    JavaBean对象与Map对象互相转化
    PowerDesigner V16.5 安装文件 及 破解文件
    eclipse get set 自动添加注释
    严重: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener
    java poi excel操作 把第一列放到最后去
    java poi excel操作 下拉菜单 及数据有效性
    maven 项目编译失败
    关于TbSchedule任务调度管理框架的整合部署
    mat 使用 分析 oom 使用 Eclipse Memory Analyzer 进行堆转储文件分析
    tomcat启动问题,卡在 preparing launch delegate 100% 的解决方法
  • 原文地址:https://www.cnblogs.com/-li926/p/9643685.html
Copyright © 2020-2023  润新知