您好,登錄后才能下訂單哦!
通常聊天室的架構(gòu)分為服務(wù)器端和客戶端:
服務(wù)器端:
接受來自于客戶端的連接請求并建立連接;
所有客戶端的連接會放進連接池中,用于廣播消息;
客戶端:
連接服務(wù)器;
向服務(wù)器發(fā)送消息;
接收服務(wù)器的廣播消息;
注意事項:
某一個客戶端斷開連接后需要從連接池中摘除,并不再接收廣播消息;
某一個客戶端斷開連接后不能影響服務(wù)器端或別的客戶端的連接;
詳細(xì)的代碼如下,文檔看注釋就好了,不再細(xì)說:
服務(wù)器:
server.go
package main
import (
"net"
"log"
"fmt"
)
func main() {
port := "9090"
Start(port)
}
// 啟動服務(wù)器
func Start(port string) {
host := ":" + port
// 獲取tcp地址
tcpAddr, err := net.ResolveTCPAddr("tcp4", host)
if err != nil {
log.Printf("resolve tcp addr failed: %v\n", err)
return
}
// 監(jiān)聽
listener, err := net.ListenTCP("tcp", tcpAddr)
if err != nil {
log.Printf("listen tcp port failed: %v\n", err)
return
}
// 建立連接池,用于廣播消息
conns := make(map[string]net.Conn)
// 消息通道
messageChan := make(chan string, 10)
// 廣播消息
go BroadMessages(&conns, messageChan)
// 啟動
for {
fmt.Printf("listening port %s ...\n", port)
conn, err := listener.AcceptTCP()
if err != nil {
log.Printf("Accept failed:%v\n", err)
continue
}
// 把每個客戶端連接扔進連接池
conns[conn.RemoteAddr().String()] = conn
fmt.Println(conns)
// 處理消息
go Handler(conn, &conns, messageChan)
}
}
// 向所有連接上的鄉(xiāng)親們發(fā)廣播
func BroadMessages(conns *map[string]net.Conn, messages chan string) {
for {
// 不斷從通道里讀取消息
msg := <-messages
fmt.Println(msg)
// 向所有的鄉(xiāng)親們發(fā)消息
for key, conn := range *conns {
fmt.Println("connection is connected from ", key)
_, err := conn.Write([]byte(msg))
if err != nil {
log.Printf("broad message to %s failed: %v\n", key, err)
delete(*conns, key)
}
}
}
}
// 處理客戶端發(fā)到服務(wù)端的消息,將其扔到通道中
func Handler(conn net.Conn, conns *map[string]net.Conn, messages chan string) {
fmt.Println("connect from client ", conn.RemoteAddr().String())
buf := make([]byte, 1024)
for {
length, err := conn.Read(buf)
if err != nil {
log.Printf("read client message failed:%v\n", err)
delete(*conns, conn.RemoteAddr().String())
conn.Close()
break
}
// 把收到的消息寫到通道中
recvStr := string(buf[0:length])
messages <- recvStr
}
}
客戶端:
client.go
package main
import (
"net"
"log"
"fmt"
"os"
)
func main() {
Start(os.Args[1])
}
func Start(tcpAddrStr string) {
tcpAddr, err := net.ResolveTCPAddr("tcp4", tcpAddrStr)
if err != nil {
log.Printf("Resolve tcp addr failed: %v\n", err)
return
}
// 向服務(wù)器撥號
conn, err := net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
log.Printf("Dial to server failed: %v\n", err)
return
}
// 向服務(wù)器發(fā)消息
go SendMsg(conn)
// 接收來自服務(wù)器端的廣播消息
buf := make([]byte, 1024)
for {
length, err := conn.Read(buf)
if err != nil {
log.Printf("recv server msg failed: %v\n", err)
conn.Close()
os.Exit(0)
break
}
fmt.Println(string(buf[0:length]))
}
}
// 向服務(wù)器端發(fā)消息
func SendMsg(conn net.Conn) {
username := conn.LocalAddr().String()
for {
var input string
// 接收輸入消息,放到input變量中
fmt.Scanln(&input)
if input == "/q" || input == "/quit" {
fmt.Println("Byebye ...")
conn.Close()
os.Exit(0)
}
// 只處理有內(nèi)容的消息
if len(input) > 0 {
msg := username + " say:" + input
_, err := conn.Write([]byte(msg))
if err != nil {
conn.Close()
break
}
}
}
}
測試方法:
編譯server.go和client.go;
打開終端,啟動server,默認(rèn)會監(jiān)聽9090端口;
再打開多個終端,啟動client,client啟動命令:client 服務(wù)器IP:9090;
在client中輸入字符并回車,可以看到別的終端都會收到消息;
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。