QTcpSocket和TCP填充

时间:2017-07-13 07:28:54

标签: c++ qt sockets tcp qtcpsocket

美好的一天。

我发送了一个自定义协议,用于通过TCP进行日志记录,如下所示:

Timestamp (uint32_t -> 4 bytes)
Length of message (uint8_t -> 1 byte)
Message (char -> Length of message)

时间戳转换为BigEndian以进行传输,一切都正常,除了一个小细节:填充

时间戳是自己发送的,但是我的应用程序(使用Ubuntu下的BSD套接字)不是仅发送时间戳(4个字节),而是自动在消息中附加两个字节的填充。

Wireshark正确认识到这一点并将两个无关字节标记为填充,但QTcpSocket(Qt 5.8,mingw 5.3.0)显然假设两个额外字节实际上是有效负载,这显然会混淆我的协议。

我有什么办法教导' QTcpSocket忽略填充(就像它应该)或以任何方式摆脱填充?

我想避免做整个'创建一个足够大的缓冲区并预先组装整个数据包,以便在可能的情况下一次性发送出来。

非常感谢。

因为有人问过,用于发送数据的代码是:

return 
    C->sendInt(entry.TS)        &&
    C->send(&entry.LogLen, 1)   &&
    C->send(&entry.LogMsg, entry.LogLen);

其中sendInt声明为(Src是参数):

Src = htonl(Src);
return send(&Src, 4);

其中'发送'声明为(SourceLen作为参数):

char *Src = (char *)Source;
while(Len) {
    int BCount = ::send(Sock, Src, Len, 0);
    if(BCount < 1) return false;
    Src     += BCount;
    Len     -= BCount;
}
return true;

:: send是标准的BSD发送功能。

通过QTcpSocket

完成阅读
uint32_t timestamp;
if (Sock.read((char *)&timestamp, sizeof(timestamp)) > 0)
{
    uint8_t logLen;
    char message[256];            
    if (Sock.read((char *)&logLen, sizeof(logLen)) > 0  &&
        logLen > 0                                      &&
        Sock.read(message, logLen) == logLen
    ) addToLog(qFromBigEndian(timestamp), message);
}

SockQTcpSocket实例,已连接到主机,addToLog是处理函数。

另外需要注意的是,发送方需要在嵌入式系统上运行,因此使用QTcpServer不是一个选项。

3 个答案:

答案 0 :(得分:3)

您的读取逻辑似乎不正确。你有......

uint32_t timestamp;
if (Sock.read((char *)&timestamp, sizeof(timestamp)) > 0)
{
    uint8_t logLen;
    char message[256];            
    if (Sock.read((char *)&logLen, sizeof(logLen)) > 0  &&
        logLen > 0                                      &&
        Sock.read(message, logLen) == logLen
    ) addToLog(qFromBigEndian(timestamp), message);
}

来自QTcpSocket::read(data, MaxSize)的文档......

  

从设备读取最多maxSize字节为数据,并返回   读取的字节数

如果您对Sock.read的一次调用读取部分数据怎么办?你基本上丢弃这些数据而不是缓冲它以便下次重用。

假设你有一个适当的范围QByteArray ...

QByteArray data;

你的阅读逻辑应该更符合......

/*
 * Append all available data to `data'.
 */
data.append(Sock.readAll());

/*
 * Now repeatedly read/trim messages from data until
 * we have no further complete messages.
 */
while (contains_complete_log_message(data)) {
  auto message = read_message_from_data(data);
  data = data.right(data.size() - message.size());
}

/*
 * At this point `data' may be non-empty but doesn't
 * contain enough data for a complete message.
 */

答案 1 :(得分:1)

如果填充的长度始终是固定的,则只需添加socket->read(2);即可忽略2个字节。

另一方面,它可能只是冰山一角。你用什么来读写?

答案 2 :(得分:0)

您不应该调用发送三次但只调用一次。要转换为BigEndian,您可以使用Qt functions并将所有内容写入单个缓冲区,并且只调用send一次。这不是你想要的,但我认为这是你需要做的事情,它应该很容易,因为你已经知道你的消息的大小。您也不需要离开Qt世界来发送消息。