• socket进阶


    1、关于send和sendall的区别

    sendall:
    Send a data string to the socket.  For the optional flags
    argument, see the Unix manual. This calls send() repeatedly
    until all data is sent. If an error occurs, it's impossible
    to tell how much data has been sent.
    send:
    Send a data string to the socket.  For the optional flags
    argument, see the Unix manual. Return the number of bytes
    sent; this may be less than len(data) if the network is busy.
    有上面两段话可知,send发送数据(数据需为bytes)可能会小于要发送的数据长度,返回的是已经发送的字节数,sendall就是循环调用send直到数据全部发送完成,所以下面两段代码是等价的
    client = socket.socket()       #创建一个套接字实例
    client.connect(addr)           #将套接字连接到远程地址
    
    client.sendall(b'hello,world')     #发送数据,只能是bytes形式,等同于下面这段
    
    # buffer = b'hello,world'
    # while buffer:
    #     b = client.send(buffer)
    #     buffer = buffer[b:]

    2、粘包问题

    先看段代码

     1 import socket
     2 import time
     3 addr = ("localhost",1024)
     4 
     5 server = socket.socket()  #创建一个socket实例
     6 
     7 server.bind(addr)  #绑定套接字绑定到本地地址,格式为(地址,端口)
     8 server.listen(5)    #监听端口
     9 
    10 conn,addr = server.accept() #等待请求
    11 print(conn,addr)#conn就是客户端连接过来而在服务器端为其生成的一个实例,addr客户端地址,格式为(地址,端口)
    12 
    13 
    14 
    15 data1 = conn.recv(1024)  #接收数据
    16 data2 = conn.recv(1024)
    17 print("data1:{},data2:{}".format(data1,data2))
    18 
    19 server.close()  #关闭连接
     1 import socket
     2 
     3 addr = ("localhost",1024)
     4 
     5 client = socket.socket()       #创建一个套接字实例
     6 client.connect(addr)           #将套接字连接到远程地址
     7 
     8 client.send(b'hello')     #发送数据,只能是bytes形式
     9 client.send(b'world')
    10 
    11 client.close()                         #关闭连接

    这里客户端连续发送两次,服务器端接收两次,我们想要的结果是,依次收到hello和world,但是结果是这样子的:

    <socket.socket fd=420, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 1024), raddr=('127.0.0.1', 51569)> ('127.0.0.1', 51569)
    data1:b'helloworld',data2:b''

    连在一起发送过来了

    所谓粘包问题主要还是C/S两端数据传输时  因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
    根本原因:
    粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。udp并不会出现该问题
    解决方法:
    1、自定义字典类型 的数据报头{文件名:a,文件的size:1090}计算出该报头的长度(len(字节))
    2、使用struct.pack('i',报头长度(一个数字))把一个数字压缩成固定的size 4个字节,发送给对端。

    3、对端 struct.unpack(‘i’,recv(4))接收固定大小4个字节;这就是接收到了 报头的长度。

    4.recv(报头长度)这就是发送过来的报头信息了
    对上面例子进行优化:
    #client
    
    import socket
    import struct
    addr = ("localhost",1024)
    
    client = socket.socket()       #创建一个套接字实例
    client.connect(addr)           #将套接字连接到远程地址
    
    msg = b'hello'
    msg1 = b'world'
    msg_size = len(msg)
    
    
    a = struct.pack('i',msg_size)
    client.send(a)             #发送数据长度
    client.send(b'hello')     #这里我们只要处理这个粘包就可以了
    client.send(msg1)
    
    client.close()                         #关闭连接
    
    
    #server
    import socket
    import time
    addr = ("localhost",1024)
    
    server = socket.socket()  #创建一个socket实例
    
    server.bind(addr)  #绑定套接字绑定到本地地址,格式为(地址,端口)
    server.listen(5)    #监听端口
    
    conn,addr = server.accept() #等待请求
    print(conn,addr)#conn就是客户端连接过来而在服务器端为其生成的一个实例,addr客户端地址,格式为(地址,端口)
    
    
    size = conn.recv(4)   #接收报头,4bytes
    print(size[0])
    data1 = conn.recv(size[0])  #size[0]就是要接收的数据长度
    data2 = conn.recv(1024)
    print("data1:{},data2:{}".format(data1,data2))
    
    server.close()  #关闭连接
    
    
    

      

     


  • 相关阅读:
    大牛总结的Linux提权Exp合集
    CTF中图片隐藏文件分离方法总结
    解压报错gzip: stdin: not in gzip format tar: Child returned status 1 tar: Error is not recoverable: exiting now的解决方法
    CTF中常见Web源码泄露总结
    解决“当前扩展缓存策略没有进行注册”的错误
    【红色警报】XXE 高危漏洞将大面积影响微信支付安全,可能导致系统沦陷,请升级你的系统!
    Senparc.Weixin SDK v5.0 升级公告
    使用 VSTS 进行 CI 的过程中,无法识别 .NET Core 2.x 的情况处理
    .net core DI 注册 Lazy<> 类型
    【备忘】ASP.NET MVC 5 升级到 ASP.NET Core MVC 的部分变化
  • 原文地址:https://www.cnblogs.com/zj-luxj/p/7113051.html
Copyright © 2020-2023  润新知