华风 发表于 2018-9-20 10:23:14

Golang之chan/goroutine(转)

  原文地址:http://tchen.me/posts/2014-01-27-golang-chatroom.html?utm_source=tuicool&utm_medium=referral 看了一上午写得很好,可以拿来试试刀
  最近在team内部培训golang,目标是看看golang能否被C工程师快速掌握。我定了个一个月,共计20小时的培训计划,首先花10个小时(两周,每天1小时)让大家掌握golang的基本要素,能写一些入门级的程序,之后再花两周时间做一个1000行代码规模的Proof of concept项目。为了能在培训的slides上直接运行go code,我做了个简单的 coderunnerd,可以接受websocket传过来的code,编译运行再把stdout返回给websocket,为了更清晰地说明goroutine和chan的使用,以及golang的一些best practice,我分阶段写了个 chatroom。本文介绍一下如何使用goroutine和chan来做一个简单的聊天室。

需求
  聊天室的需求很简单:


[*]服务器监听某个端口,客户端可连接并开始聊天。
[*]任何客户端的发言都会被广播给所有客户端。
[*]客户端可以为自己设定名字或者执行一些聊天命令。
设计与实现

基本想法
  服务器(Server):


[*]Server accept下来的connection被存在一个数据结构Client中,并以connection为key,Client为value,存在map里。
[*]每个Client都有自己的goroutine去接受和发送消息。Client和Server之间通过channel来传递消息。
  客户端(Client):


[*]发送和接收都有各自的goroutine,通过channel和stdin/stdout交互
实现
  所有chat相关的逻辑都被封装在 chat package里,client和server的cli只负责将ui和chat粘合起来。
  首先,是核心的数据结构:
  

type Message chan string  

  
type Client struct {
  
conn   net.Conn
  
incoming Message
  
outgoing Message
  
reader   *bufio.Reader
  
writer   *bufio.Writer
  
quitingchan net.Conn
  
name   string
  
}
  

  Client 是一个服务器和客户端都共享的数据结构。conn是建立的连接,reader/writer是conn上的bufio。Client与外界的接口是incoming/outgoing两个channel,即:Server 会把要发送的内容 push 到 outgoing channel 里,供writer去写;而从reader读入的数据会 push 到 incoming channel 里,供 Server 读。
  每个 Client 有自己的名字,服务器端代码会使用这个名字(客户端代码不会使用)。
  

type Token chan int  
type ClientTable map*Client
  

  
type Server struct {
  
listener net.Listener
  
clientsClientTable
  
tokens   Token
  
pendingchan net.Conn
  
quitingchan net.Conn
  
incoming Message
  
outgoing Message
  
}
  

  Server 保存一张 ClientTable。每个 accept 到的 conn 会 push 进 pending channel,等待创建client。Server有 incoming / outgoing 两个 channel,分别和 client 的 incoming / outgoing 关联。
  Server 有一组 tokens,决定了一个Server最多能装多少Client(避免Server overloading)。
  下面看 Server 的创建流程:
  

const (  
MAXCLIENTS = 50
  
)
  

  
func CreateServer() *Server {
  
server := &Server{
  
clients:make(ClientTable, MAXCLIENTS),
  
tokens:   make(Token, MAXCLIENTS),
  
pending:make(chan net.Conn),
  
quiting:make(chan net.Conn),
  
incoming: make(Message),
  
outgoing: make(Message),
  
}
  
server.listen()
  
return server
  
}
  

  很简单,无须多说。server.Listen() 实现如下:
  

func (self *Server) listen() {  
go func() {
  
for {
  
select {
  
case message :=
页: [1]
查看完整版本: Golang之chan/goroutine(转)