2017-01-16 34 views
0

我已经在golang中实现了一个演示tcp聊天服务器,它工作正常,但每当用户断开连接,我尝试写一条消息到广播频道让其他用户知道用户已经断开它的阻止,并且不会进一步处理来自其他客户端的任何新消息,因为它的非缓冲通道前往:与去频道的死锁问题,并选择

我已经评论了代码并解释了它可以通过它,我不知道为什么代码块,我已经写了封邮件

  1. 我要写一个通道
  2. 我写TH E通道
  3. 我从通道

阅读和消息井井有条还是我的味精通道块。 Ps:如果我使用缓冲通道,代码不会阻塞,但我想知道我的代码卡在哪里。 我也尝试运行代码才能使用标志-RACE,但没有帮助

package main 

import (
    "fmt" 
    "io" 
    "net" 
    "sync" 
) 

func main() { 
    msg := make(chan string)   //broadcast channel (making it buffered channel the problem goes away) 
    allConn := make(map[net.Conn]int) //Collection of incoming connections for broadcasting the message 
    disConn := make(chan net.Conn) //client disconnect channel 
    newConn := make(chan net.Conn) //new client connection channel 
    mutext := new(sync.RWMutex)  //mux to assign unique id to incoming connections 
    i := 0 
    listener, err := net.Listen("tcp", "127.0.0.1:8081") 
    checkErr(err) 
    fmt.Println("Tcp server started at 127.0.0.1:8081") 
    //Accept incoming connections and store them in global connection store allConn 
    go func() { 
     for { 
      conn, err := listener.Accept() 
      checkErr(err) 
      mutext.Lock() 
      allConn[conn] = i 
      i++ 
      mutext.Unlock() 
      newConn <- conn 
     } 
    }() 
    for { 
     select { 
     //Wait for a new client message to arrive and broadcast the message 
     case umsg := <-msg: 
      fmt.Println("Broadcast Channel: Already Read") 
      bmsg := []byte(umsg) 
      for conn1, _ := range allConn { 
       _, err := conn1.Write(bmsg) 
       checkErr(err) 
      } 

     //Handle client disconnection [disConn] 
     case conn := <-disConn: 
      mutext.RLock() 
      fmt.Println("user disconneting", allConn[conn]) 
      mutext.RUnlock() 
      delete(allConn, conn) 
      fmt.Println("Disconnect: About to Write") 
      //this call results in deadlock even when channel is empty, buffered channel resolves the issue 
      //need to know why 
      msg <- fmt.Sprintf("Disconneting", allConn[conn]) 
      fmt.Println("Disconnect: Already Written") 

     //Read client incoming message and put it on broadcasting channel and upon disconnect put on it disConn channel 
     case conn := <-newConn: 
      go func(conn net.Conn) { 
       for { 
        buf := make([]byte, 64) 
        n, err := conn.Read(buf) 
        if err != nil { 
         if err == io.EOF { 
          disConn <- conn 
          break 
         } 
        } 
        fmt.Println("Client: About to Write") 
        msg <- string(buf[0:n]) 
        fmt.Println("Client: Already Written") 
       } 
      }(conn) 
      mutext.RLock() 
      fmt.Println("User Connected", allConn[conn]) 
      mutext.RUnlock() 
     } 
    } 
} 
func checkErr(err error) { 
    if err != nil { 
     panic(err) 
    } 
} 

回答

4

在围棋,无缓冲通道是“同步点”。也就是说,如果你有一个频道c,并且做了c <- value,那么goroutine会阻塞,直到有人准备做v = <- c(并且反过来,从阻塞的频道接收没有东西接收块直到值可用,但这可能是可能的不那么令人惊讶)。具体而言,对于阻塞通道,接收在发送完成之前完成。

由于您只有一个goroutine,它将无法循环回读取通道,并且写入将被阻塞,直到可以读取为止。

从理论上讲,您可以通过执行如下操作来解决此问题:go func() { msg <- fmt.Sprintf("Disconneting", allConn[conn] }(),因此基本上会产生一个短暂的goroutine来执行写操作。

+0

有关详细信息,请参阅https://golang.org/ref/mem#tmp_7(“通道通信”部分,如果锚点更改)。 – Vatine