在Go语言中编写网络程序时,我们将看不到传统的编码形式。以前我们使用Socket编程时,会按照如下步骤展开:
(1)建立Socket:使用socket()函数。
(2)绑定Socket:使用bind()函数。
(3)监听:使用listen()函数。或者连接:使用connect()函数。
(4)接受连接:使用accept()函数。
(5)接收:使用receive()函数。或者发送:使用send()函数。
Go语言标准库对此过程进行了抽象和封装。无论我们期望使用什么协议建立什么形式的连接,都只需要调用net.Dial()即可。
Dial()函数
Dial()函数的原型如下:
其中net参数是网络协议的名字,addr参数是IP地址或域名,而端口号以“:”的形式跟随在地址或域名的后面,端口号可选。如果连接成功,返回连接对象,否则返回error。
TCP链接:
UDP链接:
ICMP链接(使用协议名称):
ICMP链接(使用协议编号):
这里我们可以通过以下链接查看协议编号的含义: http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml。
目前, Dial()函数支持如下几种网络协议: “tcp”、 “tcp4”(仅限IPv4)、 “tcp6”(仅限IPv6)、 “udp”、 “udp4”(仅限IPv4)、 “udp6”(仅限IPv6)、 “ip”、 “ip4”(仅限IPv4)和”ip6”(仅限IPv6)。
在成功建立连接后,我们就可以进行数据的发送和接收。发送数据时,使用conn的Write()成员方法,接收数据时使用Read()方法。
ICMP示例程序
package mainimport ("bytes""fmt""io""net""os")func main() {if len(os.Args) != 2 {fmt.Println("Usage: ", os.Args[0], "host")os.Exit(1)}service := os.Args[1]conn, err := net.Dial("ip4:icmp", service)checkError(err)var msg [512]bytemsg[0] = 8 // echomsg[1] = 0 // code 0msg[2] = 0 // checksummsg[3] = 0 // checksummsg[4] = 0 // identifier[0]msg[5] = 13 //identifier[1]msg[6] = 0 // sequence[0]msg[7] = 37 // sequence[1]len := 8check := 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// 先假设为偶数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]bytefor {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}
TCP示例程序
package mainimport ("bytes""fmt""io""net""os")func main() {if len(os.Args) != 2 {fmt.Fprintf(os.Stderr, "Usage: %s host:port", os.Args[0])os.Exit(1)}service := os.Args[1]conn, err := net.Dial("tcp", service)checkError(err)_, err = conn.Write([]byte("HEAD / HTTP/1.0\r\n\r\n"))checkError(err)result, err := readFully(conn)checkError(err)fmt.Println(string(result))os.Exit(0)}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]bytefor {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}
执行这段程序并查看执行结果:
更丰富的网络通信
实际上, Dial()函数是对DialTCP()、 DialUDP()、 DialIP()和DialUnix()的封装。我们也可以直接调用这些函数,它们的功能是一致的。这些函数的原型如下:

