2017-02-09 39 views
0

我創建一個簡單的聊天服務器作爲一個個人項目來學習網絡包和一些併發去。我的第一個想法是使服務器使用nc命令echo -n "hello" | nc -w1 -4 localhost 2016 -p 61865發送任何內容。然而,在第一次讀取後,我的代碼會忽略後續消息。我怎樣才能繼續閱讀使用網絡康恩閱讀方法

func (s *Server) messageReader(conn net.Conn) { 
defer conn.Close() 
buffer := make([]byte, 1024) 
for { 
    //read buff 
    blen, err := conn.Read(buffer) 
    if err != nil { 
     log.Fatal(err) 
    } 

    message := string(buffer[:blen]) 

    if message == "/quit" { 
     fmt.Println("quit command received. Bye.") 
     return 
    } 

    if blen > 0 { 
     fmt.Println(message) 
     buffer = buffer[:0] 
    } 
} 
} 

// Run Start up the server. Manages join and leave chat 
func (s *Server) Run() { 
// Listen on port TCP 2016 
listener, err := net.Listen("tcp", ":2016") 
if err != nil { 
    log.Fatal(err) 
} 
defer listener.Close() 

for { 
    //wait for connection 
    conn, err := listener.Accept() 
    if err != nil { 
     log.Fatal(err) 
    } 

    go s.messageReader(conn) 

} 
} 

如果我發送一個新的消息,從一個新的客戶端將打印沒有問題,但如果我再派一個什麼都不做。我錯過了什麼,我需要重置康恩或關閉它,併產生一個新的?

回答

3

打印完信息後,將buffer切成長度爲零。您無法將任何數據讀入零長度片。根本沒有理由重新切片你的讀取緩衝區。

您還需要在檢查錯誤之前處理讀取的字節,因爲io.EOF可以在成功讀取時返回。

你不應該在服務器的讀取循環使用log.Fatal,如調用os.Exit

的工作messageReader體可能看起來像:

defer conn.Close() 
buffer := make([]byte, 1024) 
for { 
    n, err := conn.Read(buffer) 
    message := string(buffer[:n]) 

    if message == "/quit" { 
     fmt.Println("quit command received. Bye.") 
     return 
    } 

    if n > 0 { 
     fmt.Println(message) 
    } 

    if err != nil { 
     log.Println(err) 
     return 
    } 
} 

你應該因爲你沒有使用雖然那注意任何形式的組幀協議在這裏,你不能保證每個conn.Read返回一個完整的或單一的消息。您需要有某種更高級別的協議才能在流中分隔消息。

+0

你會在標準庫中建議什麼協議? '淨/ HTTP'可以工作,但如果我沒有誤HTTP不是一個消息協議。我試圖保持範圍小,但我也想知道可以做什麼或應該如何做。 感謝您的答案額外的位。非常有用的知識。 – cmejia

+0

@cmejia:協議取決於你的需求。最常見的成幀方法可能是簡單的以長度爲前綴的字符串。你也可以做一個你可以確保不會出現在你的消息中的分隔符。 ['net/textproto'](https://golang.org/pkg/net/textproto/)包含點編碼格式,使用'\ r \ n'分隔行,'。\ r \ n'以表示消息的結尾以及MIME格式鍵值。 – JimB