c++ - TCP/IP - 在 C/C++ 中发送 HEX 数据包以控制中继板
问题描述
我有一个有 16 个继电器的电子板,它通过 TCP/IP 工作。板子的IP地址是192.168.1.4,端口是3000。我想在Ubuntu下用C/C++控制。
有一个 HEX 命令列表,可以使用这些命令远程打开和关闭板上的每个继电器。这是列表:
"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 应用程序控制板。目前,我从 base 开始,我只是使用 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);
}
我应该如何发送十六进制命令?当我编译这段代码时,我收到了这个警告:
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 指令。
EDIT1 这是尼尔森建议后的代码。
#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;
}
}
但是,不幸的是,它仍然不起作用。
解决方案
这个
const char *command = "580112000000016C\n";
不会按原样工作,因为这是一个 ASCII 字符串(或您的本地实现使用的任何字符集),您需要对其进行解析以获取数字形式:您缺少与xxd
原始管道的步骤等效的内容。(您还应该删除尾随\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
管道的阶段:这可以让您确认网络代码在隔离中正常工作。
当我说您可以将字符串形式转换为八位字节数组形式时,我的意思是:首先让八位字节数组工作。然后,编写一个函数将字符串形式转换为八位字节数组,最后测试该函数的输出是否相同。
如果您注意到方法论中的一种模式,希望总是将问题分解为更小的子问题。如果您编写程序并不确定您的错误是否在于网络代码、字符串格式或缓冲区操作,那么您一次编写了太多代码并等待太长时间来测试它。
推荐阅读
- spring - 在基于证书的客户端的情况下调用 AuthenticationManager
- performance - drawImage() 创建模糊图像
- javascript - react native 从 API 端点获取完整数据后如何显示数据?
- r - R read_excel() 使文件锁定
- javascript - 做空标签可见或使用 CSS 或 Javascript 或 PHP 添加一些文本或样式?
- sql-server - 从 SQL 查询创建数据透视表
- html - 在电子邮件 html 正文内容中设置 CSS 动画关键帧属性
- ios - 只有一个 UITableView 响应触摸
- activeweb - ActiveWeb:测试 JSON 响应属性和值
- java - 我得到NaN,为什么?