zoukankan      html  css  js  c++  java
  • 网络编程小总结/socket套接字/TCP粘包问题

    内容回顾

    网络编程:使计算机之间基于网络实现数据交互

    交换机:只要是接入了交换机的计算机 彼此之间都是互联的

    局域网:互联网其实都是由N多个局域网连接而成

    基于以太网协议通信的特点

    • 通信基本靠吼
      • 广播
      • 单播

    arp协议:根据IP地址获取mac地址

    OSI协议:(补充)

    路由器:连接局域网

    域名解析:www.baidu.com >>> 14.215.177.39:80

    应用层

    • HTTP协议:超文本传输协议
    • FTP协议

    UDP协议

    • 数据报协议
    • 无需建立双向通道 数据传输是不安全的
    • 将内存中的数据直接发送出去 不会做保留
    • 例如早期的qq

    TCP协议类似于打电话

    UDP协议类似于发短信


    今日内容

    socket套接字


    解决端口占用问题

    from socket import SOL_SOCKET,SO_REUSEADDR
    sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,
    1) #就是它,在bind前加

    服务端

    import socket
    ​
    # 买手机
    server = socket.socket()  # 不传参数时,默认用的就是TCP协议
    # 插电话卡 bind((host,port)) 绑定ip和port
    server.bind(('127.0.0.1',8080))
    # 开机 半连接池
    server.listen(5)
    # 接听电话 等着别人给你打电话
    conn, addr = server.accept()  # 阻塞,等待
    # 听别人说话 接收1024个字节数据
    data = conn.recv(1024)  # 阻塞,等待
    # 给别人回话
    conn.send(b'hello baby~')
    # 挂电话
    conn.close()
    # 关机
    sercer.close()

    客户端

    import socket
    ​
    client = socket.socket()  # 拿电话
    client.connect(('127.0.0.1',8080))  # 拨号,写的是对方的ip和port
    client.send(b'hello world!')  # 对别人说话
    data = client.recv(1024)  # 听别人说话,接收1024个字节数据
    client.close()  # 挂电话

    总结:

    127.0.0.1是本机回还地址,只能自己识别自己,不能访问其他

    send和recv对应:

    • 不要出现两边都是相同的情况
    • recv是跟内存要数据,至于数据的来源,我们无需考虑

    关于半连接池

    半连接池的概念

    图解:假如上述一共7个对象,首位已在被执行,有5个等待位对应5个对象,还有1个对象在等待进入等待位,server.listen(5)中的5就是5个等待位

    通信循环下的服务端

    import socket
    ​
    server = socket.socket()  # 生成一个对象
    server.bind(('127.0.0.1',8080))  # 绑定ip和port
    server.listen(5)  # 半连接池,最大等待数目
    while True:  # 使服务端24小时不停的提供服务
        conn, addr = server.accept()  # 等到别人来 conn就是类似于双向通道
        print(addr)  # ('127.0.0.1',51323) 客户端的地址
        while True:
            try:
                data = conn.recv(1024)
                print(data)  # b""是二进制数据,针对mac与linux客户端异常退出之后,服务端不会报错,只会一直接收b""
                if len(data) == 0: break
                conn.send(data.upper())
            except ConnectionResetError as e:
                printi(e)
                break
        conn.close()

    通信循环下的客户端

    import socket
    ​
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    while True:
        msg = input('>>>:').encode('utf-8')
        if len(msg) == 0: continue
        client.send(msg)
        data = client.recv(1024)
        print(data)

    TCP粘包问题

    TCP特点

    • 会将数据量比较小的一次性发给对方

    如何将数据打包成固定的数据包(运用struct模块)?

    import struct
    ​
    res = 'ssssssssssssssssssssssss'# 当原始数据特别大时,i模式打包不了,需要更换其他模式
    res1 = struct.pack('i',len(res))
    print(len(res1))  # 打包后的数据长度为4
    ​
    res2 = struct.unpack('i',res1)[0]
    print(res2)  # 解包之后的真实数据长度

    如果遇到数据量特别大时,又该如何解决?

    import struct
    # 当原始数据特别大的时候 i模式打包不了 需要更换模式?
    # 如果遇到数据量特别大的情况 该如何解决?
    d = {
        'name':'jason',
        'file_size':34554354353453545245345324565465465654,
        'info':'为大家的骄傲'
    }
    import json
    json_d = json.dumps(d)
    print(len(json_d))
    ​
    res1 = struct.pack('i',len(json_d))
    print(len(res1))
    ​
    res2 = struct.unpack('i',res1)[0]
    print('解包之后的',res2)

    关于subprocess的应用

    import subprocess
    ​
    cmd = input('cmd>>>:')
    obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
    print(obj.stdout.read().decode('gbk'))  # 正确命令返回的结果
    print(obj.stderr.read().decode('gbk'))  # 错误的命令返回的结果
    # subprocess获取到的数据 拿完就没有了  不能重复的拿
    print(obj.stdout.read().decode('gbk'))  # 正确命令返回的结果
    print(obj.stderr.read().decode('gbk'))  # 错误的命令返回的结果

    解决粘包问题的服务端

    import socket
    import subprocess
    import struct
    import json
    ​
    server = socket.socket()
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    while True:
        conn, addr = server.accept()
        while True:
            try:
                cmd = conn.recv(1024)
                if len(cmd) == 0:break
                cmd = cmd.decode('uft-8')
                obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
                res = obj.stdout.read() + obj.stderr.read()
                json_d = json.dumps(d)
                # 先制作一个字典的报头
                header = struct.pack('i',len(json_d))
                # 发送字典报头
                conn.send(header)
                # 发送字典
                conn.send(json_d.encode('utf-8'))
                # 再发真实数据
                conn.send(res)
            except ConnectionResetError:
                break
        conn.close()   

    解决粘包问题的客户端

    import socket
    import struct
    import json
    ​
    client = socket.socket()
    client.connect(('127.0.0.1',8080))
    while True:
        msg = input('>>>:').encode('utf-8')
        if len(msg) == 0: continue
        client.send(msg)
        # 先接收字典报头
        header_dict = client.recv(4)
        # 解析报头 获取字典的长度
        dict_size = struct.unpack('i',header_dict)[0]  # 解包时一定要加上索引0
        # 接收字典数据
        dict_bytes = client.recv(dict_size)
        dict_json = json.loads(dict_bytes.decode('utf-8'))
        # 从字典中获取真实数据
        recv_size = 0
        real_data = b''
        while recv_size < dict_json.get('file_size'):
            data = client.recv(1024)
            real_data += data
            recv_size += len(data)
        print(real_data.decode('gbk'))

    解决粘包问题

    服务端

    • 1.先制作一个发送给客户端的字典
    • 2.制作字典的报头
    • 3.发送字典的报头
    • 4.发送字典
    • 5.再发真实数据

    客户端

    • 1.先接受字典的报头
    • 2.解析拿到字典的数据长度
    • 3.接受字典
    • 4.从字典中获取真实数据的长度
    • 5.接受真实数据

    案例实现:写一个上传电影的功能

    • 1.循环打印某一个文件夹下面的所有的文件
    • 2.用户选取想要上传的文件
    • 3.将用户选择的文件上传到服务端
    • 4.服务端保存该文件
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 排队打水问题
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    Java实现 蓝桥杯VIP 算法提高 特殊的质数肋骨
    现在使用控件, 更喜欢继承(覆盖控件已有的函数,很奇怪的一种使用方式)
    Controls 属性与继承 TShape 类的小练习(使用TShape可以解决很多图形问题)
    QT创建窗口程序、消息循环和WinMain函数(为主线程建立了一个QEventLoop,并执行exec函数)
  • 原文地址:https://www.cnblogs.com/zhukaijian/p/11317112.html
Copyright © 2011-2022 走看看