这个应用演示如何使用 Google Go 语言和 HTML5 的 WebSocket 来实现一个简单的基于 Web 的聊天程序。
下图是聊天应用的截图:
你可输入 email 来加入聊天室,我们将从 Gravatar 上获取对应的用户名和头像,当你正在聊天时,你能在界面右侧看到聊天室其他人的姓名和头像。你可以输入信息来跟他们聊天。
现在,让我们来看看如何实现这么一个程序。
服务器端首先我们需要一个名为 ActiveRoom 聊天室引擎作为整个应用的核心。该引擎将在下面的代码中定义,当程序主函数启动时将会初始化一个聊天室引擎实例并作为一个全局变量。
正在运行的实例用于维护所有 websocket 连接并处理收到的消息。一旦通过广播渠道接收到一个新的消息,它会将该消息发送到所有连接发送渠道。
Message 是服务器和客户端数据交互的基本数据类型,在这里我们定义了两个消息类型,一个是文本消息,另外一个是实时的用户在线状态。
type ActiveRoom struct {
OnlineUsers map[string]*OnlineUser
Broadcast chan Message
CloseSign chan bool
}
type Message struct {
MType string
TextMessage TextMessage
UserStatus UserStatus
}
func (this *ActiveRoom) run() {
for {
select {
case b := <-this.Broadcast:
for _, online := range this.OnlineUsers {
online.Send <- b
}
case c := <-this.CloseSign:
if c == true {
close(this.Broadcast)
close(this.CloseSign)
return
}
}
}
}
OnlineUser 类代表一个成功连接到聊天室的用户,它维护着 ActiveRoom 实例的指针、服务器和客户端之间的 websocket 连接,同时包括正在聊天的用户和 Go 的通讯渠道。
OnlineUser 定义了两个指针方法PushToClient:
从发送渠道读取消息然后将这些消息通过 websocket 推送给客户端。
PullFromClient:
从客户端读取消息并发送到正在运行的 ActiveRoom 实例。
这两个方法使用 "for" 语句来等待新的消息,除非 websocket 连接中断。
type OnlineUser struct {
InRoom *ActiveRoom
Connection *websocket.Conn
UserInfo *User
Send chan Message
}
func (this *OnlineUser) PullFromClient() {
for {
var content string
err := websocket.Message.Receive(this.Connection, &content)
if err != nil {
return
}
m := Message{
MType: TEXT_MTYPE,
TextMessage: TextMessage{
UserInfo: this.UserInfo,
Time: humanCreatedAt(),
Content: content,
},
}
this.InRoom.Broadcast <- m
}
}