golang 实现 tcp-聊天室
以下代码都已传到github平台:https://github.com/ElzatAhmed/go-tcp-chatroom
想必做golang网络开发的同学对gosdk中net/http包十分熟悉,使用net/http可以快速实现http服务器的搭建。但是大家对tcp协议的直接使用可能就没有那么熟悉了。在接下来的文档里我将讲解如何使用gosdk中的net包实现一个以tcp协议为基础的简易的终端群聊聊天室应用。
在dive in之前,需要回顾/介绍以下golang并发编程中的核心概念:
goroutine
在go.dev中是这么介绍goroutine的 A goroutine is a lightweight thread managed by the Go runtime
,goroutine是一种被go runtime管理的轻量级线程,其并不会直接对应到重量级的系统线程上,而是被go scheduler调度到少量的系统线程上进行维护。所以在一个go程序中,我们可以同时创造成千上万个goroutine,将其交给go runtime去进行调度和垃圾回收。
channel
不知道你是否听说过一种理论叫做CSP,Communicating Sequential Processes
,go将其核心思想总结成以下 Do not communicate by sharing memory, share memory by communicating
,也就是说不要利用共享内存的方式去交互,利用交互的方式去共享内存(这里的交互这一名词可能用得不太对,大家可以以communication去理解)。channel正是利用这种思想而定义的。channel可以被理解为支持写入和读取的消息管道,其是完全并发安全,channel的每一个元操作都是以单线程的方式进行的,这就代表我们不仅能利用channel进行线程间通信,我们还可以利用它实现锁。以下是一种利用buffered channel (size大于0的channel) 实现的结构非常简单的锁:
package chlock
// Chlock is a locker implemented using channel
type Chlock struct {
ch chan interface{
}
}
// New returns the pointer to a new Chlock struct
func New() *Chlock {
return &Chlock{
ch: make(chan interface{
}, 1),
}
}
// Lock writes once to the channel and
// blocks all the other goroutines which tries to write
func (lock *Chlock) Lock() {
lock.ch <- struct{
}{
}
}
// Unlock reads from the channel and unblock one goroutine
func (lock *Chlock) Unlock() {
<-lock.ch
}
创建1000个goroutine对同一个全局变量count
进行加一操作去测试我们实现的ChLock是否有效:
package main
import (
"fmt"
"sync"
"github.com/elzatahmed/channel-lock/chlock"
)
var count int
func main() {
var wg sync.WaitGroup
wg.Add(1000)
lock := chlock.New()
for i := 0; i < 1000; i++ {
go add(lock, &wg)
}
wg.Wait()
fmt.Printf("count = %d\n", count)
}
func add(lock *chlock.Chlock, wg *sync.WaitGroup) {
lock.Lock()
defer wg.Done()
defer lock.Unlock()
count++
}
输出:
➜ channel-lock go run main.go
count = 1000
介绍完goroutine和channel之后我们就dive in到实现tcp聊天室的过程当中:
tcp聊天室的实现
首先我们开始打造聊天室的模型基础:
模型
第一个模型即用户模型,我们简单的以用户的名字作为用户的主键,并为其创建两个channel:
- receive channel:即获取消息的channel
- done channel:发送/获取断开连接的channel
// user is a single connection to the tcp chat server
type user struct {
name string
receive chan message
done chan interface{