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

socket 可以传输结构体吗?

  •  
  •   sbldehanhan · 2023-06-13 09:48:24 +08:00 · 5171 次点击
    这是一个创建于 564 天前的主题,其中的信息可能已经有所发展或是发生改变。

    例如: 结构体: 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  
       2023-06-13 09:51:37 +08:00   ❤️ 5
    什么结构体不结构体,最终都是 序列化、反序列化
    throcean
        2
    throcean  
       2023-06-13 09:52:54 +08:00
    当然可以了
    eMvKtXL36Ft5mIaV
        3
    eMvKtXL36Ft5mIaV  
       2023-06-13 09:53:45 +08:00
    可以的,属性没有指针就正常
    luvfinn
        4
    luvfinn  
       2023-06-13 09:54:44 +08:00
    这边封包传过去,那边解包后挂一个指针数据不就读出来了吗
    changnet
        5
    changnet  
       2023-06-13 09:55:07 +08:00
    当然可以。不过这时候就得考虑内存对齐了,结构体是否为 POD 类型了。
    tool2d
        6
    tool2d  
       2023-06-13 09:55:13 +08:00
    需要先传结构体大小,recv 有可能只接受一般数据。
    tool2d
        7
    tool2d  
       2023-06-13 09:56:25 +08:00
    recv 有可能只接收一半数据。

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

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

    你要先把结构体转化成二进制数据. 然后再解析出来. 也就是楼上说的序列化和反序列化的概念.
    haikea
        18
    haikea  
       2023-06-13 10:21:47 +08:00
    可以传,你自己定好协议就行,比如数据包从哪一位到哪一位是结构体,结构体怎么解析
    Guaidaodl
        19
    Guaidaodl  
       2023-06-13 10:23:05 +08:00
    至于如何把结构体转化成二进制数据, 这个方法就实在太多了. 比如直接平铺数据, 或者用 ProtoBuf 协议. 甚至可以将结构体转换成 JSON 字符串再转成 utf-8 的流
    kagetu
        20
    kagetu  
       2023-06-13 10:23:47 +08:00
    我觉得你能问这个问题,可能与我当初无法理解“报文”是个什么概念差不多,不知道自己到底有没有收到这个叫“报文”的东西,后来理解了(也不知道是不是真理解)才知道其实就是指发送的数据,只不过是一个名字的问题。
    那么再回来看你的这个问题,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  
       2023-06-13 10:26:38 +08:00   ❤️ 4
    传结构体不知道, 我一直在研究怎么通过 socket 传送小动物, 研究好了我就能做传送阵了.
    coderxy
        22
    coderxy  
       2023-06-13 10:26:41 +08:00
    结构体是你这个语言的概念, 对于网络设备来说,都是二进制数据流。
    kagetu
        23
    kagetu  
       2023-06-13 10:29:55 +08:00
    所以复杂的地方还不只在于你能不能发送结构,你还要和对方商量后怎么识别到底发的什么,要发送的数据有多大,比如你发了 8 个 00001111 ,由于网卡了一下,对方只接收到了前 4 个 0000 就以后全接完了。那他读取时肯定就不对了。
    所以除了在首位加个区别发送数据类型的标志,你可能还需要再加个发送的数据大小的。具体根据你的实际情况去自定义就可以了,一般是把数据的大小放在首位。
    Maboroshii
        24
    Maboroshii  
       2023-06-13 10:30:30 +08:00
    大小端和对齐要处理好
    gps949
        25
    gps949  
       2023-06-13 10:30:39 +08:00
    传啥不是传二进制啊,只要你说的“结构体”和二进制间 encode 、decode 规则确定了,啥都行。这里面 encode 、decode 就涉及序列化、字符编码这些了。
    xqdoo00o
        26
    xqdoo00o  
       2023-06-13 10:32:56 +08:00
    建议看下 Protobuf 或者 FlatBuffers
    nuk
        27
    nuk  
       2023-06-13 10:35:01 +08:00
    如果是同一个程序可以,不然不同的编译器,不同的编译 flag 都会造成差异
    lincanbin
        28
    lincanbin  
       2023-06-13 10:40:11 +08:00
    序列化就可以,json 或者 pb 或者其他
    duke807
        29
    duke807  
       2023-06-13 10:43:42 +08:00 via Android
    可以
    为了扩展性,建议用 msgpack

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

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

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

    因为用起来超级麻烦啊

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