TCP/IP - 以 C/C++ 格式发送十六进制数据包以控制中继板
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);
}
我应该如何发送十六进制命令? 当我编译此代码时,我收到以下警告:
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;
^~~~~~~~~~~~~~~~~~
显然该程序不起作用。
你能帮我吗?我是初学者,我不明白如何正确发送十六进制指令。
编辑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 = "580112000000016Cn";
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 = "580112000000016Cn";
不会按原样工作,因为这是一个 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};
确实应该工作:至少,八位字节值是正确的,并且您知道它们的顺序正确。
如果您非常喜欢编写第一个(字符串(形式,那么这是字符串应转换为的输出。
关于方法的快速评论:
你有一个有效的外壳管道。这意味着您可以使用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;
}
正如编译器所说,int
类型不够大,无法容纳数据。您应该使用unsigned long long
或#include<stdint.h>
并使用uint64_t
。但是,最简单的方法是直接定义数据const char command[] = {0x58, 0x01, 0x12, 0x00, 0x00, 0x00, 0x01, 0x6C};
。
此外,还应检查send()
的返回值以验证是否已发送所有数据。
更新 - 使用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;
}
- 如何在C++中将十六进制字符串转换为文本数据
- 如何在 c++ 中生成十六进制二进制数据的 sha256 哈希?
- C++将 wwn 字符串转换为识别为十六进制的数据类型
- TCP/IP - 以 C/C++ 格式发送十六进制数据包以控制中继板
- 将40个字节长的数据框从十六进制转换为二进制,然后转换为十进制
- 从十六进制数据中提取十六进制值
- C++ WSAsend 数据包的奇怪十六进制转储
- visual读取一个文件的十六进制数据,并将其存储到c++中的字符串2d数组中
- 以十六进制打印数据结构值
- 从文本文件到实际十六进制的文本十六进制数据字符串
- 如何从具有十六进制二进制数据的 char* 加载流字符串
- 将二进制十六进制数据转换为 ASCII 等效数据并存储在字符串中
- 增强数据的序列化十六进制十进制编码
- 将原始十六进制通信数据日志处理为可读值
- 在c++中快速将原始数据转换为十六进制字符串
- 将十六进制数据发送到串口
- 如何在c++中读取以空格分隔的十六进制字节数据
- 将存储为字符串的十六进制数据转换为文件
- 从c++到c#的二进制/十六进制数据的编组/输出
- 如何计算二进制SHA256哈希为给定的十六进制数据在c++