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

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

进入连接

这节有点涉及到对进入数据的准备环节处理。根据我们前面介绍的ad-hoc协议,命令名+换行符+数据+换行符。自然数据是和具体命令相关的。要处理这样的情况,我们创建了一个Endpoint对象,它具有下面的属性:

它允许注册一个或多个处理器函数,每个函数可以处理一个特殊的命令。 它根据命令名将具体命令调度到相关的处理器函数。

首先我们声明一个HandleFunc类型,该类型为接收一个bufio.ReadWriter指针值的函数类型, 也就是后面我们要为每种不同命令注册的处理器函数。它接收的参数是使用ReadWriter接口包装的net.Conn连接。


type HandleFunc func(*bufio.ReadWriter)

然后我们声明一个Endpoint结构体类型,它有三个属性:

listener: net.Listen()返回的Listener对象。 handler: 用于保存已注册的处理器函数的映射。 m: 一个互斥锁,用于解决map的多goroutine不安全的问题。

type Endpoint struct {
 listener net.Listener
 handler map[string]HandleFunc
 m sync.RWMutex  // Maps不是线程安全的,因此需要互斥锁来控制访问。
}

func NewEndpoint() *Endpoint {
 return &Endpoint{
  handler: map[string]HandleFunc{},
 }
}

func (e *Endpoint) AddHandleFunc(name string, f HandleFunc) {
 e.m.Lock()
 e.handler[name] = f
 e.m.Unlock()
}

func (e *Endpoint) Listen() error {
 var err error
 e.listener, err = net.Listen("tcp", Port)
 if err != nil {
  return errors.Wrap(err, "Unable to listen on "+e.listener.Addr().String()+"n")
 }
 log.Println("Listen on", e.listener.Addr().String())
 for {
  log.Println("Accept a connection request.")
  conn, err := e.listener.Accept()
  if err != nil {
   log.Println("Failed accepting a connection request:", err)
   continue
  }
  log.Println("Handle incoming messages.")
  go e.handleMessages(conn)
 }
}

// handleMessages读取连接到第一个换行符。 基于这个字符串,它会调用恰当的HandleFunc。

func (e *Endpoint) handleMessages(conn net.Conn) {
 // 将连接包装到缓冲reader以便于读取
 rw := bufio.NewReadWrite(bufio.NewReader(conn), bufio.NewWriter(conn))
 defer conn.Close()

 // 从连接读取直到遇到EOF. 期望下一次输入是命令名。调用注册的用于该命令的处理器。

 for {
  log.Print("Receive command '")
  cmd, err := rw.ReadString('n')
  switch {
  case err == io.EOF:
   log.Println("Reached EOF - close this connection.n ---")
   return
  case err != nil:
   log.Println("nError reading command. Got: '" + cmd + "'n", err)
  }

  // 修剪请求字符串中的多余回车和空格- ReadString不会去掉任何换行。

  cmd = strings.Trim(cmd, "n ")
  log.Println(cmd + "'")

  // 从handler映射中获取恰当的处理器函数, 并调用它。

  e.m.Lock()
  handleCommand, ok := e.handler[cmd]
  e.m.Unlock()

  if !ok {
   log.Println("Command '" + cmd + "' is not registered.")
   return
  }

  handleCommand(rw)
 }
}