TCP/IP - 在 C/C++ 中发送 HEX 数据包以控制中继板

TCP/IP - Send HEX packets in C/C++ to control a relay board

我有一个电子板,它有 16 个继电器,它在 TCP/IP 上工作。 开发板的IP地址为192.168.1.4,端口为3000。 我想在 Ubuntu.

下用 C/C++ 控制它

有一个十六进制命令列表,可以使用这些命令远程打开和关闭板上的每个继电器。 这是列表:

    "580112000000016C",  // switch on the relay 1
    "580111000000016B",  // switch off the relay 1
    "580112000000026D",  // switch on the relay 2
    "580111000000026C",  // switch off the relay 2
    "580112000000036E",  // and so on..

通过在 Ubuntu:

下发送命令行命令,我能够正确地打开和关闭每个继电器
echo '580112000000016C' | xxd -r -p | nc 192.168.1.4 3000

以上代码正确打开继电器。

我想对 C/C++ 代码做同样的事情,因为我想从 WxWidgets 应用程序控制面板。 目前,我从基础开始,我只是使用 C/C++ 代码来测试 tcp 连接。

这是我的代码:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include <unistd.h>

using namespace std;

int main(int argc, char**argv) {
    int sockfd, n;
    struct sockaddr_in servaddr;
    char sendline[1000];
    char recvline[1000];

    std::string serveraddr = "192.168.1.4";

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(serveraddr.c_str());
    servaddr.sin_port = htons(3000);

    connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

    int number = 0x580112000000016C;

    int sendsize;
    sendsize = snprintf(sendline, sizeof(sendline), "%x", number);

    send(sockfd, sendline, sendsize * sizeof(char), 0);

}

我应该如何发送 HEX 命令? 当我编译此代码时,我收到此警告:

g++ tcp.c -o tcp tcp.c: In function ‘int main(int, char**)’:
tcp.c:29:18: warning: overflow in implicit constant conversion [-Woverflow]
     int number = 0x580112000000016C;
                  ^~~~~~~~~~~~~~~~~~

显然这个程序不工作。

你能帮帮我吗?我是初学者,不了解如何正确发送 HEX 指令。

编辑1 这是尼尔森建议后的代码。

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <cstring>
#include <string>
#include <unistd.h>

using namespace std;

int main(int argc, char**argv) {
    int sockfd, n;
    struct sockaddr_in servaddr;
    char sendline[1000];
    char recvline[1000];

    std::string serveraddr = "192.168.1.4";

    sockfd = socket(AF_INET, SOCK_STREAM, 0);

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(serveraddr.c_str());
    servaddr.sin_port = htons(3000);

    connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

    //int number = 0x580112000000016C;
    //uint8_t buffer[] = { 0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x01, 0x6C};
   const char *command = "580112000000016C\n";

int bytes_to_send = strlen(command);
int bytes_sent = 0;

while(bytes_sent < bytes_to_send) {
    n = send(sockfd, command + bytes_sent, bytes_to_send - bytes_sent, 0);
    if ( n < 0 ) {
        printf("Error!\n"); break;
    }
    bytes_sent += n;
}
}

然而,不幸的是,它仍然不起作用。

正如编译器所说,int 类型不够大,无法容纳数据。您应该使用 unsigned long long#include<stdint.h> 并使用 uint64_t。但是,直接定义数据是最简单的 const char command[] = {0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x01, 0x6C};.

您还应该检查 send() 的 return 值以验证所有数据已发送。

更新 - 使用 send():

int bytes_to_send = sizeof(command)/sizeof(command[0]);
int bytes_sent = 0;
int n;
while(bytes_sent < bytes_to_send) {
    n = send(sockfd, command + bytes_sent, bytes_to_send - bytes_sent, 0);
    if ( n < 0 ) {
        printf("Error!\n"); break;
    }
    bytes_sent += n;
}

这个

const char *command = "580112000000016C\n";

不会按原样工作,因为那是一个 ASCII 字符串(或您的本地实现使用的任何字符集),您需要解析它以获得数字形式:您缺少 [= 的等价物14=] 原始管道的步骤。 (你还应该删除尾随的 \n 除非你真的想通过网络发送它,但这不是这里的主要问题)。

接下来,这个

int number = 0x580112000000016C;

除非该文字适合您平台的 int,否则将无法工作。由于这通常是 32 位,因此您不能在其中放入 64 位数字。

固定变量大小

#include <cstdint>

uint64_t number = 0x580112000000016Cull;

可能会起作用,但现在您遇到了一个新问题:此值发送到网络的实际八位字节序列取决于您平台的字节顺序。

这个

uint8_t buffer[] = { 0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x01, 0x6C};

确实应该有效:至少,八位位组值是正确的,而且您知道它们的顺序是正确的。

如果您非常喜欢编写第一个(字符串)形式,那么这就是字符串应转换为的输出。


对方法的快速评论:

您有一个可用的 shell 管道。这意味着您可以使用 nc -l 编写服务器管道,以准确查看您的 C++ 程序实际发送的内容,以进行比较。这样做。

这也意味着您可以先将程序编写为 替换管道的 nc 阶段:这使您可以确认网络代码在隔离状态下是否正常工作.

当我说您可以将字符串形式转换为八位字节数组形式时,我的意思是:首先让八位字节数组工作。然后,编写一个函数将字符串形式转换为八位字节数组,最后测试该函数的输出是否相同。

如果您注意到方法中的模式,希望始终将问题分解为更小的子问题。如果你写了一个程序,发现你不确定你的错误是在网络代码中,还是在字符串格式化或缓冲区操作中,那么你一次写了太多代码并且等待太长时间来测试它。

shell命令xxd -r -p将十六进制字符串作为输入并将其解码为原始二进制文件以进行输出。

echo '580112000000016C' 输出十六进制字符串,xxd 然后解码并输出为二进制,nc 然后发送。

您发送的不是套接字代码中的原始二进制文件,而是十六进制字符串。

试试这个:

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>
#include <unistd.h>
using namespace std;

int main(int argc, char**argv)
{
    int sockfd, n;
    struct sockaddr_in servaddr;

    std::string serveraddr = "192.168.1.4";

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if ( sockfd < 0 )
    {
        cerr << "Error creating socket! " << strerror(errno) << endl;
        return 1;
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = inet_addr(serveraddr.c_str());
    servaddr.sin_port = htons(3000);

    if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
    {
        cerr << "Error connecting socket!" << strerror(errno) << endl;
        close(sockfd);
        return 1;
    }

    uint8_t command[] = {0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x01, 0x6C}; // switch on the relay 1
    //uint8_t command[] = {0x58, 0x01, 0x11, 0x00, 0x00, 0x00, 0x01, 0x6B}; // switch off the relay1
    //uint8_t command[] = {0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x02, 0x6D}; // switch on the relay 2
    //uint8_t command[] = {0x58, 0x01, 0x11, 0x00, 0x00, 0x00, 0x02, 0x6C}; // switch off the relay 2
    //uint8_t command[] = {0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x03, 0x6E}; // and so on..

    int bytes_to_send = sizeof(command);
    int bytes_sent = 0;

    do
    {
        n = send(sockfd, command + bytes_sent, bytes_to_send - bytes_sent, 0);
        if ( n < 0 )
        {
            cerr << "Error writing to socket!" << strerror(errno) << endl;
            close(sockfd);
            return 1;
        }
        bytes_sent += n;
    }
    while (bytes_sent < bytes_to_send);

    close(sockfd);
    return 0;
}