【问题标题】:Receiving binary data over socket in c++在 C++ 中通过套接字接收二进制数据
【发布时间】:2014-03-16 15:07:19
【问题描述】:

我有一个连接到本地网络的 MCU 单元。该设备每秒通过 TCP 套接字发送其状态数据 100 次。通过同一个套接字,我还可以发出命令和数据。

我正在尝试编写一个简单的 TCP 客户端,它可以连接到设备、获取数据并在请求时发送命令。目前我可以毫无问题地发送命令(命令以字符串形式发送,这很有效)。我的问题是从设备接收数据。

我从设备手册中知道,数据首先应该是 32 位整数,然后是大约 30 个双精度数的数组)

我正在建立一个新线程,我想连续拦截数据并处理它:

void *com::setListenerSocket(void* threadSocketData) {
        struct socketData *sockData;
        sockData = (socketData*)threadSocketData;
        com::tcp_client cListener;

        struct timeval timeout;
        timeout.tv_sec=0;
        timeout.tv_usec=12500;

    //  setsockopt(cListener, IPPROTO_TCP, SO_RCVTIMEO, (char *)timeout, sizeof(timeout)); //no effect
        cListener.connect(sockData->host , sockData->port);
        cout << "listener thread started" << endl;
        cout << "host=" << sockData->host << " at port=" << sockData->port << endl;

        cListener.send_data("Listener Reporting\n");
    while(listenerRun){
        listenerHB++;
        cout << endl <<"*****************************       BGN     *************************************************************"<< endl;
        char *received = new char[PACKETSIZE];
        cListener.receive(PACKETSIZE, received);
        unsigned int test;
        test = (uint32_t) received;
        cout << "received=" << test << endl;
        for(int i = 0; i< PACKETSIZE ; ++i)
          cout << received[i] << ", ";
        cout << endl <<"*****************************       END     *********************************************"<< endl;
        robotPacket data;
        //parseData(received, &data);
        //usleep(1 MS);
    }
    cout << "listener signing out" << endl;;
    pthread_exit(NULL);
}

监听方法是:

void com::tcp_client::receive(int size, char* buffer) {

  unsigned int bytecnt = 0, archive = 1;

  while(bytecnt < (unsigned int)size) {
//     bytecnt += recv(sock , &buffer[bytecnt] , size-bytecnt , 0); 
    bytecnt += recv(sock , buffer , size-bytecnt , MSG_WAITALL); 
  }

  unsigned int test;
  test = buffer[0];
  cout << "value=" << test << "¤" << endl;
  }

我把指向缓冲区开头的指针转换成uint32_t,但是结果完全错误(我知道应该是包长度,但是很容易变成几百万)

当我使用 nl 命令时: nc -l 12354 我可以在我的程序中写入文本并且一切都正确

编辑:与更有经验的人会面: 已解决:我自己无法发布解决方案,所以我会放在这里以防有人遇到类似问题。

首先,我希望套接字能够自动处理单个传输的开始位置和结束位置 - 正如 Basyle Starynkevitch 所指出的那样,这是不正确的。但是,TCP 确保数据按顺序输入并且没有任何丢失,因此我可以放心地假设,对于承诺的 500 字节,我将获得 500 字节或其他任何内容。

其次,数据以大端序输入 - 正如 Basyle 所指出的那样。我做了一些位移,但仍然有错误的结果,但是......

最后,我在类型转换方面遇到了另一个问题。我使用的是 char*,而数据是无符号字符。 用 unsigned char* 替换缓冲区 char* 解决了解码问题。

对于需要与通过局域网通信的旧电子设备进行交互的任何人来说,这都是一种解决方案。

如果有人以简洁的形式发布我上面写的内容,我想这个案子可以被认为是关闭的。

【问题讨论】:

  • recv 可能会以否定的bytecnt 失败
  • if (probe = recv(sock , buffer , size-bytecnt , MSG_WAITALL) > 0)bytecnt += probe;现在数据进来的速度要慢得多,但第一个整数仍然很疯狂......什么会导致负字节?
  • 阅读recv(2)。您需要准确了解您正在实施的协议。担心endianness。见htonl(3)endian(3)
  • 是否有可能,我的套接字连接在数据包中间开始侦听并填满了数据包的另一半并以这种方式损坏?套接字客户端能否检测到新数据包何时开始?
  • 在应用程序端,TCP 没有数据包的概念(它只是一个字节流)。见Nagle's algorithm & this answer

标签: c++ linux sockets binary


【解决方案1】:

您可能想查看 tcpclient (http://cr.yp.to/ucspi-tcp/tcpclient.html)。您可以简单地在 tcpclient 下运行您的 C/C++ 程序 - 即 tcpclient 将生成您的程序并打开到服务器的 tcp 连接,并将您的程序的标准输出通过管道传输到服务器,并将服务器的输出通过管道传输到程序的标准输入。这样做的好处是您可以让 tcpclient 承担所有繁重的工作,例如设置套接字等,并且您可以专注于程序的核心功能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-23
    • 2011-08-11
    • 1970-01-01
    • 2015-03-26
    • 2012-10-11
    • 2020-09-08
    • 1970-01-01
    相关资源
    最近更新 更多