2017-01-16 2 views
0

Я реализовал демонстрационный сервер чата tcp в golang, он отлично работает, но каждый раз, когда пользователь отключается, и я пытаюсь написать сообщение на широковещательный канал, чтобы другие пользователи знали пользователь отключил его и не будет обрабатывать какие-либо новые сообщения от другого клиента, потому что его небуферизованный каналПерейти: Проблема с блокировкой с каналом go и выберите

Я прокомментировал код и объяснил, что вы можете пройти через него, я не знаю, почему код блоки, я написал сообщ

  1. Я собирался написать на канал
  2. я написал к й е каналы
  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

В Go, небуферизованный канал является «точкой синхронизации». То есть, если у вас есть канал c, и do c <- value, goroutine блокирует, пока кто-то не будет готов делать v = <- c (и обратное имеет место, получая от блокирующего канала без чего-либо, чтобы получать блоки до тех пор, пока значение не будет доступно, но это возможно менее удивительно). В частности, для блокирующего канала прием завершается до завершения отправки.

Поскольку у вас есть только один горутин, он не сможет вернуться к чтению с канала, и запись будет заблокирована, пока что-то не прочитает.

Вы могли бы теоретически обойти это, сделав что-то вроде: go func() { msg <- fmt.Sprintf("Disconneting", allConn[conn] }(), так что, по сути, создавая недолговечный горотин, чтобы писать.

+0

Спецификации описаны в https://golang.org/ref/mem#tmp_7 (раздел «Связь с каналом», если изменение привязки). – Vatine

Смежные вопросы