1

我需要使用 IrDA 套接字通信从设备发送命令和读取响应。尽管打包命令相当简单,但无法确定响应的预期大小。例如,命令“GET_ERRORS”会导致设备返回从0 到 n的数据, \n每行最多 80 个字节的分隔行。我在这里阅读了帖子* ,但设备未向我提供 实际数据块 * 之前的 *** 标头。

[编辑]
这是 GET_ERRORS 命令的典型响应(为便于阅读而缩写):

Date       Time     Fault
10/12/2000 02:00:00 3f46
10/12/2000 02:00:00 bcf5
10/12/2000 02:00:00 1312
10/12/2000 02:00:00 a334
10/12/2000 02:00:00 b212
10/12/2000 02:00:00 b212
10/12/2000 02:00:00 c43a
%

如果我知道返回的数据长度,则此示例(来自 SO post HERE)效果很好:

int recv_all(int sockfd, void *buf, size_t len, int flags)
{
    size_t toread = len;
    char  *bufptr = (char*) buf;

    while (toread > 0)
    {
        ssize_t rsz = recv(sockfd, bufptr, toread, flags);
        if (rsz <= 0)
            return rsz;  /* Error or other end closed cnnection */

        toread -= rsz;  /* Read less next time */
        bufptr += rsz;  /* Next buffer position to read into */
    }

    return len;
}

但是如果我想接收未知数量的数据,我唯一知道要做的就是声明一个大缓冲区,然后将它传递给这样的东西:

int IrdaCommReceive(int irdaCommSocket, void* largeBuf, size_t len, int* rcvd)
{
    char *bufptr = (char *)largeBuf;

    int bytesRcvd = recv(irdaCommSocket, bufptr, len, 0);

    if (bytesRcvd < 0) return WSAGetLastError();
    *rcvd = bytesRcvd;

    return 0;
}

有没有更好的方法来为不确定大小的套接字数据编写接收函数?

4

3 回答 3

5

必须以某种方式构建套接字消息以保证通信的完整性。例如,UDP 数据报套接字发送/接收自包含消息,数据不能跨越消息边界。TCP 流套接字没有该限制,因此必须改为使用描述消息长度的标头或不出现在消息数据中的唯一终止符来分隔逻辑消息。IrDA 插座也不例外。

由于您没有显示GET_ERRORS响应的实际数据或任何代码,但确实说响应消息的前面没有标头,因此只剩下两种可能性:

  1. 传输级别的消息帧。如果您使用该SOCK_DGRAM类型创建 IrDA 套接字,则会为您处理此问题。如果您recv()使用的缓冲区太小,则该消息将被丢弃并出现WSAEMSGSIZE错误。

  2. 在应用程序级别进行消息定界。如果您使用该SOCK_STREAM类型创建 IrDA 套接字,则必须自己处理。你提供一个任意的缓冲区,并recv()用任何可用的数据填充它,直到请求的大小。您只需recv()再次调用以接收更多数据,并根据需要继续操作,直到找到您要查找的数据。

我假设是后者,因为这就是 IrDA 通常与套接字一起使用的方式。我找不到任何基于 IrDA 的GET_ERRORS命令的任何在线文档(你有这样的文档吗?),但我必须假设数据有一个你没有考虑的终止分隔符。它可能会向您发送 0-n 行,但我敢打赌最后一行的长度为 0 或等值。在这种情况下,您只需recv()循环调用,直到收到该终止符。

于 2013-09-24T19:39:42.570 回答
1

你可以试试这样的。

将您的命令发送到设备。然后在一个循环中调用套接字上的 epoll() 或 select() 来测试套接字是否已准备好接收数据。如果 select() 返回表明套接字已准备好读取数据,则重复调用 recv() 直到收到 \n,标记一行的结尾。那个 recv_all() 很适合这个目的。根据需要处理该行,然后返回循环的开头。

您可以使用 select() 上的超时来允许您的程序关闭并执行其他操作,以判断何时没有更多数据来自设备等。修改超时参数的 select 实现在这里可能很有用。

当然,这假设 select() 适用于 Windows 上的 irda 套接字......

于 2013-09-24T20:33:03.270 回答
1

只使用 realloc 将是我天真的建议。这是我在这里收集的一些旧代码的内部循环。

///////////// step 3 - get received bytes ////////////////
    // Receive until the peer closes the connection
    hdr->contentLen = 0;
    while(1)
    {
        memset(hdr->readBuffer, 0, bufSize);
        hdr->thisReadSize = recv (hdr->conn, hdr->readBuffer, bufSize, 0);
        if ( hdr->thisReadSize <= 0 )
            break;

        // nasty - but seemingly necessary. Headers don't always include the content-length
        // without a known content length, we cannot accurately allocate the memory required to hold
        // the full (header+message) response. Therefore, we re-allocate our memory each time a successful read is performed.
        // the alternative would be to write directly to file, or allocate (hopefully) more memory than we
        // need (Too bad if we allocate 10 Mb & a resource is 10.1 Mb !)
        hdr->result = (char*)realloc(hdr->result, hdr->thisReadSize+hdr->contentLen);

        memcpy(hdr->result+hdr->contentLen, hdr->readBuffer, hdr->thisReadSize);
        hdr->contentLen += hdr->thisReadSize;
    }
于 2013-09-24T18:57:29.793 回答