本文共 2004 字,大约阅读时间需要 6 分钟。
TCP和UDP并非在IP层之上唯一的协议。通过查看网站www.iana.org/assignments/protocol-numbers,可以发现该列表上大约有140个协议,Unix系统中的/etc/protocols文件通常包含这些信息。TCP和UDP在该列表中分别占据编号6和17。
Go语言允许开发者创建原始套接字,从而支持其他协议的通信甚至自定义协议的实现。然而,它仅提供了基础功能:连接主机、读写数据包。接下来的章节将着重于设计和实现基于TCP的协议,同时也需处理IP层相关的问题。
为了简化问题,我们选择一个最简单的示例:发送一个ping消息给主机。Ping使用ICMP协议的echo命令。ICMP是一个面向字节的协议,客户端发送一个字节流到目标主机并等待回复。消息格式如下:
以下程序用于实现ping功能,可能需要root权限才能运行:
package mainimport ( "bytes" "fmt" "io" "net" "os")func main() { if len(os.Args) != 2 { fmt.Println("Usage: ", os.Args[0], "host") os.Exit(1) } addr, err := net.ResolveIPAddr("ip", os.Args[1]) if err != nil { fmt.Println("Resolution error", err.Error()) os.Exit(1) } conn, err := net.DialIP("ip4:icmp", addr, addr) checkError(err) var msg [512]byte msg[0] = 8 // echo类型标识符 msg[1] = 0 // 标识码 msg[2] = 0 // 校验和部分,待计算 msg[3] = 0 // 校验和的低字节 msg[4] = 0 // 标识符的低字节 msg[5] = 13 // 标识符的高字节 msg[6] = 0 // 序列号的低字节 msg[7] = 37 // 序列号的高字节 // 计算校验和 len := 8 check := checkSum(msg[0:len]) msg[2] = byte((check >> 8) & 0xff) msg[3] = byte(check & 0xff) // 发送消息 _, err = conn.Write(msg[0:len]) checkError(err) // 读取回复 _, err = conn.Read(msg[0:]) checkError(err) fmt.Println("Got response") if msg[5] == 13 { fmt.Println("identifier matches") } if msg[7] == 37 { fmt.Println("Sequence matches") } os.Exit(0)}func checkSum(msg []byte) uint16 { sum := 0 for n := 0; n < len(msg)-1; n += 2 { sum += int(msg[n]) * 256 + int(msg[n+1]) } sum = (sum >> 16) + (sum & 0xffff) sum += (sum >> 16) return uint16(sum)}func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) os.Exit(1) }} 该程序首先解析目标主机地址,建立一个IP地址连接,发送ping请求并处理回复。程序中checkSum函数用于计算消息的校验和,确保数据包的完整性。checkError函数用于处理错误信息,防止程序因错误而崩溃。
通过这种方式,我们可以成功实现基于ICMP协议的ping命令,验证目标主机的连通性。
转载地址:http://atcfk.baihongyu.com/