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.服务端保存该文件
  • 相关阅读:
    arcgis api for javascipt 加载天地图、百度地图
    百度地图通过经纬度获取地址信息
    通过百度获取IP地址对应的经纬度
    黑马lavarel教程---13、分页
    legend3---6、legend3爬坑杂记
    黑马vue---1-7、vue杂记
    黑马在线教育项目---15-16、datatables插件
    尚学堂requireJs课程---3、私有和公有属性和方法
    尚学堂requireJs课程---2、模块
    尚学堂requireJs课程---1、作用域回顾
  • 原文地址:https://www.cnblogs.com/zhukaijian/p/11317112.html
Copyright © 2011-2022 走看看