Go语言中TCP/IP网络编程的深入讲解

2020-01-28 13:01:56于丽

net包实现服务端的核心部分是:

net.Listen()在给定的本地网络地址上来创建新的监听器。如果只传端口号给它,例如":61000", 那么监听器会监听所有可用的网络接口。 这相当方便,因为计算机通常至少提供两个活动接口,回环接口和最少一个真实网卡。 这个函数成功的话返回Listener。

Listener接口有一个Accept()方法用来等待请求进来。然后它接受请求,并给调用者返回新的连接。Accept()一般来说都是在循环中调用,能够同时服务多个连接。每个连接可以由一个单独的goroutine处理,正如下面代码所示的。

代码部分

与其让代码来回推送一些字节,我更想要它演示一些更有用的东西。 我想让它能给服务器发送带有不同数据载体的不同命令。服务器应该能标识每个命令和解码命令数据。

我们代码中客户端会发送两种类型的命令: "STRING"和"GOB"。它们都以换行符终止。

"STRING"命令包含一行字符串数据,可以通过bufio中的简单读写操作来处理。

"GOB"命令由结构体组成,这个结构体包含一些字段,包含一个分片和映射,甚至指向自己的指针。 正如你所见,当运行这个代码时,gob包能通过我们的网络连接移动这些数据没有什么稀奇(fuss).

我们这里基本上都是一些即席协议(ad-hoc protocol: 特设的、特定目的的、即席的、专案的), 客户端和服务端都遵循它,命令行后面是换行,然后是数据。对于每个命令来说,服务端必须知道数据的确切格式,知道如何处理它。

要达到这个目的,服务端代码采取两步方式实现。

第一步: 当Listen()函数接收到新连接,它会产生一个新的goroutine来调用handleMessage()。 这个函数从连接中读取命令名, 从映射中查询合适的处理器函数,然后调用它。 第二步: 选择的处理器函数读取并处理命令行的数据。

package main

import (
 "bufio"
 "encoding/gob"
 "flag"
 "github.com/pkg/errors"
 "io"
 "log"
 "net"
 "strconv"
 "strings"
 "sync"
)

type complexData struct {
 N int
 S string
 M map[string]int
 P []byte
 C *complexData
}

const (
 Port = ":61000"
)

Outcoing connections(发射连接)

使用发射连接是一种快照。net.Conn满足io.Reader和io.Writer接口,因此我们可以将TCP连接和其他任何的Reader和Writer一样看待。


func Open(addr string) (*bufio.ReadWriter, error) {
 log.Println("Dial " + addr)
 conn, err := net.Dial("tcp", addr)

 if err != nil {
  return nil, errors.Wrap(err, "Dialing " + addr + " failed")
 }

 return bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)), nil
}

打开TCP地址的连接。它返回一个带有超时的TCP连接,并将其包装进缓冲的ReadWriter。拨号远程进程。注意本地端口是实时(on the fly)分配的。如果必须指定本地端口号,请使用DialTCP()方法。