V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sbldehanhan
V2EX  ›  C

socket 可以传输结构体吗?

  •  
  •   sbldehanhan · 319 天前 · 4831 次点击
    这是一个创建于 319 天前的主题,其中的信息可能已经有所发展或是发生改变。

    例如: 结构体: struct test { int a; char b[1024]; float c; }; 数据: struct test data; 发送: send(sockfd, &data, sizeof(data), 0); 接收: recv(connfd, &data, sizeof(data), 0);

    50 条回复    2023-10-25 13:44:41 +08:00
    xiangyuecn
        1
    xiangyuecn  
       319 天前   ❤️ 5
    什么结构体不结构体,最终都是 序列化、反序列化
    throcean
        2
    throcean  
       319 天前
    当然可以了
    wtsclwq
        3
    wtsclwq  
       319 天前
    可以的,属性没有指针就正常
    luvfinn
        4
    luvfinn  
       319 天前
    这边封包传过去,那边解包后挂一个指针数据不就读出来了吗
    changnet
        5
    changnet  
       319 天前
    当然可以。不过这时候就得考虑内存对齐了,结构体是否为 POD 类型了。
    tool2d
        6
    tool2d  
       319 天前
    需要先传结构体大小,recv 有可能只接受一般数据。
    tool2d
        7
    tool2d  
       319 天前
    recv 有可能只接收一半数据。

    你可以多看看实际例子,都是要封装一次的。
    hankai17
        8
    hankai17  
       319 天前   ❤️ 4
    粘包警察在此
    dynos01
        9
    dynos01  
       319 天前 via iPad   ❤️ 1
    不建议就这样直接把结构体发出去,因为要考虑内存对齐,字节序,后续拆分也麻烦。推荐的办法是序列化成字符串(比如 JSON ,或者你自己定义一个也行)发出去,对端再还原回来。
    NessajCN
        10
    NessajCN  
       319 天前
    结构体是语言特性,编译完了就只是连续的数据而已
    你例子里的 struct test 来说,其实就是占据了 4+1*1024+4 个字节的一串 1010101 数字而已
    你在接收端也定义同样的 struct 后,recv()函数就把那串数字按结构体定义分配到变量所在的地址
    fgwmlhdkkkw
        11
    fgwmlhdkkkw  
       319 天前
    发结构化的数据,可以不需要带长度信息。你可以直接发 json……
    dynos01
        12
    dynos01  
       319 天前 via iPad
    @dynos01 更正一下说法,不一定是字符串,也可以是一段字节。比如一种简单的设计:先 8 个字节传结构体大小,再 4 个字节传 a ,再直接把 b 放进去,最后 4 字节传 c 。对端拆分也好拆分,因为知道总大小,就可以确定接受这么多字节的数据,接着就能拿出三个字段并还原出结构体。涉及大端 /小端的地方一律用网络序,保证兼容性。

    当然最简单的还是去找个序列化 /反序列化库。
    Nazz
        13
    Nazz  
       319 天前   ❤️ 1
    这种原始的方式很容易崩吧
    hahastudio
        14
    hahastudio  
       319 天前
    可以的,不过推荐你看一看 protobuf
    Metre
        15
    Metre  
       319 天前
    可以,注意不同操作系统大小端对齐
    BBCCBB
        16
    BBCCBB  
       319 天前   ❤️ 1
    去看一下序列化和反序列化的概念. 你就懂了
    Guaidaodl
        17
    Guaidaodl  
       319 天前
    socket 就是传二进制数据. 结构体是更上层的抽象啊.

    你要先把结构体转化成二进制数据. 然后再解析出来. 也就是楼上说的序列化和反序列化的概念.
    haikea
        18
    haikea  
       319 天前
    可以传,你自己定好协议就行,比如数据包从哪一位到哪一位是结构体,结构体怎么解析
    Guaidaodl
        19
    Guaidaodl  
       319 天前
    至于如何把结构体转化成二进制数据, 这个方法就实在太多了. 比如直接平铺数据, 或者用 ProtoBuf 协议. 甚至可以将结构体转换成 JSON 字符串再转成 utf-8 的流
    kagetu
        20
    kagetu  
       319 天前
    我觉得你能问这个问题,可能与我当初无法理解“报文”是个什么概念差不多,不知道自己到底有没有收到这个叫“报文”的东西,后来理解了(也不知道是不是真理解)才知道其实就是指发送的数据,只不过是一个名字的问题。
    那么再回来看你的这个问题,send 的操作不管你是要发送结构体还是其它什么,都只是把对应内存地址里的数据弄成 01100101 这样发给对方,对方用 recv 接收到这些 01100101 后放到自己的内存里。
    那对方怎么知道这些 01 是什么呢,前提就是你和对方已经商量好这次发送的是什么。对于你的结构 test ,对方也需要有一个同样的结构 test ,然后 struct a = {0}; recv(connfd, &a, sizeof(a), 0); ,或者 memcpy(&a, &data, sizeof(struct test_b);大概这样就可以了。
    那如果你还有一个 struct test_b ,想要判断到底发 a 还是发 b ,有很多种方法,比如在发送的数据前加一位标志。这个标志 0 就代表后面的数据是 test ,如果是 1 就代表后面的数据是 test_b 。接收方先判断第一位数据是 0 还是 1 ,然后根据结果读取到对应结构。这个 1 你可以用 int ,或者 short ,那对方判断时也记得要对应的用 int 或者 short 。

    如有错误之处,还望谅解。
    bthulu
        21
    bthulu  
       319 天前   ❤️ 4
    传结构体不知道, 我一直在研究怎么通过 socket 传送小动物, 研究好了我就能做传送阵了.
    coderxy
        22
    coderxy  
       319 天前
    结构体是你这个语言的概念, 对于网络设备来说,都是二进制数据流。
    kagetu
        23
    kagetu  
       319 天前
    所以复杂的地方还不只在于你能不能发送结构,你还要和对方商量后怎么识别到底发的什么,要发送的数据有多大,比如你发了 8 个 00001111 ,由于网卡了一下,对方只接收到了前 4 个 0000 就以后全接完了。那他读取时肯定就不对了。
    所以除了在首位加个区别发送数据类型的标志,你可能还需要再加个发送的数据大小的。具体根据你的实际情况去自定义就可以了,一般是把数据的大小放在首位。
    Maboroshii
        24
    Maboroshii  
       319 天前
    大小端和对齐要处理好
    gps949
        25
    gps949  
       319 天前
    传啥不是传二进制啊,只要你说的“结构体”和二进制间 encode 、decode 规则确定了,啥都行。这里面 encode 、decode 就涉及序列化、字符编码这些了。
    xqdoo00o
        26
    xqdoo00o  
       319 天前
    建议看下 Protobuf 或者 FlatBuffers
    nuk
        27
    nuk  
       319 天前
    如果是同一个程序可以,不然不同的编译器,不同的编译 flag 都会造成差异
    lincanbin
        28
    lincanbin  
       319 天前
    序列化就可以,json 或者 pb 或者其他
    duke807
        29
    duke807  
       319 天前 via Android
    可以
    为了扩展性,建议用 msgpack

    或者 msgpack 的小端版本:
    https://github.com/dukelec/msgpackel

    不要用 json 和 protobuf
    sbldehanhan
        30
    sbldehanhan  
    OP
       319 天前
    @coderxy 我可能没表达清除。我知道可以传,其实我是想知道这样传输会不会有什么问题?例如粘包、字节序和内存对齐什么的,我不太清楚在这里会不会造成影响。
    sbldehanhan
        31
    sbldehanhan  
    OP
       319 天前
    @duke807 好的。
    wildman9527
        32
    wildman9527  
       319 天前
    最简单的方法: 把 4 字节指针传过去就好了, 通过指针远程调用, 当然这样也有不好的地方就是: 判断野指针不方便, 因为这个指针太野了.
    Monad
        33
    Monad  
       319 天前
    @duke807 为什么不要用 protobuf 呢~
    JiRouWaZi
        34
    JiRouWaZi  
       319 天前
    本质就是发二进制 ; 你可以直接发结构体的二进制 或者 发字符串这个二进制编码的一个高级格式

    楼上说的大小端判断是因为 cpu 的一些特性,接受方的设备和发送方的特性不一致
    huluye
        35
    huluye  
       319 天前 via iPhone
    最好还是要做一下序列化和反序列化。直接把结构体地址交给 socket 发送的话,结构体内部是否有引用到其他对象是需要考虑的,然后字节序也是个问题。
    huluye
        36
    huluye  
       319 天前 via iPhone
    还有不同编译环境对结构体的字段对齐的处理也会有差异
    fregie
        37
    fregie  
       319 天前
    tcp socket 传输的是字节流,就是一堆字节
    你要自己把结构体编码成一堆字节用 socket 传给另一端,另一端再把这一堆字节解码成结构体
    kenvix
        38
    kenvix  
       319 天前
    最好是序列化,把内存数据直接传递除了要考虑楼上说的字节序、对齐,更大的问题是,如果链路不可信,数据被篡改后是要命的
    mooyo
        39
    mooyo  
       319 天前
    警惕粘包问题 socket 化
    byaiu
        40
    byaiu  
       319 天前
    粘包是有什么说法吗?
    本来写网络程序就要处理流->用户可识别消息,不可避免会收到一半要等什么的。
    这种处理为啥要含沙射影?
    duke807
        41
    duke807  
       319 天前 via Android
    @Monad

    因为用起来超级麻烦啊

    msgpack 和 json 可以无缝切换,扩展性比 json 好,性能更高
    ysc3839
        42
    ysc3839  
       319 天前 via Android
    @byaiu 因为有些教程误人子弟,硬是认为 TCP 发送的是数据包,接收时会“粘在一起”,然而 TCP 传的是字节流,本来就是连续的。
    这就好比有个教程说,文件会“粘块”,写入文件时分两次写入了两块,下次读的时候会粘成一块读出来。
    tomychen
        43
    tomychen  
       319 天前
    粘包警出列
    DreamSpace
        44
    DreamSpace  
       319 天前
    @bthulu 如果有生物体可用的序列化 /反序列化方式的话,感觉也不是不行啊。
    Jirajine
        45
    Jirajine  
       319 天前
    你的结构体不要太大,并且全部都是 inline 的字段,不要包含指针 /引用。
    两边的程序和系统架构也要是一样的。
    之前看到一个用 zig 写的分布式数据库就是这样做的,极致的 kiss ,cast bytes without parse.
    MeteorCat
        46
    MeteorCat  
       319 天前 via Android
    每种语言都有序列化对象方法吧
    MrKrabs
        47
    MrKrabs  
       319 天前
    同一平台确实无所谓
    siweipancc
        48
    siweipancc  
       318 天前 via iPhone
    网络 io 不也是 socket 吗。
    sbldehanhan
        49
    sbldehanhan  
    OP
       318 天前
    @kagetu 讲的很好。感谢。
    alqaz
        50
    alqaz  
       185 天前
    可以,考虑字节序和对其就行了。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1040 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 23:07 · PVG 07:07 · LAX 16:07 · JFK 19:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.