Linux下使用SocketCan出现CAN总线Socket读取错误如何解决?
问题排查与修复方案
1. 关键错误点
1.1 接收缓冲区配置层级错误
设置SO_RCVBUF时使用了错误的套接字层级,SO_RCVBUF属于SOL_SOCKET层级,不是SOL_CAN_RAW层级,该错误会导致接收缓冲区配置失效,引发读取异常:
// 错误写法 setsockopt(sdr, SOL_CAN_RAW, SO_RCVBUF, (char *)&sock_buf_size, sizeof(sock_buf_size));
修改为:
setsockopt(sdr, SOL_SOCKET, SO_RCVBUF, (char *)&sock_buf_size, sizeof(sock_buf_size));
1.2 读取返回值变量类型错误
使用int8_t存储read()的返回值不符合规范,read()返回值类型为ssize_t,出错时返回-1,若后续使用CAN FD帧,帧长度超过127时会发生溢出截断,直接导致判断逻辑错误。
修改为:
ssize_t nbytesReceive;
1.3 读取逻辑错误
当前读取逻辑存在两处明显缺陷:
- 进入while循环前先执行了一次read,读到的有效帧直接被丢弃,无任何处理
- while循环条件
nbytesReceive != 0不符合CAN套接字行为:阻塞模式下CAN套接字无数据时会持续阻塞,不会返回0,只有出错时返回-1,此时必然不等于sizeof(struct can_frame),直接触发报错。
修正后的读取逻辑参考:
int can_receive(void) { struct can_frame frameRead; ssize_t nbytesReceive; LE_INFO("SDR is: %d ", sdr); while(1) // 可根据业务需求添加退出条件 { nbytesReceive = read(sdr, &frameRead, sizeof(frameRead)); if (nbytesReceive < 0) { // 信号触发的中断可重试,其他错误直接返回 if (errno == EINTR) continue; LE_INFO("Socket reading error, nbytesReceive = %zd, errno = %d", nbytesReceive, errno); return SOCK_READING_ERROR; } else if (nbytesReceive != sizeof(frameRead)) { LE_INFO("Incomplete CAN frame received, len = %zd", nbytesReceive); continue; } else { LE_INFO("Receive message %X %X %X %X %X %X %X %X %X", frameRead.can_id, frameRead.data[0], frameRead.data[1], frameRead.data[2], frameRead.data[3], frameRead.data[4], frameRead.data[5], frameRead.data[6], frameRead.data[7]); } } return 0; }
1.4 冗余套接字创建引发资源泄漏
初始化阶段重复调用了socket()创建套接字,第一次创建的套接字未关闭直接被覆盖,属于资源泄漏,删除第一行多余的创建语句即可:
sdr = socket(PF_CAN, SOCK_RAW, CAN_RAW); // 该行多余,直接删除 LE_INFO("sdr è: %d", sdr); if ((sdr = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0)
1.5 过滤器配置校验
当前配置的过滤器仅匹配无RTR标记的标准帧,若实际接收的是扩展帧、带RTR标记的帧,或者ID不在配置的7个ID范围内,都会被过滤器直接丢弃。可临时注释过滤器配置代码,验证无过滤场景下能否正常接收,排查是否为过滤规则匹配问题。
2. 调试建议
- 报错时打印
errno的值,可根据错误码快速定位问题类型 - 确认CAN接口MTU配置,若为CAN FD设备需要对应调整帧结构和读取长度
内容的提问来源于stack exchange,提问作者kevin94
相关产品推荐
相关产品推荐

