进入连接
这节有点涉及到对进入数据的准备环节处理。根据我们前面介绍的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)
}
}









