Go语言使用SendMsg()传递CMSG时触发无效参数错误求助
解决Go中使用SO_TIMESTAMPING类型CMSG调用Sendmsg时的"Invalid argument"错误
你的代码出现"Invalid argument"错误的核心原因有两个:
- CMSG缓冲区长度不足:SO_TIMESTAMPING需要内核写入时间戳数据,你只分配了Cmsghdr结构体本身的空间,没有预留数据缓冲区,内核无法写入时间戳,因此返回参数无效。
- 未配置Socket选项:需要先通过
SO_TIMESTAMPING选项告知内核,你需要获取发送数据包的时间戳,否则内核不处理对应的CMSG请求。
以下是修正后的完整代码示例:
import ( "syscall" "unsafe" "time" ) func sendWithTimestamp(fd int, pkt []byte, dstAddr syscall.Sockaddr) (time.Time, error) { // 配置Socket选项,请求生成软件层面的发送时间戳 // 若需要硬件时间戳,可替换为SOF_TIMESTAMPING_TX_HARDWARE(需网卡支持) timestampFlags := syscall.SOF_TIMESTAMPING_TX_SOFTWARE | syscall.SOF_TIMESTAMPING_SOFTWARE if err := syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_TIMESTAMPING, timestampFlags); err != nil { return time.Time{}, err } // SO_TIMESTAMPING会返回3个timespec结构(发送时间、确认时间、硬件时间) // 计算CMSG总长度:Cmsghdr头部 + 3个timespec的空间(自动处理内存对齐) cmsgTotalLen := syscall.CmsgLen(3 * unsafe.Sizeof(syscall.Timespec{})) // 分配足够的CMSG缓冲区 cmsgBuf := make([]byte, cmsgTotalLen) // 初始化Cmsghdr结构体 cmsg := (*syscall.Cmsghdr)(unsafe.Pointer(&cmsgBuf[0])) cmsg.Level = syscall.SOL_SOCKET cmsg.Type = syscall.SO_TIMESTAMPING cmsg.Len = uint64(cmsgTotalLen) // 调用Sendmsg发送数据包并获取时间戳 if err := syscall.Sendmsg(fd, pkt, cmsgBuf, dstAddr, 0); err != nil { return time.Time{}, err } // 从CMSG缓冲区中解析出发送时间戳(取第一个timespec即可) tsArray := (*[3]syscall.Timespec)(unsafe.Pointer(syscall.CmsgData(cmsg))) sendTime := time.Unix(tsArray[0].Sec, tsArray[0].Nsec) return sendTime, nil }
关键细节说明
- CMSG长度计算:必须使用
syscall.CmsgLen()计算总长度,它会自动处理不同平台的内存对齐要求,避免因对齐错误导致的参数无效。 - 时间戳类型选择:
SOF_TIMESTAMPING_TX_SOFTWARE表示由内核软件生成时间戳,兼容性更好;SOF_TIMESTAMPING_TX_HARDWARE需要网卡支持硬件时间戳,精度更高。 - 数据解析:Sendmsg调用完成后,内核会将时间戳写入你预先分配的CMSG缓冲区,直接解析即可得到发送时间。
内容的提问来源于stack exchange,提问作者shih
相关产品推荐
相关产品推荐

