亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

如何訪問所有連接的 tcp 客戶端的變量?

如何訪問所有連接的 tcp 客戶端的變量?

Go
當年話下 2023-06-19 17:12:00
我正在用 go 編寫的寵物項目中設置一個 tcp 服務器。我希望能夠維護所有連接的客戶端的一部分,然后在新客戶端連接到我的服務器或從我的服務器斷開連接時修改它。我現在的主要心理障礙是我是否應該聲明一個包級切片,或者只是將一個切片傳遞給我的處理程序。我的第一個想法是聲明我的ClientList切片(我知道切片可能不是我最好的選擇,但我決定暫時保留它)作為包級變量。雖然我認為這行得通,但我看到許多帖子不鼓勵使用它們。我的另一個想法是在我的主函數中聲明ClientList為一個切片,然后我傳遞ClientList給我的HandleClient函數,所以每當客戶端連接/斷開連接時,我都可以調用AddClient或RemoveClient傳遞這個切片并添加/刪除適當的客戶端。此實現如下所示。代碼肯定還有其他問題,但我一直在努力思考一些看起來應該非常簡單的事情。type Client struct {    Name string      Conn net.Conn}type ClientList []*Client// Identify is used to set the name of the clientfunc (cl *Client) Identify() error {// code here to set the client's name in the based on input from client}// This is not a threadsafe way to do this - need to use mutex/channelsfunc (cList *ClientList) AddClient(cl *Client) {    *cList = append(*cList, cl)}func (cl *Client) HandleClient(cList *ClientList) {    defer cl.Conn.Close()    cList.AddClient(cl)    err := cl.Identify()    if err != nil {        log.Println(err)        return    }    for {        err := cl.Conn.SetDeadline(time.Now().Add(20 * time.Second))        if err != nil {            log.Println(err)            return        }        cl.Conn.Write([]byte("What command would you like to perform?\n"))        netData, err := bufio.NewReader(cl.Conn).ReadString('\n')        if err != nil {            log.Println(err)            return        }        cmd := strings.TrimSpace(string(netData))        if cmd == "Ping" {            cl.Ping() //sends a pong msg back to client        } else {            cl.Conn.Write([]byte("Unsupported command at this time\n"))        }    }}func main() {    arguments := os.Args    PORT := ":" + arguments[1]    l, err := net.Listen("tcp4", PORT)    if err != nil {        fmt.Println(err)        return    }從我最初的測試來看,這似乎有效。我能夠打印出我的客戶列表,我可以看到正在添加新客戶,并且在Identify()調用之后也添加了他們的名字。當我使用 -race 標志運行它時,我確實收到了數據競爭警告,所以我知道我需要一種線程安全的方式來處理添加的客戶端。當我添加它時刪除客戶端也是如此。將我的 ClientList 傳遞到 中是否有任何其他問題我可能會遺漏HandleClient,或者我將 ClientList 聲明為包級變量可以獲得任何好處?
查看完整描述

1 回答

?
拉莫斯之舞

TA貢獻1820條經驗 獲得超10個贊

這種方法存在幾個問題。


首先,您的代碼包含數據競爭:每個 TCP 連接都由一個單獨的 goroutine 提供服務,并且它們都試圖同時修改切片。


go build -race您可能會嘗試使用(或go install -race— 無論您使用什么)構建代碼,并看到它因啟用的運行時檢查而崩潰。


這個很容易修復。最直接的方法是在類型中添加一個互斥變量ClientList:


type ClientList struct {

  mu      sync.Mutex

  clients []*Client

}

…并使類型的方法在改變字段時保持互斥量clients,如下所示:


func (cList *ClientList) AddClient(cl *Client) {

  cList.mu.Lock()

  defer cList.mu.Unlock()


  cList.clients = append(cList.clients, o)

}

(如果你曾經遇到過你的ClientList類型的典型使用模式是頻繁調用只讀取包含列表的方法,你可以開始使用sync.RWLock允許多個并發讀取器的類型。)


其次,我將“識別”客戶端的部分從處理函數中分離出來。至此,在handler中,如果識別失敗,handler退出,但client不會退市。


我會說最好預先識別它,并且只有在認為客戶沒問題時才運行處理程序。


此外,值得RemoveClient在處理程序主體的頂部添加一個延遲調用,以便在處理程序完成時正確地取消列出客戶端。


IOW,我希望看到這樣的事情:


func (cl *Client) HandleClient(cList *ClientList) {

    defer cl.Conn.Close()


    err := cl.Identify()

    if err != nil {

        log.Println(err)

        return

    }


    cList.AddClient(cl)

    defer cList.RemoveClient(cl)


    // ... the rest of the code

}


查看完整回答
反對 回復 2023-06-19
  • 1 回答
  • 0 關注
  • 126 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號