• 网络编程与黏包问题


    传输层

    1.TCP协议: 

            TCP协议规定了数据在传输过程中需要遵循的规则,数据传输过程中能够遵循的协议有很多其中TCP协议和UDP协议是较为常见的两个。

            1.1:三次握手  

            

            1.2:四次握手

     

    \bullet  基于TCP传输数据因为有双向通道所以很安全

    \bullet  TCP传数据不容易丢失因为有二次确认机制 ,每次发送数据都需要返回确认消息否则在一定时间会反复发送

    2.UDP协议

            UDP协议发送数据没有任何的通道也没有任何的限制,但是没有TCP协议传输数据来的安全(没有二次确认机制)。

            eg:发短信重要发送了 不管别人有没有看到也不管回不回复。

    应用层

            应用层提供各种各样的应用层协议,这些协议嵌入在各种我们使用的应用程序中,主要取决程序员自己采用什么策略和协议。

    \bullet  常见协议有:HTTP HTTPS FTP.......

    scoket套接字编程

    1.scoket套接字的作用

            可以看成是两个网络应用程序进行通信时,各自通信连接中的端点

    2.套接字家族

            基于文件类型的套接字家族

                    套接字家族的名字:AF_UNIX

            基于网络类型的套接字家族

                    套接字家族的名字:AF_INET

    3.代码实现

    1. '''服务端'''
    2. import socket
    3. # 1.创建一个socket对象
    4. server = socket.socket() # 括号内什么都不写 默认就是基于网络的TCP套接字
    5. # 2.绑定一个固定的地址(ip\port)
    6. server.bind(('0.0.0.0', 8080)) # 地址、端口
    7. # 3.半连接池
    8. server.listen(5)
    9. # 4.开业 等待接客
    10. sock, address = server.accept()
    11. print(sock, address) # sock是双向通道 address是客户端地址
    12. # 5.数据交互
    13. sock.send(b'hello big baby~') # 朝客户端发送数据
    14. data = sock.recv(1024) # 接收客户端发送的数据 1024bytes
    15. print(data)
    16. # 6.断开连接
    17. sock.close() # 断链接
    18. server.close() # 关机
    1. '''客户端'''
    2. import socket
    3. # 1.产生一个socket对象
    4. client = socket.socket()
    5. # 2.连接服务端(拼接服务端的ip和port)
    6. client.connect(('0.0.0.0', 8080))
    7. # 3.数据交互
    8. data = client.recv(1024) # 接收服务端发送的数据
    9. print(data)
    10. client.send(b'hello sweet server') # 朝服务端发送数据
    11. # 4.关闭
    12. client.close()

    4.代码优化

            4.1:自定义消息

            4.2:循环通信

            4.3:服务端保持持续提供服务

            4.4:判断输入消息不能为空

    1. '''服务端'''
    2. import socket
    3. from socket import SOL_SOCKET, SO_REUSEADDR
    4. # 1.创建一个socket对象
    5. server = socket.socket() # 括号内什么都不写 默认就是基于网络的TCP套接字
    6. # 2.绑定一个固定的地址(ip\port)
    7. server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) # 就是它,在bind前加
    8. server.bind(('127.0.0.1', 8080)) # 127.0.0.1本地回环地址(只允许自己的机器访问)
    9. # 3.半连接池
    10. server.listen(5)
    11. # 4.开业 等待接客
    12. while True:
    13. sock, address = server.accept()
    14. print(sock, address) # sock是双向通道 address是客户端地址
    15. # 5.数据交互
    16. while True:
    17. try:
    18. msg = input('请输入发送给客户端的消息>>>:').strip()
    19. if len(msg) == 0: continue # 判断输入的是否是空的
    20. sock.send(msg.encode('utf8')) # 朝客户端发送数据
    21. data = sock.recv(1024) # 接收客户端发送的数据 1024bytes
    22. if len(data) == 0: # 判断接受的是否是空
    23. break
    24. print(data.decode('utf8'))
    25. except ConnectionResetError:
    26. sock.close()
    27. break
    1. '''客户端'''
    2. import socket
    3. # 1.产生一个socket对象
    4. client = socket.socket()
    5. # 2.连接服务端(拼接服务端的ip和port)
    6. client.connect(('0.0.0.0', 8080))
    7. # 3.数据交互
    8. while True:
    9. data = client.recv(1024) # 接收服务端发送的数据
    10. print(data.decode('utf8'))
    11. msg = input('请输入发送给客户端的消息>>>:').strip()
    12. if len(msg) == 0: # 判断是否是零
    13. msg = '暂无消息'
    14. client.send(msg.encode('utf8')) # 朝服务端发送数据

    5.半连接池

            当服务器在响应了客户端的第一次请求后会进入等待状态,半连接池其实就是一个容器,系统会自动将半连接放入这个容器中,可以避免半连接过多。

    1. server.listen(5)
    2. # 自定义数量

    TCP黏包问题及解决思路

    1.什么是黏包问题

            接收端不知道发送端将要传送的字节流的长度,UDP不会出现这种情况而TCP协议, 数据流会向流水一样一起, 接收端不清楚每个消息的界限, 不知道每次应该去多少字节的数据。

    2.TCP特性

            流式协议:所有的数据类似于水流一样连接在一起传输(数据量小并且间隔很短就会自动组织到一起)

    3.struct模块

    Packing(打包成字符串)

    Unpacking(解包)

     

    4.黏包解决方法:

    1. '''客户端'''
    2. import socket
    3. import struct
    4. import json
    5. client = socket.socket()
    6. client.connect(('0.0.0.0', 8080))
    7. while True:
    8. # 先接收长度为4的报头数据
    9. header_len = client.recv(4)
    10. # 根据报头解包出字典的长度
    11. dict_len = struct.unpack('i', header_len)[0]
    12. # 直接接收字典数据
    13. dict_data = client.recv(dict_len) # b'{"file_name":123123123}'
    14. # 解码并反序列化出字典
    15. real_dict = json.loads(dict_data)
    16. print(real_dict)
    17. # 从数据字典中获取真实数据的各项信息
    18. total_size = real_dict.get('file_size') # 32423423423
    19. file_size = 0
    20. with open(r'文件路径', 'wb') as f:
    21. while file_size < total_size:
    22. data = client.recv(1024)
    23. f.write(data)
    24. file_size += len(data)
    25. print('文件接收完毕')
    26. break
    1. '''服务端'''
    2. import socket
    3. import os
    4. import struct
    5. import json
    6. server = socket.socket()
    7. server.bind(('0.0.0.0', 8080))
    8. server.listen(5)
    9. while True:
    10. sock, address = server.accept()
    11. while True:
    12. # 构造数据文件的字典
    13. file_dict = {
    14. 'file_name': 'a.txt',
    15. 'file_size': os.path.getsize(r'文件绝对路径')
    16. }
    17. # 将字典转换成json格式
    18. dict_json = json.dumps(file_dict)
    19. file_bytes_dict = len(dict_json.encode('utf8'))
    20. # 将字典打包成固定长度的数据
    21. dict_len = struct.pack('i', file_bytes_dict)
    22. # 发送固定长度的字典报头
    23. sock.send(dict_len)
    24. # 发送真实字典数据
    25. sock.send(dict_json.encode('utf8'))
    26. # 发送真实数据
    27. with open(r'读取文件', 'rb') as f:
    28. for line in f:
    29. sock.send(line)
    30. break

  • 相关阅读:
    SQL vs NoSQL: 为满足您的业务需求选择正确的数据库模型
    docker下redis备份文件dump.rdb获取
    区块链论文一般发表在哪些地方?
    Linux 下进程间通讯之管道
    成都瀚网科技有限公司:怎么优化抖店体验分?
    强力的应用容器引擎---------Docker的资源控制
    【Flink】flink 状态恢复 because the operator is not available in the new program
    Android gradle动态配置不同打包环境参数值
    2023-10-28 LeetCode每日一题(从数量最多的堆取走礼物)
    springboot家乡特色推荐系统springboot28
  • 原文地址:https://blog.csdn.net/weixin_52596593/article/details/126179765