【问题标题】:Golang concurrently reading from a tcp connectoinGolang 同时从 tcp 连接读取
【发布时间】:2018-10-15 17:30:05
【问题描述】:

我在 Go 项目中遇到了一些问题。代码太大,无法复制和粘贴,所以我会尽力解释。

我的程序首先连接到一个 TCP 服务器,然后它启动一个 goroutine 作为参数传递连接对象。

我想要实现的是让客户端从 tcp 连接中无限读取,同时通过发送检索数据来获取用户输入并与服务器通信。我尝试过使用另一个 goroutine,但是每当尝试从服务器检索数据时程序都会阻塞。

这是在 go playground 上的错误重现。

https://play.golang.org/p/OD5ozCRmy_4服务器

https://play.golang.org/p/t1r_BAQM-jn客户端

基本上,每当客户端尝试从连接中读取时,它就会卡住。 感谢您的帮助。

【问题讨论】:

  • 您正在读取两个 goroutine 中的连接,但只写入其中一个,当然这会阻塞.... 除非我误解了服务器应该向您发送的内容?
  • 您正在循环的每次迭代中创建和丢弃 bufio.Reader。这可以丢弃从网络读取但尚未从应用程序读取的数据。在循环之外创建 bufio.Reader。
  • 当涉及的代码太大而无法复制粘贴时,是时候创建一个Minimal, Complete, Verifiable Example。将代码精简到重现问题所需的最低限度本身通常是查找原因的有益练习。
  • 对于初学者来说,虽然在 2 个地方同时读取 net.Conn 是安全的,但这样做没有任何意义。您无法确定流的哪些部分转到哪个 Read 调用(更不用说在 bufio.Readers 中缓冲了隐藏数据)。我不确定这应该做什么,但没有办法让它工作
  • 从一个 goroutine 读取消息,使用消息数据和/或本地状态确定消息的目标并分发到该目标。

标签: go networking concurrency


【解决方案1】:

你应该使用频道 这是一个可以接收一些连接的示例,每个连接都可以根据需要发送数据

包tcp

import (
    "bufio"
    "fmt"
    "net"
    "strconv"

    "../log"

    "../config"
    "../controllers"
    h "../helpers"
    )

    type msgFormat struct {
      text []byte
      net.Conn
    }

var accounts = make(map[net.Conn]int)
var conns = make(chan net.Conn)
var dconns = make(chan net.Conn)
var msgs = make(chan msgFormat)
var i int

//Init is first point
func Init() {
    startserver()
    for {
        select {
        case conn := <-conns:
            handleconnect(conn)
        case msg := <-msgs:
            go handlemsg(msg)
        case dconn := <-dconns:
            handlediscounect(dconn)
        }
    }
}
func handlemsg(incomemsg msgFormat) {
    logger.Log.Println(string(incomemsg.text))
    resp, err := controllers.Do(incomemsg.text)
    if err != nil {
        logger.Log.Println(err.Error())
    }
    strLen := []byte(h.Lpad(string(fmt.Sprintf("%v", len(resp))), "0", 4))
    //
    fresponse := append(strLen, resp...)

    incomemsg.Write(fresponse)
    logger.Log.Println("response is %v" , string(fresponse))
}

func startserver() {
    conf := config.GetConfigInstance()

    ln, err := net.Listen(conf.SERVER.Nettype, conf.SERVER.Address)
    if err != nil {
        logger.Log.Println(err.Error())
    }
    logger.Log.Printf("server is serving at %v", conf.SERVER.Address)
    go func() {
        for {
            conn, err := ln.Accept()
            if err != nil {
                logger.Log.Println(err.Error())
            }
            conns <- conn
        }
    }()

}

func readdate(conn net.Conn, i int) {
    for {
        rd := bufio.NewReader(conn)

        dataLen := make([]byte, 4)
        _, err := rd.Read(dataLen)
        if err != nil {
            break
        }
        intLen, _ := strconv.Atoi(string(dataLen))
        data := make([]byte, intLen)
        _, err = rd.Read(data)
        if err != nil {
            break
        }
        msgs <- msgFormat{data, conn}
    }
    dconns <- conn
}

func handleconnect(newconnection net.Conn) {
    accounts[newconnection] = i
    i++
    // if addr , ok := newconnection.RemoteAddr().str
    logger.Log.Printf("Action: Client_Connected %v is connected via %v \n", i, newconnection.RemoteAddr().(*net.TCPAddr).IP)
    go readdate(newconnection, i)
}

func handlediscounect(disconnection net.Conn) {
    logger.Log.Printf("Action: Client_Disconnected %v / %v is gone\n", accounts[disconnection] + 1, disconnection.RemoteAddr().(*net.TCPAddr).IP)
    delete(accounts, disconnection)
}

【讨论】:

  • 您好,感谢您的回答。我已经设法让它与频道完美配合。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-02
  • 1970-01-01
  • 2018-06-28
  • 1970-01-01
相关资源
最近更新 更多