• 套接字中的TCP协议


    socket(套接字)

    本地回环地址

    127.0.0.1

    我们先来写一个简单地服务器和客户端

    服务端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    import socket
    server = socket.socket()  # 就比如买了一个手机
    server.bind(("127.0.0.1",8080))  # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
    server.listen(5# 半连接池,最大等待连接数为5个,就比如开机
    conn,address = server.accept()  # 接听电话等着别人给你打电话
    date = conn.recv(1024# 听别人说话,接收1023个字节数
    print(date)
    conn.send(b"hello"# 给别人回话
    conn.close()  # 挂断电话
    server.close()  # 关机

    客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import socket
    client = socket.socket()  #拿电话
    client.connect(("127.0.0.1",8080))  #绑定的是IP地址和端口号,也是一个元组 拨号
    client.send(b"hello"# 对别人发消息
    date = client.recv(1024#接收别人说话,没次接收1024个字节
    print(date)
    client.close()  # 挂电话

    注意,在我们写服务器与客户端的时候

    send与recv必须要一一对应

    不能出现两边都相同的

    recv是跟内存要数据,至于数据的来源你无需考虑

    粘包

    我们来看下面的的代码

    服务端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import socket
    server = socket.socket()  # 就比如买了一个手机
    server.bind(("127.0.0.1",8088))  # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
    server.listen(5# 半连接池,最大等待连接数为5个,就比如开机
    conn,address = server.accept()  # 接听电话等着别人给你打电话
    date = conn.recv(1024# 听别人说话,接收1023个字节数
    print(date)
    date = conn.recv(1024# 听别人说话,接收1023个字节数
    print(date)
    conn.close()  # 挂断电话
    server.close()  # 关机

    客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import socket
    client = socket.socket()  #拿电话
    client.connect(("127.0.0.1",8088))  #绑定的是IP地址和端口号,也是一个元组 拨号
    client.send(b"hello"# 对别人发消息
    client.send(b"hello"# 对别人发消息
    client.close()  # 挂电话

    服务端打印结果

    1
    b'hellohello'

    这是应为;

    tcp协议会将时间间隔短的,和文件大小小的会一次打包发个对方

    如果我们将代码改一下,将服务端收到的字节数1024改为我们知道的字节数,看看是否还会粘包

    服务端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    import socket
    server = socket.socket()  # 就比如买了一个手机
    server.bind(("127.0.0.1",8088))  # bind中绑定的是IP地址和端口号,注意是一个元组,就比如,将手机卡,插入了手机
    server.listen(5# 半连接池,最大等待连接数为5个,就比如开机
    conn,address = server.accept()  # 接听电话等着别人给你打电话
    date = conn.recv(5# 听别人说话,接收1023个字节数
    print(date)
    date = conn.recv(5# 听别人说话,接收1023个字节数
    print(date)
    conn.close()  # 挂断电话
    server.close()  # 关机

    客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import socket
    client = socket.socket()  #拿电话
    client.connect(("127.0.0.1",8088))  #绑定的是IP地址和端口号,也是一个元组 拨号
    client.send(b"hello"# 对别人发消息
    client.send(b"hello"# 对别人发消息
    client.close()  # 挂电话

    打印结果

    1
    2
    b'hello'
    b'hello'

    在我们知道了,我么传输的文件大小是多大的时候,规定给接收数据的recv就会避免粘包

    解决粘包问题

    struct模块

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    import struct
    print("--------------------------1--------------------------")
    msg = "asdasdasdasdasd"
    print("原字符串的长度")
    print(len(msg))
    handler = struct.pack("i",len(msg))
    print("创建报头的长度")
    print(len(handler))
    res = struct.unpack("i",handler)[0]
    print("解报头过后的长度")
    print(res)
    print("--------------------------2--------------------------")
    msg1 = "asdasdasdasdasdasdasdasd"
    print("原字符串的长度")
    print(len(msg1))
    handler1 = struct.pack("i",len(msg1))
    print("创建报头的长度")
    print(len(handler1))
    res1 = struct.unpack("i",handler1)[0]
    print("解报头过后的长度")
    print(res1)
    """
    --------------------------1--------------------------
    原字符串的长度
    15
    创建报头的长度
    4
    解报头过后的长度
    15
    --------------------------2--------------------------
    原字符串的长度
    24
    创建报头的长度
    4
    解报头过后的长度
    24
    """

    经过观察这个模块会将自己的长度固定为4,8几个等级,我们一般选i就够用了

    我们就可以将这个发过去,解报头,就可以知道原来的数据的字节大小

    如果这个字节大小比我们接收的大

    我们就然他一直接收,直达接收完为止

    代码如下

    服务端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    import socket
    import subprocess
    import json
    import struct
    # 创建套接字
    server = socket.socket()
    # 绑定端口号,及IP地址
    server.bind(("127.0.0.1", 8181))
    # 进入半连接池状态
    server.listen(5)
    while True:
        conn, address = server.accept()  # 等待客户端连接
        while True:
            # 判断是否会出错
            try:
                date = conn.recv(1024).decode("utf-8"# 接收客户端发来的数据
                if len(date) == 0: break  # 判断客户端发来的数据是否为空,为空就退出循环,关闭这个通道
                # 创建一个subprocess的对象
                obj = subprocess.Popen(date, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                # 将对象拿到的类容做个合并
                res = obj.stdout.read() + obj.stderr.read()
                # 创建一个字典,将拿到的数据的字符数,装入字典中
                dic = {"file_size": len(res)}
                # 将字典数据类型,转换为json的字符串数据类型
                json_dic = json.dumps(dic)
                # 将json字符串数据类型,创建为报头
                handler = struct.pack("i", len(json_dic))
                # 发送报头
                conn.send(handler)
                # 发送json字符串的字典
                conn.send(json_dic.encode("utf-8"))
                # 发送原始数据
                conn.send(res)
            # 捕捉异常
            except ConnectionResetError:
                break
        # 关闭通道
        conn.close()

    客户端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    import socket
    import struct
    import json
    # 创键套接字
    client = socket.socket()
    # 绑定端口和IP地址
    client.connect(("127.0.0.1", 8181))
    while True:
        # 用户输入发送内容,并装换成2进制
        msg = input("cmd>>>").strip().encode("utf-8")
        # 判断用户输入是否为空,为空跳过本次循环
        if len(msg) == 0: continue
        # 发送数据
        client.send(msg)
        # 接收报头
        handler = client.recv(4)
        # 解析报头,注意必须加索引,拿到json过后的字典的字节数
        json_dic_size = struct.unpack("i", handler)[0]
        # 接收json字典的字节数的json字典
        json_dic = client.recv(json_dic_size)
        # 将json数据的字典,反序列化
        dic = json.loads(json_dic)
        # 定义一个字节数为0 的变量
        msg_size = 0
        # 定义一个空的2进制字符
        msg_date = b''
        # 判断,如果定义的字节数小于我们字典中的字节数就读取文件,如果等于和大于就退出循环,打印文件类容
        while msg_size < dic.get("file_size"):
            date = client.recv(1024)
            # 文件类容累加
            msg_date += date
            # 字节数累加
            msg_size += len(date)
        print(msg_date.decode("gbk"))

      


     

  • 相关阅读:
    docker简单介绍----Dockerfile命令
    docker简单介绍----docker仓库的应用
    nginx实现https的配置文件
    docker简单介绍---网络端口管理
    mysql查看实时连接数
    linux 批量测试域名返回码脚本
    docker简单介绍----镜像和容器管理
    香冢
    GitHub 在使用命令行 git push 时报错:The requested URL returned error: 403
    守规矩应该被骂吗?
  • 原文地址:https://www.cnblogs.com/le-le666/p/11324291.html
Copyright © 2020-2023  润新知