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'))
    
    
  • 相关阅读:
    Delphi XE5 android 蓝牙通讯传输
    Delphi XE5 android toast
    Delphi XE5 android openurl(转)
    Delphi XE5 如何设计并使用FireMonkeyStyle(转)
    Delphi XE5 android 捕获几个事件
    Delphi XE5 android listview
    Delphi XE5 android 黑屏的临时解决办法
    Delphi XE5 android popumenu
    Delphi XE5 android 获取网络状态
    Delphi XE5 android 获取电池电量
  • 原文地址:https://www.cnblogs.com/aheng/p/11991663.html
Copyright © 2011-2022 走看看