【问题标题】:Writing messages of different types through sockets in C在 C 中通过套接字编写不同类型的消息
【发布时间】:2012-07-01 04:31:45
【问题描述】:

我想通过 C 中的 TCP 套接字连接向我的 Binder 类发送消息。我必须通过此连接使用write() 方法。在一条消息中发送所有信息的最佳方法是什么?

【问题讨论】:

  • 你尝试了什么,它是如何失败的?

标签: c sockets


【解决方案1】:

无法保证您可以在一次读/写操作中发送/接收所有数据; 太多的因素可能会影响质量/数据包大小/连接稳定性/等。
这个question/answer explains it

一些C-examples here.
socket programming in C的一个很好的解释。
一个quick overview of TCP/IP

关于发送不同类型的消息:
您发送的数据来自您的服务器应用程序,由您的客户端应用程序接收,然后客户端应用程序可以以任何喜欢的方式解释此数据。

【讨论】:

  • 请同时输入链接中的内容。如果这些链接消失,这将有助于未来的读者。
  • @Thrustmaster:我不知道:这些链接可能是受版权保护的材料? (互联网档案可能会有所帮助 - 他们如何处理受版权保护的材料?)无论如何,我认为鲁莽地复制他人的内容不会很好。
  • 我的意思是答案应该是自包含的;并且不必是整个内容的拉动。现在不能链接,但是Meta上有很多关于它的帖子。
  • @Thrustmaster:这个元链接:meta.stackexchange.com/a/113563/136755 和这个:meta.stackexchange.com/q/132911/136755 似乎涵盖了这个问题。
【解决方案2】:

如果您的数据是相关的,您可以在单独的标头中创建一个结构,并在客户端和服务器代码中使用它,并发送此结构的一个变量。如果它不相关,那么我不确定您为什么需要将它们作为一条消息发送。

【讨论】:

    【解决方案3】:

    如果您想通过单次写入和单次读取来传输和接收数据,那么您必须使用数据报套接字。由于数据报套接字是无连接的,所以不能使用write/read。相反,您使用sendto/recvfromsendmsg/recvmsg。由于数据报套接字不可靠,因此您必须实现自己的协议以容忍乱序数据传递和数据丢失。

    如果您不想处理数据报套接字的不可靠特性,那么您需要一个流套接字。由于连接了流套接字,因此传输的数据得到保证并且有序。如果您发送的数据始终具有相同的大小,那么您可以通过对send 调用使用阻塞模式然后在recv 调用中传入MSG_WAITALL 来模拟数据报。

    #define MY_MSG_SIZE 9876
    
    int my_msg_send (int sock, const void *msg) {
        int r, sent = 0;
        do {
            r = send(sock, (const char *)msg + sent, MY_MSG_SIZE - sent,
                     MSG_NOSIGNAL);
            if (r <= 0) {
                if (r < 0 && errno == EINTR) continue;
                break;
            }
            sent += r;
        } while (sent < MY_MSG_SIZE);
        if (sent) return sent;
        return r;
    }
    
    int my_msg_recv (int sock, void *msg) {
        int r, rcvd = 0;
        do {
            r = recv(sock, (char *)msg + rcvd, MY_MSG_SIZE - rcvd, MSG_WAITALL);
            if (r <= 0) {
                if (r < 0 && errno == EINTR) continue;
                break;
            }
            rcvd += r;
        while (rcvd < MY_MSG_SIZE);
        if (rcvd) return rcvd;
        return r;
    }
    

    请注意,该软件仍需处理某些错误情况。对于EINTR,需要重试I/O操作。对于其他错误,数据的交付或检索可能不完整。但一般来说,对于阻塞套接字,我们期望上面的 do-while 循环只进行一次迭代。

    如果您的消息并不总是相同的大小,那么您需要一种方法来框架消息。框架消息意味着您需要一种方法来检测消息的开头和结尾。也许通过流式套接字构建消息的最简单方法是在消息之前加上它的大小。然后接收者将首先读出大小,然后读取消息的其余部分。您应该能够轻松地调整 my_msg_sendmy_msg_recv 的示例代码来做到这一点。

    最后,还有你的信息本身的问题。如果消息的大小并不总是相同,这可能意味着消息中有一个或多个可变长度记录。示例是值数组或字符串。如果发送者和接收者都同意记录的顺序,那么在每个可变长度记录之前加上它的长度就足够了。因此,假设您的消息具有以下结构:

    struct my_data {
        const char *name;
        int address;
        int *types;
        int number_of_types;
    };
    

    那么你可以像这样表示struct my_data 的一个实例:

    NAME_LEN : 4 bytes
    NAME : NAME_LEN bytes
    ADDRESS : 4 bytes
    TYPES_LEN : 4 bytes
    TYPES : TYPES_LEN * 4 bytes
    

    NAME_LEN 将从strlen(msg-&gt;name) 获得,TYPES_LEN 将从msg-&gt;number_of_types 获得其值。发送消息时,前面会加上上面消息的总长度。

    我们一直使用 32 位量来表示长度,这可能足以满足您的目的。通过套接字传输数字时,发送方和接收方必须就数字的字节顺序达成一致。也就是说,数字 1 是表示为0.0.0.1 还是1.0.0.0。这通常可以使用 network byte ordering 来处理,后者使用前者。套接字头文件提供了宏 htonl,它将 32 位值从主机的本机字节顺序转换为网络字节顺序。这在存储一个值时使用,比如NAME_LEN。接收方将使用相应的宏ntohl 将传输的值恢复为主机使用的表示形式。如果主机本机字节顺序已经与网络字节顺序匹配,那么宏当然可以是无操作的。在异构环境中发送和接收数据时,使用这些宏特别重要,因为发送方和接收方可能具有不同的主机字节顺序。

    【讨论】:

      猜你喜欢
      • 2015-01-10
      • 1970-01-01
      • 2012-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-24
      • 1970-01-01
      相关资源
      最近更新 更多