zoukankan      html  css  js  c++  java
  • 网络编程——TCP协议、UDP协议、socket套接字、粘包问题以及解决方法

    网络编程——TCP协议、UDP协议、socket套接字、粘包问题以及解决方法

    TCP协议(流式协议)

    ​ 当应用程序想通过TCP协议实现远程通信时,彼此之间必须先建立双向通信通道,基于该双向通道实现数据的远程交互,该双向通道直到任意一方主动断开才会失效

    TCP协议的“三次握手” 和 “四次挥手”

    三次握手 建连接

    ​ 1、客户端向服务端发送建立连接请求

    ​ 2、服务端返回收到请求信息,同时向客户端发送连接请求

    ​ 3、客户端接收到服务端发来的请求,返回连接成功给服务端,完成双向连接

    建立好连接后,会有一个反馈机制

    ​ 客户端往服务器发送请求,服务端必须返回响应,告诉客户端收到请求了,并且将服务端的数据一并返回给客户端

    ​ C ----> S :一次请求,必须有一次响应

    优点

    ​ 数据安全

    缺点

    ​ ①:传输速度慢

    ​ ②:洪水攻击

    ​ 指的是通过伪造大量的请求,往对方服务器发送请求,导致对方服务器响应跟不上,以至于瘫痪

    ​ 半连接池listen:限制用户在同一个时间段内的访问数量

    四次挥手 断连接

    ​ 1、客户端向服务端发送断开连接的请求

    ​ 2、服务端返回接收到请求的信息给客户端

    ​ 3、服务端确认所有数据接收完成以后,再发送同意断开连接的请求给客户端

    ​ 4、客户端返回收到断开连接的请求,给服务端

    UDP协议

    UDP协议也被称之为数据包协议

    UDP协议的特点:

    ​ ①:不需要建立连接

    ​ ②:不需要知道对方是否收到

    ​ ③:数据不安全,会丢包

    ​ ④:传输速度快

    ​ ⑤:能支持并发

    ​ ⑥:不会粘包

    ​ ⑦:无需先启动服务端再启动客户端

    UDP的优点:

    ​ ①:传输速度快

    ​ ②:能支持并发

    ​ ③:不会粘包

    UDP的缺点:(致命缺点)

    数据不安全,容易丢失

    UDP的应用场景:早期的QQ聊天室

    socket套接字通信

    什么是socket?

    socket是一个模块,又称套接字,用来封装互联网协议(应用层以下的层)

    为什么要有socket?

    socket可以实现互联网协议应用层以下的层 的工作

    提高开发效率

    怎么使用socket?

    # server.py文件
    import socket
    sever = socket.socket()
    sever.bind(("127.0.0.1", 9527))
    # 半连接池
    sever.listen(5)     # 最多接受5个客户端发送的请求,实际上是6个
    # conn:指的是服务端网客户端的通道
    coon, addr = sever.accept()
    # 接收客户端发送过来的消息
    ret = coon.recv(1024)   # 一次可接受1024bytes的数据
    print(ret)
    # 关闭服务器
    coon.close()
    
    
    # client.py文件
    """
    启动服务器后再启动客户端
    """
    import socket
    
    client = socket.socket()
    
    client.connect(("127.0.0.1", 9527))
    # 必须发送bytes类型的数据
    # # 方式一
    # client.send('hello'.encode('utf-8'))
    # 方式二
    client.send(b'hello')
    

    粘包问题

    TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾。

    问题一:无法确认对方发送过来数据的大小

    问题二:在发送数据时间隔短并且数据量小的情况下,会将所有数据一次性发送

    解决方法: 确认对方的数据的大小

    解决粘包问题(struct模块)

    struct模块是什么?

    是一个Python内置的模块,它可以将固定长度的数据,打包成固定格式的长度

    常用的模式:i 模式

    struct模块作用

    ​ 可以将真实数据,做成一个固定长度的报头,客户端发送给服务器,服务器可以接收报头,然后对报头进行解包,获取真实数据的长度,进行接收即可

    import struct
    
    data = b'11111111111111'
    print(len(data))
    # 打包制作报头
    header = struct.pack('i', len(data))
    print(header)
    print(len(header))
    
    
    # 解包获取真实数据长度 ---> 得到一个元组,元组中第一个值是真实数据的长度
    res = struct.unpack('i', header)[0]
    print(res)
    

    扩展例题

    # srever.py文件
    import socket
    import struct
    import subprocess
    
    server = socket.socket()
    server.bind(("127.0.0.1", 9527))
    
    server.listen(5)
    
    while True:
    
        coon, addr = server.accept()
        print(addr)
    
        while True:
            try:
                # 获取客户端传来的报头
                header = coon.recv(4)
                # 解包获取真实数据长度
                data_len = struct.unpack('i', header)[0]
    
                # 准备接收真实数据
                cmd = coon.recv(data_len)
    
                if len(cmd) == 0:
                    continue
    
                cmd = cmd.decode('utf-8')
                if cmd == "q":
                    break
    
                obj = subprocess.Popen(
                    cmd, shell=True,
                    stdout=subprocess.PIPE,
                    stderr=subprocess.PIPE
                )
    
                result = obj.stdout.read() + obj.stderr.read()
    
                print("发送给客户端的真实数据长度为", len(result))
    
                header = struct.pack('i', len(result))
                print(len(header))
                coon.send(header)
                coon.send(result)
    
            except Exception as e:
                print(e)
                break
    
        coon.close()
    
    # client.py文件
    import struct
    import socket
    
    client = socket.socket()
    client.connect(("127.0.0.1", 9527))
    
    while True:
        cmd = input("客户端输入的内容>>>:")
    
        cmd_bytes = cmd.encode('utf-8')
    
        # 做一个报头
        header = struct.pack("i", len(cmd_bytes))
        print(len(header))
    
        client.send(header)
    
        # 待服务器确认长度后,发送真实数据长度
        client.send(cmd_bytes)
    
        # 接收服务端返回的报头
        header = client.recv(4)
    
        # 解包,接收服务端返回的真实数据
        data_len = struct.unpack('i', header)[0]
        result = client.recv(data_len)
    
        print("接收服务端返回的真实数据长度为:", len(result))
        print(result.decode('gbk'))
    
    
  • 相关阅读:
    Zend Studio使用
    iOS中block实现的探究
    用python演示一个简单的AST(抽象语法树)
    Cocos2D-x权威指南: CCNode类方法:
    ListView的优化
    可变參数
    android媒体--stagefright概述【一】
    flume安装及配置
    linux包之sysstat之mpstat与pidstat命令
    Java实现第十届蓝桥杯等差数列
  • 原文地址:https://www.cnblogs.com/aheng/p/11991663.html
Copyright © 2011-2022 走看看