纯C++写的、Linux下 的CAN报文发送、接收类

22 篇文章 2 订阅

commonToolkits_global.h 

#ifndef COMMONTOOLKITS_GLOBAL_H
#define COMMONTOOLKITS_GLOBAL_H

#include <QtCore/qglobal.h>

#if defined(COMMONTOOLKITS_LIBRARY)
#  define COMMONTOOLKITS_EXPORT Q_DECL_EXPORT
#else
#  define COMMONTOOLKITS_EXPORT Q_DECL_IMPORT
#endif

#endif // COMMONTOOLKITS_GLOBAL_H

cansocketlinux.h

#ifndef _CANSOCKET_H
#define _CANSOCKET_H

// The order of the following includes is mandatory, because some
// distributions use sa_family_t in can.h without including socket.h
#include <sys/socket.h>
#include <sys/uio.h>
#include <linux/can.h>
#include <sys/time.h>
#include "commonToolkits_global.h"
#include <string>
#include <vector>


struct CanFrame
{
    uint32_t frameId;      // 帧id
    uint8_t dataLen{0};    // 帧数据包长度
    uint8_t data[8]{0};    // 存放can帧的数据包缓冲区
};


class COMMONTOOLKITS_EXPORT CanSocket
{
public:
    CanSocket(const std::string &name, bool bInit = false);

    ~CanSocket();

   void initCan();  // 初始化can,主要是设置波特率
 
    bool open();    // 打开can
    void close();   // 关闭can

    bool writeFrame(const CanFrame& frame); // 发送can数据到can总线

    std::vector<CanFrame> readSocket();     // 获取CAN总线的数据包
public:
    bool getState() {
        if(canSocket == -1) {
            return false;
        }
        return true;
    }

private:
    bool connectSocket();

    sockaddr_can m_address;
    char m_ctrlmsg[CMSG_SPACE(sizeof(timeval)) + CMSG_SPACE(sizeof(__u32))];

    int canSocket = -1;
    std::string canSocketName;
    bool canFdOptionEnabled = false;
    std::string errMsg;
};

cansocketlinux.cpp 


#include "cansocketlinux.h"
#include "memory.h"
#include "stddef.h"
#include <string>
#include "qglobal.h"

#include <linux/can/raw.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include<linux/sockios.h>

const std::string CAN0_OPEN = "ifconfig can0 up";
const std::string CAN0_CLOSE = "ifconfig can0 down";
const std::string CAN0_CONFIG = "ip link set can0 type can bitrate 250000 triple-sampling on";

const std::string CAN1_OPEN = "ifconfig can1 up";
const std::string CAN1_CLOSE = "ifconfig can1 down";
const std::string CAN1_CONFIG = "ip link set can1 type can bitrate 250000 triple-sampling on";

void CanSocket::initCan()
{
      system(CAN0_CONFIG.data());
      system(CAN0_OPEN.data());
   
      system(CAN1_CONFIG.data());
      system(CAN1_OPEN.data());
   
}

CanSocket::CanSocket(const std::string &name, bool bInit = false):
    canSocketName(name)
{
    if(bInit)
    {
        initCan();
    }
}

CanSocket::~CanSocket()
{
    close();
}

bool CanSocket::open()
{
    if (canSocket == -1) {
        if (!connectSocket()) {
            close(); // sets UnconnectedState
            return false;
        }
    }
    return true;
}

void CanSocket::close()
{
    ::close(canSocket);
    canSocket = -1;
}

bool CanSocket::writeFrame(const CanFrame &frame)
{
    if (canSocket== -1)
        return false;

    if(frame.dataLen > 8) {
        return false;
    }
    canid_t canId = frame.frameId;
    canId |= CAN_EFF_FLAG;

    int64_t bytesWritten = 0;

    can_frame sendframe;
    ::memset(&sendframe, 0, sizeof(sendframe));
    sendframe.can_dlc = frame.dataLen;
    sendframe.can_id = canId;
    ::memcpy(sendframe.data, frame.data, sendframe.can_dlc);

    bytesWritten = ::write(canSocket, &sendframe, sizeof(sendframe));


    if (bytesWritten < 0) {
        return false;
    }

    return true;
}

std::vector<CanFrame> CanSocket::readSocket()
{
    std::vector<CanFrame> newFrames;

    for (;;) {
        struct can_frame frame;

        const int bytesReceived = ::read(canSocket, &frame, sizeof(frame));

        if (bytesReceived != sizeof(frame)) {
            break;
        }

        struct timeval timeStamp;
        if (ioctl(canSocket, SIOCGSTAMP, &timeStamp) < 0) {
            ::memset(&timeStamp, 0, sizeof(timeStamp));
        }

        CanFrame bufferedFrame;
        bufferedFrame.frameId = (frame.can_id & CAN_EFF_MASK);
        bufferedFrame.dataLen = frame.can_dlc;
        if(frame.can_dlc > 8) {
            continue;
        }
        memcpy(bufferedFrame.data, frame.data, frame.can_dlc);

        newFrames.push_back(std::move(bufferedFrame));
    }
    return newFrames;
}

bool CanSocket::connectSocket()
{
    struct ifreq interface;

    if ((canSocket = socket(PF_CAN, SOCK_RAW | SOCK_NONBLOCK, CAN_RAW)) < 0) {
        //errMsg = errno;
        return false;
    }

    int loopback = 0;
    setsockopt(canSocket, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));
    strcpy(interface.ifr_name, canSocketName.data());
    if (ioctl(canSocket, SIOCGIFINDEX, &interface) < 0) {
        //errMsg = errno;
        return false;
    }

    m_address.can_addr.tp.rx_id = 0;
    m_address.can_addr.tp.tx_id = 0;
    m_address.can_family  = AF_CAN;
    m_address.can_ifindex = interface.ifr_ifindex;

    if (bind(canSocket, (struct sockaddr *)(&m_address), sizeof(m_address))  < 0) {
        //qDebug("%s", qt_error_string(errno).toLatin1().data());
        //errMsg = std::string(qt_error_string(errno).toLocal8Bit().constData());
        return false;
    }

    return true;
}

说明:

  • 仅仅用到了Qtcore,如果你不想依赖Qt,可以把代码中qt的全部去掉,其实qt的东西也不多。
  • initCan()用于初始化CAN的波特率。一般波特率设置在开机自启动的脚本中;也可以通过构造函数的第二个设置为true,让程序来设置CAN波特率。
  • initCan只初始化了can0、can1两路,且波特率设置为250k,可以跟你自身需求,更改这些属性。

注意:有些can设置波特率时不支持triple-sampling on,如果设置波特率报错,请将

ip link set can0 type can bitrate 250000  triple-sampling on

改为:

ip link set can0 type can bitrate 250000 
  • 主要接口功能见头文件注释。
  • 本类只能在Linux运行,在Windows下编译通不过。win11建议用Qt自带的QCanBusDevice类,但QCanBusDevice必须要求Qt5.8版本及以上版本才行,我当时是因为Linux下不能用5.8及以上版本的Qt,就用蒓C++写了这个。
  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
发送DHCP报文获取DHCP信息,可以使用Linux下的socket编程来实现。具体步骤如下: 1. 创建socket对象,使用raw socket型。 2. 设置socket属性,包括IP地址、端口号等。 3. 创建DHCP报文,并填充报文字段。 4. 发送DHCP报文。 5. 接收DHCP服务器的响应报文。 6. 解析响应报文,获取DHCP信息。 以下是一个简单的示例代码: ```c++ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define BUF_SIZE 1024 #define DHCP_PORT 67 #define DHCP_SERVER_PORT 68 typedef struct { uint8_t op; // Message op code / message type. uint8_t htype; // Hardware address type (e.g., '1' = 10mb ethernet). uint8_t hlen; // Hardware address length (e.g., '6' for 10mb ethernet). uint8_t hops; // Client sets to zero, optionally used by relay agents when booting via a relay agent. uint32_t xid; // Transaction ID, a random number chosen by the client, used by the client and server to associate messages and responses between a client and a server. uint16_t secs; // Filled in by client, seconds elapsed since client began address acquisition or renewal process. uint16_t flags; // Broadcast flag. struct in_addr ciaddr; // Client IP address; only filled in if client is in BOUND, RENEW or REBINDING state and can respond to ARP requests. struct in_addr yiaddr; // 'your' (client) IP address. struct in_addr siaddr; // IP address of next server to use in bootstrap; returned in DHCPOFFER, DHCPACK by server. struct in_addr giaddr; // Relay agent IP address, used in booting via a relay agent. uint8_t chaddr[16]; // Client hardware address. char sname[64]; // Optional server host name, null terminated string. char file[128]; // Boot file name, null terminated string; "generic" name or null in DHCPDISCOVER, fully qualified directory-path name in DHCPOFFER. uint32_t magic_cookie; // Fixed value: 0x63825363. uint8_t options[308]; // Optional parameters field. } dhcp_packet; int main(int argc, char* argv[]) { int sockfd; struct sockaddr_in servaddr, cliaddr; char buf[BUF_SIZE]; socklen_t len = sizeof(cliaddr); dhcp_packet packet; // 创建socket对象 sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_UDP); if (sockfd < 0) { perror("socket"); exit(1); } // 设置socket属性 memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(DHCP_SERVER_PORT); servaddr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("bind"); exit(1); } // 创建DHCP报文 memset(&packet, 0, sizeof(packet)); packet.op = 0x01; // Message type: Boot Request packet.htype = 0x01; // Hardware type: Ethernet packet.hlen = 0x06; // Hardware address length: 6 packet.xid = random(); // Transaction ID packet.magic_cookie = htonl(0x63825363); // DHCP Magic Cookie // DHCP Option 53: DHCP Discover packet.options[0] = 0x35; // Option 53 packet.options[1] = 0x01; // Length packet.options[2] = 0x01; // DHCP Discover // DHCP Option 55: Parameter Request List packet.options[3] = 0x37; // Option 55 packet.options[4] = 0x04; // Length packet.options[5] = 0x01; // Subnet Mask packet.options[6] = 0x03; // Router packet.options[7] = 0x06; // Domain Name Server packet.options[8] = 0x0f; // Domain Name packet.options[9] = 0x2c; // Static Route packet.options[10] = 0xff; // End // 发送DHCP报文 memset(&cliaddr, 0, sizeof(cliaddr)); cliaddr.sin_family = AF_INET; cliaddr.sin_port = htons(DHCP_PORT); cliaddr.sin_addr.s_addr = inet_addr("255.255.255.255"); if (sendto(sockfd, &packet, sizeof(packet), 0, (struct sockaddr*)&cliaddr, sizeof(cliaddr)) < 0) { perror("sendto"); exit(1); } // 接收DHCP服务器的响应报文 memset(buf, 0, sizeof(buf)); if (recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr*)&cliaddr, &len) < 0) { perror("recvfrom"); exit(1); } // 解析响应报文,获取DHCP信息 // ... close(sockfd); return 0; } ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值