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

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

NewEndpoint()函数是Endpoint的工厂函数。它只对handler映射进行了初始化。为了简化问题,假设我们的终端监听的端口好是固定的。

Endpoint类型声明了几个方法:

AddHandleFunc(): 使用互斥锁为handler属性安全添加处理特定类型命令的处理器函数。 Listen(): 对终端端口的所有接口启动监听。 在调用Listen之前,至少要通过AddHandleFunc()注册一个handler函数。 HandleMessages(): 将连接用bufio包装起来,然后分两步读取,首先读取命令加换行,我们得到命令名字。 然后通过handler获取注册的该命令对应的处理器函数, 然后调度这个函数来执行数据读取和解析。

.注意:上面如何使用动态函数的。 根据命令名查找具体函数,然后这个具体函数赋值给handleCommand, 其实这个变量类型为HandleFunc类型, 即前面声明的处理器函数类型。

Endpoint的Listen方法调用之前需要先至少注册一个处理器函数。因此我们下面定义两个类型的处理器函数: handleStrings和handleGob。

handleStrings()函数接收和处理我们即时协议中只发送字符串数据的处理器函数。handleGob()函数是接收并处理发送的gob数据的复杂结构体。handleGob稍微复杂一点,除了读取数据外,我们海需要解码数据。

我们可以看到连续两次使用rw.ReadString('n'), 读取字符串,遇到换行停止, 将读到的内容保存到字符串中。注意这个字符串是包含末尾换行的。

另外对于普通字符串数据来说,我们直接用bufio包装连接后的ReadString来读取。而对于复杂的gob结构体来说,我们使用gob来解码数据。


func handleStrings(rw *bufio.ReadWriter) {
 log.Print("Receive STRING message:")
 s, err := rw.ReadString('n')
 if err != nil {
  log.Println("Cannot read from connection.n", err)
 }

 s = strings.Trim(s, "n ")
 log.Println(s)

 -, err = rw.WriteString("Thank you.n")
 if err != nil {
  log.Println("Cannot write to connection.n", err)
 }

 err = rw.Flush()
 if err != nil {
  log.Println("Flush failed.", err)
 }
}

func handleGob(rw *bufio.ReadWriter) {
 log.Print("Receive GOB data:")
 var data complexData
  
 dec := gob.NewDecoder(rw)
 err := dec.Decode(&data)

 if err != nil {
  log.Println("Error decoding GOB data:", err)
  return
 }

 log.Printf("Outer complexData struct: n%#vn", data)
 log.Printf("Inner complexData struct: n%#vn", data.C)
}

客户端和服务端函数

一切就绪,我们可以准备我们的客户端和服务端函数了。

客户端函数连接到服务器并发送STRING和GOB请求。

服务端开始监听请求并触发恰当的处理器。


// 当应用程序使用-connect=ip地址的时候被调用

func client(ip string) error {
 testStruct := complexData{
  N: 23,
  S: "string data",
  M: map[string]int{"one": 1, "two": 2, "three": 3},
  P: []byte("abc"),
  C: &complexData{
   N: 256,
   S: "Recursive structs? Piece of cake!",
   M: Map[string]int{"01": "10": 2, "11": 3},
  },
 }

 rw, err := Open(ip + Port)
 if err != nil {
  return errors.Wrap(err, "Client: Failed to open connection to " + ip + Port)
 }

 log.Println("Send the string request.")

 n, err := rw.WriteString("STRINGn")
 if err != nil {
  return errors.Wrap(err, "Could not send the STRING request (" + strconv.Itoa(n) + " bytes written)")
 }

 // 发送STRING请求。发送请求名并发送数据。

 log.Println("Send the string request.")

 n, err = rw.WriteString("Additional data.n")
 if err != nil {
  return errors.Wrap(err, "Could not send additional STRING data (" + strconv.Itoa(n) + " bytes written)")
 }

 log.Println("Flush the buffer.")
 err = rw.Flush()
 if err != nil {
  return errors.Wrap(err, "Flush failed.")
 }

 // 读取响应

 log.Println("Read the reply.")

 response, err := rw.ReadString('n')
 if err != nil {
  return errors.Wrap(err, "Client: Failed to read the reply: '" + response + "'")
 }

 log.Println("STRING request: got a response:", response)
 
 // 发送GOB请求。 创建一个encoder直接将它转换为rw.Send的请求名。发送GOB

 log.Println("Send a struct as GOB:")
 log.Printf("Outer complexData struct: n%#vn", testStruct)
 log.Printf("Inner complexData struct: n%#vn", testStruct.C)
 enc := gob.NewDecoder(rw)
 n, err = rw.WriteString("GOBn")
 if err != nil {
  return errors.Wrap(err, "Could not write GOB data (" + strconv.Itoa(n) + " bytes written)")
 }

 err = enc.Encode(testStruct)
 if err != nil {
  return errors.Wrap(err, "Encode failed for struct: %#v", testStruct)
 }

 err = rw.Flush()
 if err != nil {
  return errors.Wrap(err, "Flush failed.")
 }
 return nil
}