1. 程式人生 > >Golang實戰-一個聊天室的實現

Golang實戰-一個聊天室的實現

《Go語言程式設計》中第8.10節有個聊天室的例子,我們就來跟著完成這個例子。

先來看看主函式:

func main() {
	listener, err := net.Listen("tcp", "localhost:8000")
	if err != nil {
        //log.Fatal()列印錯誤資訊並呼叫os.Exit(1),終止程式
		log.Fatal(err)
	}

    //廣播,傳送訊息到所有客戶端
	go broadcaster()

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println(err)
			continue
		}
		go handleConn(conn)
	}
}

下面是broadcaster()相關程式碼:
//channel的三種類型(只發送、只接受、即傳送也接受)
//這裡client只發送不接受
//只接受 type client <-chan string
//即傳送也接受 type client chan string
type client chan<- string

var (
	entering = make(chan client)
	leaving  = make(chan client)
	message  = make(chan string)
)

func broadcaster() {
	clients := make(map[client]bool)
	for {
		select {
		case msg := <-message:
			for cli := range clients {
				cli <- msg
			}
		case cli := <-entering:
			clients[cli] = true
		case cli := <-leaving:
			delete(clients, cli)
			close(cli)
		}
	}
}
broadcaster監聽全域性的entering和leaving來獲知客戶端的進入和離開事件,當其接受到其中一個事件時,會更新clients集合。

broadcaster也會監聽全域性的message channel,所有客戶端的訊息都會發送到這個channel中,當message中有訊息時,broadcaster就會將其廣播至所有連線到服務端的客戶端。

func handleConn(conn net.Conn) {
	ch := make(chan string)
    //寫入訊息到客戶端的連線
	go writeToCLient(conn, ch)

	who := conn.RemoteAddr().String()
    //當客戶端連線過來時,給客戶端一條訊息
    //注意,這時的ch會立馬被writeToCLient goroutine讀取,併發送到當前客戶端
    //所以已連線的其他客戶端不會接受到該條訊息
	ch <- "You are " + who
    //這裡的message channel會被broadcaster讀取,廣播給所有已連線的客戶端
    //注意,這時當前客戶端還沒給entering,所以當前客戶端不會接受到該條訊息
	message <- who + " are arrived"
    //將當前客戶端傳送給entering channel,broadcaster會將當前客戶端新增到已連線的客戶端集合中
	entering <- ch

	input := bufio.NewScanner(conn)
    //阻塞監聽客戶端輸入
	for input.Scan() {
        //獲取客戶端輸入,併發送到message channel,然後broadcaster會將它廣播給所有連線的客戶端
        //因為這時,當前客戶端已經新增到clients集合中,所以當前客戶端也會接受到訊息
		message <- who + ": " + input.Text()
	}

    //客戶端斷開連線
	leaving <- ch
	message <- who + " are left"
	conn.Close()
}

func writeToCLient(conn net.Conn, ch <-chan string) {
	for msg := range ch {
		fmt.Fprintln(conn, msg)
	}
}

下面貼上完整程式碼

client:

package main

import (
	"io"
	"log"
	"net"
	"os"
)

func main() {
	conn, err := net.Dial("tcp", "localhost:8000")
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	//獲取服務端訊息
	go ioCopy(os.Stdout, conn)
	//將使用者輸入的文字訊息傳送到到服務端
	ioCopy(conn, os.Stdin)
}

func ioCopy(dst io.Writer, src io.Reader) {
	if _, err := io.Copy(dst, src); err != nil {
		log.Fatal(err)
	}
}

server:
package main

import (
	"bufio"
	"fmt"
	"log"
	"net"
)

func main() {
	listener, err := net.Listen("tcp", "localhost:8000")
	if err != nil {
        //log.Fatal()列印錯誤資訊並呼叫os.Exit(1),終止程式
		log.Fatal(err)
	}

    //廣播,傳送訊息到所有客戶端
	go broadcaster()

	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Println(err)
			continue
		}
        //每個客戶端一個goroutine
		go handleConn(conn)
	}
}

//channel的三種類型(只發送、只接受、即傳送也接受)
//這裡client只發送不接受
//只接受 type client <-chan string
//即傳送也接受 type client chan string
type client chan<- string

var (
	entering = make(chan client)
	leaving  = make(chan client)
	message  = make(chan string)
)

func broadcaster() {
	clients := make(map[client]bool)
	for {
		select {
		case msg := <-message:
			for cli := range clients {
                //這裡的cli就是handleConn裡的ch channel,
                //writeToCLient goroutine一直在監聽ch channel,讀取channel中的內容,並寫入客戶端連線
				cli <- msg
			}
		case cli := <-entering:
			clients[cli] = true
		case cli := <-leaving:
			delete(clients, cli)
			close(cli)
		}
	}
}

func handleConn(conn net.Conn) {
	ch := make(chan string)
    //寫入訊息到客戶端的連線
	go writeToCLient(conn, ch)

	who := conn.RemoteAddr().String()
    //當客戶端連線過來時,給客戶端一條訊息
    //注意,這時的ch會立馬被writeToCLient goroutine讀取,併發送到當前客戶端
    //所以已連線的其他客戶端不會接受到該條訊息
	ch <- "You are " + who
    //這裡的message channel會被broadcaster讀取,廣播給所有已連線的客戶端
    //注意,這時當前客戶端還沒給entering,所以當前客戶端不會接受到該條訊息
	message <- who + " are arrived"
    //將當前客戶端傳送給entering channel,broadcaster會將當前客戶端新增到已連線的客戶端集合中
	entering <- ch

	input := bufio.NewScanner(conn)
    //阻塞監聽客戶端輸入
	for input.Scan() {
        //獲取客戶端輸入,併發送到message channel,然後broadcaster會將它廣播給所有連線的客戶端
        //因為這時,當前客戶端已經新增到clients集合中,所以當前客戶端也會接受到訊息
		message <- who + ": " + input.Text()
	}

    //客戶端斷開連線
	leaving <- ch
	message <- who + " are left"
	conn.Close()
}

func writeToCLient(conn net.Conn, ch <-chan string) {
	for msg := range ch {
		fmt.Fprintln(conn, msg)
	}
}

$ go run chat_server.go

$ go run chat_client.go
You are 127.0.0.1:51246
127.0.0.1:51255 are arri
127.0.0.1:51255 are left
127.0.0.1:51265 are arri
127.0.0.1:51265: HaHa
127.0.0.1:51265 are left

$ go run chat_client.go
You are 127.0.0.1:51265
HaHa
127.0.0.1:51265: HaHa

接下來,加入點我們自己的東西:

新增房間功能,客戶端可以切換房間。

暫且想到這個,有空的話,會完成這些。

相關推薦

Golang實戰-一個聊天實現

《Go語言程式設計》中第8.10節有個聊天室的例子,我們就來跟著完成這個例子。 先來看看主函式: func main() { listener, err := net.Listen("tcp", "localhost:8000") if err != nil {

Golang之寫一個聊天

gpo 動作 color 字母數 信息 數據 用戶登錄 clas 是否 1. 海量用戶在線聊天系統 2. 點對點聊天 3. 用戶登錄&註冊 一、服務端開發 1. 用戶管理 用戶id:數字 用戶密碼:字母數字組合

socket 聊天實現

socket sets urn rom accep host amp ret nbsp server #include <stdio.h> #include <sys/types.h> /* See NOTES */ #inclu

java實戰——網路聊天

因特網已經成為了人們交流的海洋,上網的使用者越來越多,更多的人願意使用網路來認識別人,瞭解別人,這就是當下諸如qq、微信等聊天工具火熱的原因。 我們雖然不能立馬就開發出來一個像上述兩個一樣功能強大的聊天室,但我們可以自己製作一個簡單的聊天室。 我們採用傳統的Client/Server結構,功

Java學習筆記之--------網路程式設計之Socket通訊----聊天實現

Socket通訊 網路上的兩個程式通過一個雙向的通訊連線實現資料的交換,這個連線的一端稱為一個socket。基於TCP/IP協議,建立穩定的點對點的通訊。 特點:實時、快速、安全性高、佔用系統資源多、效率低。 通常也稱作"套接字",套接字是一種程序間的資料交換機制。這些程序既可以在同一機

PHP websocket之聊天實現

six tee blocking abc 監聽 nodes 內容 bug 獲取 PHP部分 &lt;?php error_reporting(E_ALL); set_time_limit(0);// 設置超時時間為無限,防止超時 date_default_timez

使用socket.io寫一個聊天

之前學習了常用的api,以及概念,這裡為了簡單的使用,寫一個demo,為了方便查詢api,這裡給一個傳送門socket.io 準備 老規矩,新建一個目錄,一個index.html, app.js index.html 為客戶端 app.js 為服

golang簡易版聊天

服務端: package main import ( "fmt" "net" ) type info struct { conn net.Conn name string } var ch chan string = make(chan s

SpringBoot整合WebSocket,打造一個聊天

本文,我們來講下SpringBoot整合WebSocket,打造一個聊天室。   WebSocket 是什麼?   WebSocket 是一種網路通訊協議,RFC6455 定義了它的通訊標準。 瞭解計算機網路協議的人應該都知道,HTTP 協議是一種無狀態

PHP 使用 swoole 實現即時通訊 demo,以及聊天實現方式

服務端程式碼 ini_set('display_errors', 1); error_reporting(E_ALL); $ws = new swoole_websocket_server("0.0.0.0", 8080); //監聽WebSocket連線開啟事件  

三種TCP協議聊天實現

一 概述 使用Java的IO實現聊天室 使用Java的NIO實現聊天室 使用Netty實現聊天室 二 IO聊天室 1 伺服器 public class IOServer { public static void main(String[] args) throws IOEx

python編寫簡易聊天實現區域網內聊天

功能: 可以向區域網內開啟接收資訊功能的ip進行傳送資訊,我們可以寫兩段埠不同的程式碼來實現在一臺電腦上與自己聊天. 關鍵點: 要想實現此功能必須將程式的埠固定 from socket impo

聊天實現-客戶端實現

cli write username flag equal else ace 釋放 clinet public class ClientSide { //服務端IP private String serverIP; //端口號 privat

[Qt] 基於Tcp協議的聊天實現

時間:2017年6月21日 一、寫在前面:         平時做圖形學的東西多一些,雖然一直對網路程式設計很感興趣,但是沒有什麼機會去嘗試一下。最近正好趕上期末的課程實習,然後就參考Qt官方的 N

融雲集成一個聊天頁面(vue版本)

  首先,說一下使用情況。因為需求,需要做一個聊天室頁面,因為不是專門的點對點聊天,是類似直播,但是是文字直播平臺的那種。現在一般的課堂,可能會需要這種。分為2個端,一個是講師端,一個是使用者端。講師端可能是單獨的APP。使用者端的頁面可能是內嵌到專門的APP,或者是微信公眾

用NodeJs使用Io做一個聊天

浪費了“黃金五年”的Java程式設計師,還有救嗎? >>>   

使用ASP.NET SignalR實現一個簡單的聊天

spl 記錄 歷史 undefine reat 語句 關鍵字 pda name  前言   距離我寫上一篇博客已經又過了一年半載了,時間過得很快,一眨眼,就把人變得滄桑了許多。青春是短暫的,知識是無限的。要用短暫的青春,去學無窮無盡的知識,及時當勉勵,歲月不待人。今天寫個隨

Netty+Android搭建一個簡易聊天實現群聊和私聊)

零,前言 JRBM專案中無論是好友私聊,公開聊天室,還是比賽平臺都需要用到長連線,之前沒有接觸過網路通訊等知識,更別說框架了,因此直接上手netty確實有些困難,在前期主要是在b站上看(https://www.bilibili.com/video/av26415011)這個

Netty+Websocket 實現一個簡易聊天

後臺程式碼 /** * 服務端 */ public class ChatServer { public static void main(String[] args) throws Exception { int port=8080; //服務端預設埠 new Ch

Go實戰 golang中使用WebSocket實時聊天 gorilla/websocket nkovacs/go s

                     生命不止,繼續 go go go!!!其實,早就應該跟大家分享golang中關於websocket的使用,但是一直不知道從何入手,也不能夠很清晰的描述出來。今天就淺嘗輒止,通過第三方庫實現websocket。WebSocketWebSocket協議是基於TCP的一種新