分享
  1. 首页
  2. 文章

Go 群聊 ( goroutine )

SeaConch · · 1671 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

前言

看了无闻老师的一节关于 goroutine 与 channel 的讲解课堂,感觉不是很明白,所以决定来实现一个聊天室的功能

为什么是群聊呢?

因为群聊相对逻辑简单些

注:本栗子只用到了 goroutine 并没有用到 channel

概述

1.聊天室的组成

聊天室分为两个部分,分别是:

  • 服务端
  • 客户端

然后,一般情况下我们互相聊天使用的都只是客户端而已,服务端只是起到调度的作用

2.信息发送与接收的流程

假设我们有 服务端(S) 客户端(C1) 客户端(C2) 客户端(C3)

并且 S 已经 与 C1 C2 C3 建立了连接

理论上的流程是这样的:

  1. C1S 发出信息
  2. S 接收到信息
  3. S 将接收到的信息广播给 C2 C3
  4. C2 C3 接收信息

图片描述

实践

1.服务端

1) 代码

package main
import (
 "time"
 "fmt"
 "net"
)
// 客户端 map 
var client_map = make(map[string]*net.TCPConn)
// 监听请求
func listen_client(ip_port string) {
 tcpAddr, _ := net.ResolveTCPAddr("tcp", ip_port)
 tcpListener, _ := net.ListenTCP("tcp", tcpAddr)
 for {// 不停地接收
 client_con, _ := tcpListener.AcceptTCP()// 监听请求连接
 client_map[client_con.RemoteAddr().String()] = client_con// 将连接添加到 map
 go add_receiver(client_con)
 fmt.Println("用户 : ", client_con.RemoteAddr().String(), " 已连接.")
 }
}
// 向连接添加接收器
func add_receiver(current_connect *net.TCPConn) {
 for {
 byte_msg := make([]byte, 2048)
 len, err := current_connect.Read(byte_msg)
 if err != nil { current_connect.Close() }
 fmt.Println(string(byte_msg[:len]))
 msg_broadcast(byte_msg[:len], current_connect.RemoteAddr().String())
 }
}
// 广播给所有 client
func msg_broadcast(byte_msg []byte, key string) {
 for k, con := range client_map {
 if k != key { con.Write(byte_msg) }
 }
}
// 主函数
func main() {
 fmt.Println("服务已启动...")
 time.Sleep(1 * time.Second)
 fmt.Println("等待客户端请求连接...")
 go listen_client("127.0.0.1:1801")
 select{}
} 

b) 描述

可以看到,撇开 main 函数,一共有 2 个 routine,分别是:

  1. 监听连接
  2. 接收消息(广播消息也在这里)

2.客户端

a) 代码

package main
import (
 "fmt"
 "net"
 "os"
 "bufio"
)
// 用户名
var login_name string
// 本机连接
var self_connect *net.TCPConn
// 读取行文本
var reader = bufio.NewReader(os.Stdin)
// 建立连接
func connect(addr string) {
 tcp_addr, _ := net.ResolveTCPAddr("tcp", addr) // 使用tcp
 con, err := net.DialTCP("tcp", nil, tcp_addr) // 拨号
 self_connect = con
 if err != nil {
 fmt.Println("服务器连接失败")
 os.Exit(1)
 }
 go msg_sender()
 go msg_receiver()
}
// 消息接收器
func msg_receiver() {
 buff := make([]byte, 2048)
 for {
 len, _ := self_connect.Read(buff) // 读取消息
 fmt.Println(string(buff[:len]))
 }
}
// 消息发送器
func msg_sender() {
 for {
 read_line_msg, _, _ := reader.ReadLine()
 read_line_msg = []byte(login_name + " : " + string(read_line_msg))
 self_connect.Write(read_line_msg)
 }
}
// 主函数
func main() {
 fmt.Println("请问您怎么称呼?")
 name, _, _ := reader.ReadLine()
 login_name = string(name)
 connect("127.0.0.1:1801")
 select{}
}

b) 描述

同样,客户端也是有两个 routine 组成:

  1. 消息接收器
  2. 消息发送器

建立连接在主线程完成

3.效果图

a) 服务端

图片描述

b) 客户端_1

图片描述

c) 客户端_2

图片描述

结尾

这里并没有用到 channel

小栗子仅为经验总结,学习交流而记


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:SeaConch

查看原文:Go 群聊 ( goroutine )

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1671 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏