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

时间:2019-09-08 15:04:36

标签: c++ c ubuntu tcp tcpclient

我有一个带有16个继电器的电子板,它可以通过TCP / IP工作。 单板的IP地址为192.168.1.4,端口为3000。 我想在Ubuntu下用C / C ++控制它。

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

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: kuehl.leon.tictactoe, PID: 2719
    java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{kuehl.leon.tictactoe/kuehl.leon.tictactoe.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2718)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593)
        at android.os.Handler.dispatchMessage(Handler.java:105)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:6541)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference
        at android.support.v7.app.AppCompatDelegateImplBase.<init>(AppCompatDelegateImplBase.java:117)
        at android.support.v7.app.AppCompatDelegateImplV9.<init>(AppCompatDelegateImplV9.java:149)
        at android.support.v7.app.AppCompatDelegateImplV11.<init>(AppCompatDelegateImplV11.java:29)
        at android.support.v7.app.AppCompatDelegateImplV14.<init>(AppCompatDelegateImplV14.java:54)
        at android.support.v7.app.AppCompatDelegateImplV23.<init>(AppCompatDelegateImplV23.java:31)
        at android.support.v7.app.AppCompatDelegateImplN.<init>(AppCompatDelegateImplN.java:31)
        at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:198)
        at android.support.v7.app.AppCompatDelegate.create(AppCompatDelegate.java:183)
        at android.support.v7.app.AppCompatActivity.getDelegate(AppCompatActivity.java:519)
        at android.support.v7.app.AppCompatActivity.findViewById(AppCompatActivity.java:190)
        at kuehl.leon.tictactoe.MainActivity.<init>(MainActivity.java:13)
        at java.lang.Class.newInstance(Native Method)
        at android.app.Instrumentation.newActivity(Instrumentation.java:1173)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2708)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2892) 
        at android.app.ActivityThread.-wrap11(Unknown Source:0) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1593) 
        at android.os.Handler.dispatchMessage(Handler.java:105) 
        at android.os.Looper.loop(Looper.java:164) 
        at android.app.ActivityThread.main(ActivityThread.java:6541) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767) 

我可以通过在Ubuntu下发送命令行命令来正确地打开和关闭每个继电器:

    "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..

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

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

这是我的代码:

echo '580112000000016C' | xxd -r -p | nc 192.168.1.4 3000

我应该如何发送十六进制命令? 当我编译此代码时,会收到以下警告:

#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);

}

显然该程序无法正常工作。

能帮我吗?我是初学者,不了解如何正确发送十六进制指令。

EDIT1 这是尼尔森建议后的代码。

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;
                  ^~~~~~~~~~~~~~~~~~

但是,不幸的是,它仍然无法正常工作。

3 个答案:

答案 0 :(得分:5)

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

无法按原样工作,因为这是一个ASCII字符串(或本地实现使用的任何字符集),并且您需要对其进行解析以获取数字形式:您缺少了{{1} }您原始管道的步骤。 (除非您确实想通过网络发送尾随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};

真正应该起作用:至少八位字节值正确,并且您知道它们的顺序正确。

如果您强烈希望 writing 是第一种(字符串)形式,则这是字符串应转换为的输出。


关于方法论的简短评论:

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

这还意味着您可以通过将程序编写为仅 来替换管道的nc阶段来开始:这使您可以确认网络代码在隔离状态下是否正常工作。

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

如果您注意到该方法学中的一种模式,则希望始终将问题分解为较小的子问题。如果您编写程序后发现不确定自己的错误是否出在网络代码中,字符串格式或缓冲区操作中,那么您一次编写了太多代码,而等待的时间太长,无法对其进行测试。

答案 1 :(得分:2)

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;
}

答案 2 :(得分:1)

正如编译器所说,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;
}