选择

时间:2016-03-24 07:36:05

标签: sockets go concurrency

假设我们为服务器提供了以下抽象(XMPP,但这里并不重要):

type Server struct {
    Addr   string
    Conn   net.Conn
    tlsCon *tls.Conn
    R      *bufio.Reader
    SSL    bool
    reader chan string
}

启动它的辅助函数:

func createServer(domain string) (s *Server, err error) {
    conn, err := net.Dial("tcp", domain+":5222")
    if err != nil {
        return nil, err
    }
    s = &Server{domain, conn, nil, bufio.NewReader(conn), false, make(chan string, 8)}
    go func(t *Server) {
        for {
            buf := bufsPool.Get().([]byte)
            n, err := s.R.Read(buf)
            if err == nil {
                s.reader <- string(buf[:n])
            } else {
                fmt.Println(err)
            }
        }
    }(s)

    return s, err
}

这个想法很简单:创建连接,如果一切顺利,请为它获取缓冲读取器(我不使用conn.read函数,因为,如果服务器需要,我启动TLS连接并重新分配R到基于它创建的阅读器,但现在情况并非如此)。

现在我们有两个函数,写和读:

func (s *Server) read() (t string) {
    t = ""
Inn:
    for {
        select {
        case u := <-s.reader:
            fmt.Println("debug", u)
            t += u
        default:
            break Inn
        }
    }
    return t
}

所以我希望read函数接收goroutine发送的数据,该数据从chan中读取socket(在createServer()中启动的那个)。想法是调用write而不是读取响应。我创建了所有这些因为服务器有时会将响应作为两个部分发送,例如我必须做传统的read()2次。 但这不起作用,我的阅读功能(见上文)根本不返回任何内容。最有可能的是,这是因为服务器无法回送数据并且我的功能退出,因为chan中没有任何内容。但有一个问题是虽然我多次调用write和read,但read总是不返回任何内容。

所以我想我有一些一般的设计错误,问题是社区是否可以帮助我找到它。感谢。

1 个答案:

答案 0 :(得分:1)

问题是你的select选择了默认分支,因为读取器通道中没有任何内容,所以它会立即中断。 (https://golang.org/ref/spec#Select_statements

您希望读取阻止,直到您收到足够的数据。例如。如果您知道您的回复需要以“\ n”结尾,请继续阅读并且在收到“\ n”之前不要中断,或者关闭频道。

如果传入的数据是换行符分隔的,并且使用chan string将整个字符串传递给另一个goroutine,或许更好的解决方案是在阅读器中使用goroutine中的bufio.Scanner,并使用is_global = remote_conn.recv(1024) 将整个字符串传递给另一个goroutine。

您也可以使用Scanner.Split设置不同的分割功能。

(另见关于tcp和分隔符的问题和答案:Golang: TCP client/server data delimiter

编辑:使用xml.Decoder.Token,您可以继续从流中读取令牌,并适当地处理它们。您可以将其与Decode(这将解码下一个令牌)或DecodeElement(这允许您解码刚刚读取的令牌)组合以解码xml。