2017-01-16 3 views
0

golang에서 데모 TCP 채팅 서버를 구현했지만 정상적으로 작동하지만 사용자가 연결을 끊을 때마다 브로드 캐스트 채널에 메시지를 작성하여 다른 사용자가 사용자가 블록을 연결 해제했으며 버퍼가없는 채널이기 때문에 다른 클라이언트의 새 메시지를 더 이상 처리하지 않습니다.Go : go 채널의 교착 상태 문제 및 선택

코드로 댓글을 달았으며 설명 할 수있는 이유를 설명했습니다. 블록, 내가 작성한 MSG를

나는 토륨을 작성한 채널
  • 에 대해 쓸거야
    1. E 채널은
    2. I는 채널

    에서 읽은 메시지는 여전히 완벽하게하기 위해 내 MSG 채널 블록에 있습니다.

    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 일 경우 누군가가 v = <- c을 할 준비가 될 때까지 차단됩니다 (반대의 경우 차단 채널에서 값을 사용할 수있을 때까지 블록을 수신 할 필요가 없지만 가능하다면 덜 놀라운). 특히 차단 채널의 경우 수신이 완료되기 전에 수신이 완료됩니다.

    하나의 goroutine 만 있으므로 채널에서 읽기로 되돌릴 수 없으며 읽을 수있을 때까지 쓰기가 차단됩니다.

    이론적으로는 go func() { msg <- fmt.Sprintf("Disconneting", allConn[conn] }()과 같은 것을 수행하여이 문제를 해결할 수 있습니다. 따라서 본질적으로 수명이 짧은 goroutine을 작성하여 작성합니다.

    +0

    자세한 내용은 https://golang.org/ref/mem#tmp_7 (앵커가 변경되면 "채널 통신"섹션)에 설명되어 있습니다. – Vatine