sockets - 使用 select 进行非阻塞读取

标签 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 重新分配给reader 是基于它创建的,但现在不是这样了)。

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

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
}

所以我希望读取函数接收 goroutine 发送的数据,该 goroutine 从 chan 的套接字(在 createServer() 中启动的套接字)读取。这个想法是调用写入而不是读取响应。我创建所有这些是因为服务器有时会分两部分发送响应,例如我必须做传统的 read() 2 次。 但这没有用,我的读取函数(见上文)什么也没有返回。最有可能的是,那是因为服务器没有设法发回数据,我的函数退出了,因为 chan 中没有任何内容。但一个问题是,尽管我多次调用 write 和 read,但 read 始终不返回任何内容。

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

最佳答案

问题是您的 select 选择了默认分支,因为读者 channel 中还没有任何内容,所以它立即中断了 for。 ( https://golang.org/ref/spec#Select_statements )

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

也许更好的解决方案是使用 bufio.Scanner在与阅读器的 goroutine 中,如果传入数据以换行符分隔,则使用 chan string 将整个字符串传递给另一个 goroutine。

您还可以使用 Scanner.Split设置不同的拆分器功能。

(另请参阅有关 tcp 和定界符的问答:Golang: TCP client/server data delimiter)

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

关于sockets - 使用 select 进行非阻塞读取,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36195197/

相关文章:

java - 我想在关闭后重用套接字(java)

c++ - 附加套接字字符数组

go - 为什么我不能通过 *interface{} 参数传递结构指针?

windows - 一个进程的 read() 可以看到另一个进程的部分 write() 吗?

c - 如何在 C 中编写基于 epoll 的套接字客户端

c# - mono 如何使用 BSD 套接字?

go - Golang 中的 Rune 和 UTF-8

go - 在端点上运行测试之前,无法在BeforeSuit中启动应用程序服务器

c# - 并发访问通用列表属性

java - 并发访问内部创建列表的静态方法