TCP、UDP模型与粘包问题

【一】三次握手和四次挥手

  • TCP协议位于osi七层协议中的传输层

    • 使用三次握手来建立连接

    • 使用四次挥手来断开连接

  • 有几个名词:

    • SYN:SYN=1 表示要建立连接

    • SYN:SYN=1 表示要建立连接

    • ACK:ACK=1 表示我收到了,允许

    • seq:随机数,建立连接无论客户端还是服务端要建立连接就要要携带

    • ack:回应请求就要加1返回

    • FIN:表示断开连接

    【1】三次握手

    发生在建立连接的阶段
    【1】第一次请求
    由客户端发起请求 带SYN=1
    表示我自己是客户端我要建立连接
    seq随机数带
    发送给服务端
    客户端 ---> 携带 SYN和SEQ ---> 发送给服务端
    【2】第二次请求
    服务端接收到客户端的请求
    ACK=1 表示收到了当前客户端发送给我的请求
    SYN=1 表示要建立连接
    seq:随机数
    服务端 ---> 接收到客户端的请求,同意建立连接 ---> 发送给客户端
    【3】第三次请求
    客户端接收到了服务端的请求
    ACK=1 表示收到了当前服务端发送给我的请求
    SYN=1 表示要建立连接
    seq:随机数
    和服务端建立连接成功

    img

    【2】四次挥手

    发生在断开连接上
    【1】第一次
    客户端向服务端发送请求,表示想要断开连接
    【2】第二次
    服务端接收到客户端的请求
    表示同意断开连接
    【3】第三次
    服务端向客户端发送请求,请求的原因是当前还有数据没有传输完成
    请求等待,等待数据传输完成
    发起请求,断开连接
    服务端向客户端发送请求,请求断开连接
    【4】第四次
    客户端接收到服务端的请求
    直接断开连接

    img

【二】UDP协议模型

client文件

# 【一】引入socket模块
from conf import settings
import socket
​
​
# 【二】创建一个client对象
# AF_INET: 当前连接的是基于网络的套接字
# SOCK_DGRAM:连接模式是UDP协议的报式模式
client = socket.socket(family=socket.AF_INET,type=socket.SOCK_DGRAM)
# 【三】直接发送数据
to_server_send_data = f'这是来自客户端的数据'
to_server_send_data = to_server_send_data.encode()
client.sendto(to_server_send_data,settings.ADDR)
print(f"client:{client}")
# 【四】接收到服务端回的消息
from_server_recv_data,addr = client.recvfrom(1024)
from_server_recv_data = from_server_recv_data.decode()
print(f'from_server_recv_data:{from_server_recv_data}')
print(from_server_recv_data)
# 【五】关闭连接对象
client.close()

server文件

from conf import settings
​
# 【一】引入socket模块
import socket
​
# 【二】创建一个server对象
# AF_INET:当前连接是基于网络的套接字
# SOCK_DGRAM:连接模式是UDP协议的报式模式
# 只会发送一次数据
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_DGRAM)
# 【三】绑定IP和PORT
server.bind(settings.ADDR)
print(f'server:{server}')
# 【四】接收到客户端的数据
from_client_socket_data,addr = server.recvfrom(1024)
from_client_socket_data = from_client_socket_data.decode()
print(f'from_client_socket_data: {from_client_socket_data}')
print(f'addr :>>>>{addr}')
# 【五】返回给客户端数据
to_client_send_data = '这是来自服务端的一条消息!'
to_client_send_data = to_client_send_data.encode()
server.sendto(to_client_send_data,addr)
# 【六】关闭连接和服务
server.close()

补充:

【1】localhost和127.0.0.1的区别

127.0.0.1又称为本机IP地址

DNS解析 ---》一串字符解析成IP

localhost就是一个域名,但是只能在本地使用

【2】报错问题
# 【一】解决办法一:通用 : 换 端口号
# 直接将启动的IP和端口号中的端口号更改
# 【二】解决办法二:加一个配置
# address already in use
# 【1】加配置
# SO_REUSEADDR : re 重新 use 使用 addr 地址
# server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定IP和PORT
# 【2】绑定端口
# server.bind(settings.ADDR)
# 【三】解决办法三:# OSError: [WinError 10048] 通常每个套接字地址(协议/网络地址/端口)只允许使用一次。
# 【1】Windows
# 查看当前端口对应的进程,杀死进程
# netstat -ano|findstr 8002
# 杀死当前进程PID对应的进程
# taskkill /pid PID号 /F

【三】TCP协议模型

settings文件

IP = '127.0.0.1'
PORT = 8088
​
ADDR = (IP, PORT)
【1】第一版

客户端文件

# 【一】引入socket模块
from conf import settings
import socket
​
​
# 【二】创建一个server对象
# AF_INET: 当前连接的是基于网络的套接字
# SOCK_STREAM:连接模式是TCP的流式模式
client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)
​
# 【三】绑定IP和PORT
client.connect(settings.ADDR)
# 【四】直接发送数据
to_server_send_data = f'这是来自客户端的数据'
to_server_send_data = to_server_send_data.encode()
client.send(to_server_send_data)
print(f'to_server_send_data :>>>> {client}')
# 【五】接收到服务端回的消息
from_server_recv_data = client.recv(1024)
from_server_recv_data = from_server_recv_data.decode()
print(from_server_recv_data)
# 【六】关闭连接对象
client.close()

服务器文件

from conf import settings
​
# 【一】引入socket模块
import socket
​
# 【二】创建一个server对象
# AF_INET:当前连接是基于网络的套接字
# SOCK_STREAM:连接模式是TCP协议的流式模式
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 【三】绑定IP和PORT
server.bind(settings.ADDR)
# 【四】监听连接对象
server.listen(5)
# 【五】建立连接对象
conn,addr = server.accept()
# 【六】接收到客户端的数据
from_client_socket_data = conn.recv(1024)
from_client_socket_data = from_client_socket_data.decode()
print(from_client_socket_data)
# 【七】返回给客户端数据
to_client_send_data = '这是来自服务端的一条消息!'
to_client_send_data = to_client_send_data.encode()
conn.send(to_client_send_data)
# 【八】关闭连接和服务
conn.close()
server.close()
【2】第二版(解决了只能发送一次数据的问题)

在里面套一个while循环即可

client文件

# 【一】引入socket模块
from conf import settings
import socket


# 【二】创建一个server对象
# AF_INET: 当前连接的是基于网络的套接字
# SOCK_STREAM:连接模式是TCP的流式模式
client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)

# 【三】绑定IP和PORT
client.connect(settings.ADDR)
while True:
    # 【四】直接发送数据
    to_server_send_data = input(f'请输入要传递给服务端的数据:').strip()
    to_server_send_data = to_server_send_data.encode()
    client.send(to_server_send_data)
    print(f'to_server_send_data :>>>> {client}')
    # 【五】接收到服务端回的消息
    from_server_recv_data = client.recv(1024)
    from_server_recv_data = from_server_recv_data.decode()
    print(from_server_recv_data)
# 【六】关闭连接对象
client.close()

server文件

from conf import settings

# 【一】引入socket模块
import socket

# 【二】创建一个server对象
# AF_INET:当前连接是基于网络的套接字
# SOCK_STREAM:连接模式是TCP协议的流式模式
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 【三】绑定IP和PORT
server.bind(settings.ADDR)
# 【四】监听连接对象
server.listen(5 )
while True:
    # 【五】建立连接对象
    conn, addr = server.accept()
    # 【六】接收到客户端的数据
    from_client_socket_data = conn.recv(1024)
    from_client_socket_data = from_client_socket_data.decode()
    print(from_client_socket_data)
    # 【七】返回给客户端数据
    to_client_send_data = input(f'请输入传递给客户端的数据:').strip()
    to_client_send_data = to_client_send_data.encode()
    conn.send(to_client_send_data)
# 【八】关闭连接和服务
conn.close()
server.close()
【3】第三版(上面只能发一个来回,这里解决了这个问题)

server文件

from conf import settings

# 【一】引入socket模块
import socket

# 【二】创建一个server对象
# AF_INET:当前连接是基于网络的套接字
# SOCK_STREAM:连接模式是TCP协议的流式模式
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 【三】绑定IP和PORT
server.bind(settings.ADDR)
# 【四】监听连接对象
server.listen(5)
# 【五】建立连接对象
conn, addr = server.accept()	# 将他放到while循环外面,每次都会换对象,在里面不会换
while True:
    # 这里面交流的对象永远都是上面接收到的
    # 【六】接收到客户端的数据
    from_client_socket_data = conn.recv(1024)
    from_client_socket_data = from_client_socket_data.decode()
    print(from_client_socket_data)
    # 【七】返回给客户端数据
    to_client_send_data = input(f'请输入传递给客户端的数据:').strip()
    to_client_send_data = to_client_send_data.encode()
    conn.send(to_client_send_data)
# 【八】关闭连接和服务
conn.close()
server.close()
【4】第四版(不允许传递空格及q退出,在里面再次添加一个while判断)

client文件

# 【一】引入socket模块
from conf import settings
import socket


# 【二】创建一个server对象
# AF_INET: 当前连接的是基于网络的套接字
# SOCK_STREAM:连接模式是TCP的流式模式
client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)

# 【三】绑定IP和PORT
client.connect(settings.ADDR)
while True:
    # 【四】直接发送数据
    to_server_send_data = input(f'请输入要传递给服务端的数据:').strip()
    if not to_server_send_data:
        print(f'不允许传递空格')
        continue
    if to_server_send_data == 'q':
        print(f'成功断开连接')
        break
    to_server_send_data = to_server_send_data.encode()
    client.send(to_server_send_data)
    print(f'to_server_send_data :>>>> {client}')
    # 【五】接收到服务端回的消息
    from_server_recv_data = client.recv(1024)
    from_server_recv_data = from_server_recv_data.decode()
    print(from_server_recv_data)
# 【六】关闭连接对象
client.close()

server文件

from conf import settings

# 【一】引入socket模块
import socket

# 【二】创建一个server对象
# AF_INET:当前连接是基于网络的套接字
# SOCK_STREAM:连接模式是TCP协议的流式模式
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 【三】绑定IP和PORT
server.bind(settings.ADDR)
# 【四】监听连接对象
server.listen(5)
# 【五】建立连接对象
conn, addr = server.accept()
while True:
    # 这里面交流的对象永远都是上面接收到的
    # 【六】接收到客户端的数据
    from_client_socket_data = conn.recv(1024)
    from_client_socket_data = from_client_socket_data.decode()
    print(from_client_socket_data)
    while True:
        # 【七】返回给客户端数据
        to_client_send_data = input(f'请输入传递给客户端的数据:').strip()
        if not to_client_send_data:
            print(f"不允许传递空格")
            continue
        if to_client_send_data == "q":
            print(f'成功断开连接')
            break
        to_client_send_data = to_client_send_data.encode()
        conn.send(to_client_send_data)
        break
# 【八】关闭连接和服务
conn.close()
server.close()
【5】第五版(无论哪一边输入q,两边都断开,不会傻等了)

client文件

# 【一】引入socket模块
from conf import settings
import socket


# 【二】创建一个server对象
# AF_INET: 当前连接的是基于网络的套接字
# SOCK_STREAM:连接模式是TCP的流式模式
client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)

# 【三】绑定IP和PORT
client.connect(settings.ADDR)
while True:
    # 【四】直接发送数据
    to_server_send_data = input(f'请输入要传递给服务端的数据:').strip()
    if not to_server_send_data:
        print(f'不允许传递空格')
        continue
    if to_server_send_data == 'q':
        print(f'成功断开连接')
        break
    to_server_send_data = to_server_send_data.encode()
    client.send(to_server_send_data)
    print(f'to_server_send_data :>>>> {client}')
    # 【五】接收到服务端回的消息
    from_server_recv_data = client.recv(1024)
    from_server_recv_data = from_server_recv_data.decode()
    print(from_server_recv_data)
    if from_server_recv_data == 'q':	
        break
# 【六】关闭连接对象
client.close()

server文件

from conf import settings

# 【一】引入socket模块
import socket

# 【二】创建一个server对象
# AF_INET:当前连接是基于网络的套接字
# SOCK_STREAM:连接模式是TCP协议的流式模式
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 解决端口号占用报错的方法,重用ip端口号
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 【三】绑定IP和PORT
server.bind(settings.ADDR)
# 【四】监听连接对象
server.listen(5)
# 【五】建立连接对象
conn, addr = server.accept()
while True:
    # 添加一个try捕获异常
    try:
        # 这里面交流的对象永远都是上面接收到的
        # 【六】接收到客户端的数据
        from_client_socket_data = conn.recv(1024)
        from_client_socket_data = from_client_socket_data.decode()
        print(from_client_socket_data)
        if not from_client_socket_data:
            break
        while True:
            # 【七】返回给客户端数据
            to_client_send_data = input(f'请输入传递给客户端的数据:').strip()
            if not to_client_send_data:
                print(f"不允许传递空格")
                continue
            if to_client_send_data == "q":
                print(f'成功断开连接')	# 这里输入q之后编码传过去,然后执行break断开
            to_client_send_data = to_client_send_data.encode()
            conn.send(to_client_send_data)
            break
    except Exception as e:
        break
# 【八】关闭连接和服务
conn.close()
server.close()

【四】粘包

【1】粘包问题的介绍

[1]粘包问题的背景

粘包问题只会发生在TCP协议 ---> 流式协议 ---> 不断的传输数据

比如有一个大缸:先放红颜色的颜料放了一缸但是还有一勺没有放进去

没有放进去的这一勺红颜色颜料和下一缸的绿颜色颜料混到了一起

不会发生在UDP协议 ---> 报式协议 ---> 一次性传输数据

比如有一个大缸:先放红颜色的颜料放了一缸但是还有一勺没有放进去

那么直接将这勺颜料扔了

[2]粘包问题

客户端发送的数据远远超出服务端的接收范围,出现没接收完整的情况,导致了不同数据之间的数据混乱

问题

[3]实例

执行本地的 ipconfig 命令会获取到当前执行的结果

将结果传递给 服务端

服务端接收到数据 只能接收一部分 另外一部分发现接收不到

只能和第二次的数据合并到一起发送

[4]解决办法

问题产生在服务端

原因是客户端向服务端发送数据,但是服务端不知道总的数据大小,只能按照默认的数据大小接收

解决思路:

客户端在发送数据的时候将数据的总大小一起发送给服务端

服务端接收到总的大小的数据长度 , 根据自己的容量大小分批次接收

【2】问题演示

client文件

# 【一】引入socket模块
from conf import settings
import socket
import subprocess


def run_cmd(command):
    result = subprocess.run(
        command,  # 子进程要执行的命令
        shell=True,  # 执行的是shell的命令
        # 存放的是执行命令成功的结果
        stdout=subprocess.PIPE,
        # 存放的是执行命令失败的结果
        stderr=subprocess.PIPE,
        encoding="gbk",
        timeout=1)
    # return code属性是run()函数返回结果的状态。
    if result.returncode == 0:
        return result.stdout
    else:
        return result.stderr


# 【二】创建一个server对象
# AF_INET: 当前连接的是基于网络的套接字
# SOCK_STREAM:连接模式是TCP的流式模式
client = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)

# 【三】绑定IP和PORT
client.connect(settings.ADDR)
while True:
    # 【四】直接发送数据
    command = input(f'请输入要传递给服务端的数据:').strip()
    if not command:
        print(f'不允许传递空格')
        continue
    if command == 'q':
        print(f'成功断开连接')
        break
    to_server_send_data = run_cmd(command=command)
    to_server_send_data = to_server_send_data.encode()
    client.send(to_server_send_data)
    print(f'to_server_send_data :>>>> {client}')
    # 【五】接收到服务端回的消息
    from_server_recv_data = client.recv(1024)
    from_server_recv_data = from_server_recv_data.decode()
    print(from_server_recv_data)
    if from_server_recv_data == 'q':
        break
# 【六】关闭连接对象
client.close()

server文件

from conf import settings

# 【一】引入socket模块
import socket

# 【二】创建一个server对象
# AF_INET:当前连接是基于网络的套接字
# SOCK_STREAM:连接模式是TCP协议的流式模式
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 解决端口号占用报错的方法,重用ip端口号
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 【三】绑定IP和PORT
server.bind(settings.ADDR)
# 【四】监听连接对象
server.listen(5)
# 【五】建立连接对象
conn, addr = server.accept()
while True:
    try:
        # 这里面交流的对象永远都是上面接收到的
        # 【六】接收到客户端的数据
        from_client_socket_data = conn.recv(1024)
        from_client_socket_data = from_client_socket_data.decode()
        print(from_client_socket_data)
        if not from_client_socket_data:
            break
        while True:
            # 【七】返回给客户端数据
            to_client_send_data = input(f'请输入传递给客户端的数据:').strip()
            if not to_client_send_data:
                print(f"不允许传递空格")
                continue
            if to_client_send_data == "q":
                print(f'成功断开连接')
            to_client_send_data = to_client_send_data.encode()
            conn.send(to_client_send_data)
            break
    except Exception as e:
        break
# 【八】关闭连接和服务
conn.close()
server.close()

【3】解决办法

import json

from conf import settings
# 【一】引入socket模块
import socket
import subprocess
import uuid


def run_cmd(command):
    result = subprocess.run(
        command,  # 子进程要执行的命令
        shell=True,  # 执行的是shell的命令
        # 存放的是执行命令成功的结果
        stdout=subprocess.PIPE,
        # 存放的是执行命令失败的结果
        stderr=subprocess.PIPE,
        encoding="gbk",
        timeout=1)
    # returncode属性是run()函数返回结果的状态。
    if result.returncode == 0:
        return result.stdout
    else:
        return result.stderr


# 【二】创建一个server对象
# AF_INET:当前连接是基于网络的套接字
# SOCK_STREAM:连接模式是TCP协议的流式模式
client = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
# 【三】绑定IP和PORT
client.connect(settings.ADDR)
while True:
    # 【四】直接发送数据
    command = input("请输入需要执行的命令 :>>>> ").strip()
    if not command:
        print(f'不允许发送空的数据')
        continue
    if command == 'q':
        print(f'当前连接已退出!')
        break
    # 【1】执行本地的命令,获取到当前命令结果
    result = run_cmd(command=command)
    # 【2】对命令的结果进行编码---> 转成二进制数据
    result_bytes = result.encode()
    # 【3】计算长度
    data_length = len(result_bytes)
    # 【4】增加一个数据概览 --> 字典格式 做数据概览
    # 存储当前文件名 / 结果名 / md5加密盐(用来校验数据的完整性)
    salt = uuid.uuid4().hex
    encrypted = settings.encrypt_data(data=result_bytes, salt=salt)
    send_data_info = {
        'command': command,
        'data_length': data_length,
        'salt': salt,
        'encrypted': encrypted
    }
    # 【5】将上面打包好的数据全部发送给服务端
    # (1)字典格式无法发送
    # 将字典转换为字符串数据 ----> json
    # dump : 处理文件数据
    # dumps : 做格式转换的
    json_str = json.dumps(send_data_info)
    # (2)将json字符串数据转换为二进制数据
    json_bytes = json_str.encode()
    # 【6】问题产生
    # JSON字符串转换为的二进制数据还是会很长
    # 让数据变短
    # struct 模块 ---> 将某几个数字转换为四个字节的二进制数据
    json_length_pack = settings.pack_data(data_length=len(json_bytes))
    # 【7】发送struct打包的数据(四个字节) + JSON数据 + 原始数据
    # JSON数据里面存的是所有数据信息而没有原始的二进制数据
    # 服务端接受的顺序取决于客户端发送的顺序
    # 先发送struct打包后的数据
    client.send(json_length_pack)  # 4 字节 --> 包含json二进制数据的长度
    # 先发送 json_bytes 打包后的数据
    client.send(json_bytes)  # 不知道
    # 再发送 result_bytes 原始数据
    client.send(result_bytes)
    # 【五】接收到服务端回的消息
    from_server_recv_data = client.recv(1024)
    from_server_recv_data = from_server_recv_data.decode()
    if from_server_recv_data == 'q':
        break
    print(f'这是来自服务端的数据 :>>>>  \n{from_server_recv_data}')
# 【六】关闭连接对象
client.close()
import json

from conf import settings
# 【一】引入socket模块
import socket

# 【二】创建一个server对象
# AF_INET:当前连接是基于网络的套接字
# SOCK_STREAM:连接模式是TCP协议的流式模式
server = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 【三】绑定IP和PORT
server.bind(settings.ADDR)
# 【四】监听连接对象
server.listen(5)
# 【五】建立连接对象
# 放在这里 进入的 while 循环中的哪个连接对象会一致不变
conn, addr = server.accept()
while True:
    try:
        # 这里面交流的对象永远是上面接收到的那一个
        # conn, addr = server.accept() : 接收到新的对象,忘记上一个连接过的对象
        # 【六】接收到客户端的数据
        # 【1】先接接收四个字节的数据 ---> struct打包好的四个字节的数据
        json_pack_data = conn.recv(4)
        if not json_pack_data:
            break
        json_bytes_length = settings.unpack_data(data=json_pack_data)
        # 【2】根据json二进制数据长度解出JSON二进制数据
        json_data_bytes = conn.recv(json_bytes_length)
        # 【3】将json二进制数据转为json字符串数据
        json_str = json_data_bytes.decode()
        # 【4】将json字符串数据转换为python的字典
        data_info = json.loads(json_str)
        # 【5】从字典中获取自定的参数
        # 获取到总的数据长度
        # 10000
        data_length = data_info.get('data_length')
        # 【6】定义参数
        # (1)总数据
        all_data = b''
        # (2)每次接收的数据大小
        size = 1024
        # data_length : 5
        # size : 2
        # count : 2 , last_size : 1
        count, last_size = divmod(data_length, size)
        # (3)已经接受的数据大小
        all_size = 0
        while all_size < count + 1:
            all_size += 1
            # 接收到每一次的数据并和总数据拼接
            if all_size == count + 1:
                all_data += conn.recv(last_size)
            else:
                all_data += conn.recv(size)
        from_client_recv_data = all_data.decode()
        print(f'这是来自客户端的数据 :>>>>  \n {from_client_recv_data}')
        # 【七】返回给客户端数据
        while True:
            to_client_send_data = input("请输入发送给客户端的数据 :>>>> ").strip()
            if not to_client_send_data:
                print(f'不允许发送空的数据')
                continue
            if to_client_send_data == 'q':
                print(f'当前连接已退出!')
            to_client_send_data = to_client_send_data.encode()
            conn.send(to_client_send_data)
            break
    except Exception as e:
        break
# 【八】关闭连接和服务
conn.close()
server.close()

【4】subprocess模块回顾

import subprocess


def run_cmd(command):
    result = subprocess.run(
        command,  # 子进程要执行的命令
        shell=True,  # 执行的是shell的命令
        # 存放的是执行命令成功的结果
        stdout=subprocess.PIPE,
        # 存放的是执行命令失败的结果
        stderr=subprocess.PIPE,
        encoding="gbk",
        timeout=1)
    # returncode属性是run()函数返回结果的状态。
    if result.returncode == 0:
        return result.stdout
    else:
        return result.stderr


if __name__ == '__main__':
    print(run_cmd(['dir']))

【5】struct模块介绍

# 【一】模块介绍
# ● struct.pack()是Python内置模块struct中的一个函数
# ● 它的作用是将指定的数据按照指定的格式进行打包
# 并将打包后的结果转换成一个字节序列(byte string),可以用于在网络上传输或者储存于文件中。
# 【二】参数简介
# struct.pack(fmt, v1, v2, ...)
# ● 其中,fmt为格式字符串,指定了需要打包的数据的格式,后面的v1,v2,...则是需要打包的数据。
# ● 这些数据会按照fmt的格式被编码成二进制的字节串,并返回这个字节串。

# 【三】示例
import struct

# 定义一个包含不同类型字段的格式字符串
format_string = 'i'

# 示例数据:整数、四个字节的原始数据、短整数
data_to_pack = '十七dasdadsad asd 撒大撒多所adsaddasdadsa da dsa asad撒大大带我去大青蛙大大大大大萨达去问问恰饭恰饭放散阀昂发昂发沙发阿发发发放上千万请发送方三房启发法阿发发发ad sada dsa dsa dsa sa dsa dsa as ad sad ad ada顿撒大大三大撒打我前端'
data_to_pack_bytes = data_to_pack.encode()

data_to_pack_len = len(data_to_pack_bytes)
print(data_to_pack_len)
# 使用 struct.pack 将数据打包成二进制字节串
packed_data = struct.pack(format_string, data_to_pack_len)

# 41000000
# 64000000
# 19010000
print("Packed data:", len(packed_data))  # 打印打包后的十六进制表示

# 解析二进制字节串,恢复原始数据
unpacked_data = struct.unpack(format_string, packed_data)
#
print("Unpacked data:", unpacked_data)  # 打印解析后的数据

##

  • 11
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux网络编程之TCP/IP基础篇 01TCPIP基础(一) ISO/OSI参考模型 TCP/IP四层模型 基本概念(对等通信、封装、分用、端口) 02TCPIP基础(二) 最大传输单元(MTU)/路径MTU 以太网帧格式 ICMP ARP RARP 03TCPIP基础(三) IP数据报格式 网际校验和 路由 04TCPIP基础(四) TCP特点 TCP报文格式 连接建立三次握手 连接终止四次握手 TCP如何保证可靠性 05TCPIP基础(五) 滑动窗口协议 UDP特点 UDP报文格式 Linux网络编程之socket编程篇 06socket编程(一) 什么是socket IPv4套接口地址结构 网络字节序 字节序转换函数 地址转换函数 套接字类型 07socket编程(二) TCP客户/服务器模型 回射客户/服务器 socket、bind、listen、accept、connect 08socket编程(三) SO_REUSEADDR 处理多客户连接(process-per-conection) 点对点聊天程序实现 09socket编程(四) 流协议与粘包 粘包产生的原因 粘包处理方案 readn writen 回射客户/服务器 10socket编程(五) read、write与recv、send readline实现 用readline实现回射客户/服务器 getsockname、getpeername gethostname、gethostbyname、gethostbyaddr 11socket编程(六) TCP回射客户/服务器 TCP是个流协议 僵进程与SIGCHLD信号 12socket编程(七) TCP 11种状态 连接建立三次握手、连接终止四次握手 TIME_WAIT与SO_REUSEADDR SIGPIPE 13socket编程(八) 五种I/O模型 select 用select改进回射客户端程序 14socket编程(九) select 读、写、异常事件发生条件 用select改进回射服务器程序。 15socket编程(十) 用select改进第八章点对点聊天程序 16socket编程(十一) 套接字I/O超时设置方法 用select实现超时 read_timeout函数封装 write_timeout函数封装 accept_timeout函数封装 connect_timeout函数封装 17socket编程(十二) select限制 poll 18socket编程(十三) epoll使用 epoll与select、poll区别 epoll LT/ET模式 19socket编程(十四) UDP特点 UDP客户/服务基本模型 UDP回射客户/服务器 UDP注意点 20socket编程(十五) udp聊天室实现 21socket编程(十六) UNIX域协议特点 UNIX域地址结构 UNIX域字节流回射客户/服务 UNIX域套接字编程注意点 22socket编程(十七) socketpair sendmsg/recvmsg UNIX域套接字传递描述符字 Linux网络编程之进程间通信篇 23进程间通信介绍(一) 进程同步与进程互斥 进程间通信目的 进程间通信发展 进程间通信分类 进程间共享信息的三种方式 IPC对象的持续性 24进程间通信介绍(二) 死锁 信号量 PV原语 用PV原语解决司机与售票员问题 用PV原语解决民航售票问题 用PV原语解决汽车租赁问题 25System V消息队列(一) 消息队列 IPC对象数据结构 消息队列结构 消息队列在内核中的表示 消息队列函数 26System V消息队列(二) msgsnd函数 msgrcv函数 27System V消息队列(三) 消息队列实现回射客户/服务器 28共享内存介绍 共享内存 共享内存示意图 管道、消息队列与共享内存传递数据对比 mmap函数 munmap函数 msync函数 29System V共享内存 共享内存数据结构 共享内存函数 共享内存示例 30System V信号量(一) 信号量 信号量集结构 信号量集函数 信号量示例 31System V信号量(二) 用信号量实现进程互斥示例 32System V信号量(三) 用信号集解决哲学家就餐问题 33System V共享内存与信号量综合 用信号量解决生产者消费者问题 实现shmfifo 34POSIX消息队列 POSIX消息队列相关函数 POSIX消息队列示例 35POSIX共享内存 POSIX共享内存相关函数 POSIX共享内存示例 Linux网络编程之线程篇 36线程介绍 什么是线程 进程与线程 线程优缺点 线程模型 N:1用户线程模型 1:1核心线程模型 N:M混合线程模型 37POSIX线程(一) POSIX线程库相关函数 用线程实现回射客户/服务器 38POSIX线程(二) 线程属性 线程特定数据 39POSIX信号量与互斥锁 POSIX信号量相关函数 POSIX互斥锁相关函数 生产者消费者问题 自旋锁与读写锁介绍 40POSIX条件变量 条件变量 条件变量函数 条件变量使用规范 使用条件变量解决生产者消费者问题 41一个简单的线程池实现 线程池性能分析 线程池实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值