TCP和UDP并不是建立在IP层之上唯一的协议。该网站:http://www.iana.org/assignments/protocol-numbers 列表上大约有140关于它们(该列表往往在Unix系统的/etc/protocols文件上。)。TCP和UDP在这个名单上分别为6和17。
Go允许你建立所谓的原始套接字,使您可以使用这些其它协议通信,或甚至建立你自己的。但它提供了最低限度的支持: 它会连接主机, 写入和读取和主机之间的数据包。在接下来的章节中,我们将着眼于设计和实现自己的基于TCP之上的协议; 这部分认为同样的问题存在于IP层。
为了简单起见,我们将使用几乎最简单的例子: 如何发送一个ping消息给主机。Ping使用"echo"命令的ICMP协议。这是一个面向字节协议, 客户端发送一个字节流到另一个主机, 并等待主机的答复。格式如下:
- 首字节是8, 表示echo消息
- 第二个字节是0
- 第三和第四字节是整个消息的校验和
- 第五和第六字节是一个任意标识
- 第七和第八字节是一个任意的序列号
- 该数据包的其余部分是用户数据
下面的程序将准备一个IP连接,发送一个ping请求到主机,并得到答复。您可能需要root权限才能运行成功。
/* Ping */ package main import ( "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 // code 0 msg[2] = 0 // checksum, fix later msg[3] = 0 // checksum, fix later msg[4] = 0 // identifier[0] msg[5] = 13 //identifier[1] msg[6] = 0 // sequence[0] msg[7] = 37 // sequence[1] len := 8 check := checkSum(msg[0:len]) msg[2] = byte(check >> 8) msg[3] = byte(check & 255) _, 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 // assume even for now for n := 1; n < len(msg)-1; n += 2 { sum += int(msg[n])*256 + int(msg[n+1]) } sum = (sum >> 16) + (sum & 0xffff) sum += (sum >> 16) var answer uint16 = uint16(^sum) return answer } func checkError(err error) { if err != nil { fmt.Fprintf(os.Stderr, "Fatal error: %s", err.Error()) os.Exit(1) } } func readFully(conn net.Conn) ([]byte, error) { defer conn.Close() result := bytes.NewBuffer(nil) var buf [512]byte for { n, err := conn.Read(buf[0:]) result.Write(buf[0:n]) if err != nil { if err == io.EOF { break } return nil, err } } return result.Bytes(), nil }