1、并发聊天服务器原理分析

2、并发聊天室

功能:

广播消息、广播上线、 查询在线用户、修改用户名、用户主动退出、超时处理

示例:

package main

import (
"fmt"
"net"
"strings"
"time"
) type Client struct {
C chan string //用户发送数据的管道
Name string //用户名
Addr string //网络地址
} //保存在线用户 cliAddr =====> Client
var onlineMap map[string]Client var messaage = make(chan string) //新开一个协程,转发消息,只要有消息来了,遍历map, 给map每个成员都发送此消息
func Manager() {
//给map分配空间
onlineMap = make(map[string]Client) for {
msg := <-messaage //没有消息前,这里会阻塞 //遍历map, 给map每个成员都发送此消息
for _, cli := range onlineMap {
cli.C <- msg
}
}
} func WriteMsgToClient(cli Client, conn net.Conn) {
for msg := range cli.C { //给当前客户端发送信息
conn.Write([]byte(msg + "\n"))
}
} func MakeMsg(cli Client, msg string) (buf string) {
buf = "[" + cli.Addr + "]" + cli.Name + ": " + msg return
} func HandleConn(conn net.Conn) { //处理用户链接
defer conn.Close() //获取客户端的网络地址
cliAddr := conn.RemoteAddr().String() //创建一个结构体, 默认,用户名和网络地址一样
cli := Client{make(chan string), cliAddr, cliAddr}
//把结构体添加到map
onlineMap[cliAddr] = cli //新开一个协程,专门给当前客户端发送信息
go WriteMsgToClient(cli, conn)
//广播某个在线
//messaage <- "[" + cli.Addr + "]" + cli.Name + ": login"
messaage <- MakeMsg(cli, "login")
//提示,我是谁
cli.C <- MakeMsg(cli, "I am here") isQuit := make(chan bool) //对方是否主动退出
hasData := make(chan bool) //对方是否有数据发送 //新建一个协程,接收用户发送过来的数据
go func() {
buf := make([]byte, 2048)
for {
n, err := conn.Read(buf)
if n == 0 { //对方断开,或者,出问题
isQuit <- true
fmt.Println("conn.Read err = ", err)
return
} msg := string(buf[:n-1]) //通过windows nc测试,多一个换行
if len(msg) == 3 && msg == "who" {
//遍历map,给当前用户发送所有成员
conn.Write([]byte("user list:\n"))
for _, tmp := range onlineMap {
msg = tmp.Addr + ":" + tmp.Name + "\n"
conn.Write([]byte(msg))
} } else if len(msg) >= 8 && msg[:6] == "rename" {
// rename|mike
name := strings.Split(msg, "|")[1]
cli.Name = name
onlineMap[cliAddr] = cli
conn.Write([]byte("rename ok\n")) } else {
//转发此内容
messaage <- MakeMsg(cli, msg)
} hasData <- true //代表有数据
} }() //别忘了() for {
//通过select检测channel的流动
select {
case <-isQuit:
delete(onlineMap, cliAddr) //当前用户从map移除
messaage <- MakeMsg(cli, "login out") //广播谁下线了 return
case <-hasData: case <-time.After(30 * time.Second): //60s后
delete(onlineMap, cliAddr) //当前用户从map移除
messaage <- MakeMsg(cli, "time out leave out") //广播谁下线了
return
}
}
} func main() {
//监听
listener, err := net.Listen("tcp", ":8000")
if err != nil {
fmt.Println("net.Listen err = ", err)
return
} defer listener.Close() //新开一个协程,转发消息,只要有消息来了,遍历map, 给map每个成员都发送此消息
go Manager() //主协程,循环阻塞等待用户链接
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("listener.Accept err = ", err)
continue
} go HandleConn(conn) //处理用户链接
} }

执行结果:

Go语言之进阶篇实现并发聊天功能的更多相关文章

  1. go语言之进阶篇接口转换

    1.go语音之进阶篇 示例: package main import "fmt" type Humaner interface { //子集 sayhi() } type Pers ...

  2. go语言之进阶篇并行和并发的区别与go语言并发优势

    1.并行和并发的概念 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行. 并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在 ...

  3. Go语言之进阶篇爬百度贴吧并发版

    1.爬百度贴吧并发版 示例: package main import ( "fmt" "net/http" "os" "strco ...

  4. Go语言之进阶篇简单版并发服务器

    1.简单版并发服务器 示例1: package main import ( "fmt" "net" "strings" ) //处理用户请求 ...

  5. go语言之进阶篇创建goroutine协程

    1.goroutine是什么 goroutine是Go并行设计的核心.goroutine说到底其实就是协程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现 ...

  6. Go语言之进阶篇爬捧腹网

    1.爬捧腹网 网页规律: https://www.pengfu.com/xiaohua_1.html   下一页 +1 https://www.pengfu.com/xiaohua_2.html 主页 ...

  7. go语言之进阶篇通过select实现斐波那契数列

    一.select作用 Go里面提供了一个关键字select,通过select可以监听channel上的数据流动. select的用法与switch语言非常类似,由select开始一个新的选择块,每个选 ...

  8. go语言之进阶篇JSON处理

    一.JSON处理 JSON (JavaScript Object Notation)是一种比XML更轻量级的数据交换格式,在易于人们阅读和编写的同时,也易于程序解析和生成.尽管JSON是JavaScr ...

  9. go语言之进阶篇正则表达式

    正则表达式是一种进行模式匹配和文本操纵的复杂而又强大的工具.虽然正则表达式比纯粹的文本匹配效率低,但是它却更灵活.按照它的语法规则,随需构造出的匹配模式就能够从原始文本中筛选出几乎任何你想要得到的字符 ...

随机推荐

  1. mongodb spring 配置文件

    在使用spring-data-mongodb中,需要配置spring文件,如下: mongodb.xml <?xml version="1.0" encoding=" ...

  2. RelativeLayout 高度宽度

    RelativeLayout ss = (RelativeLayout) findViewById(R.id.myRelativeLayout); ss.setLayoutParams(new Rel ...

  3. ICE::Handle 使用崩溃问题

    简单例子如下: #include "Ice/Ice.h" #include "IceUtil/IceUtil.h" #include "Printer ...

  4. oracle 删除字段中空格

    update  sales_report set region =  REGEXP_REPLACE(region,  '( ){1,}', '')

  5. 什么是K线?K线的详解!

    K线图这种图表源于日本德川幕府时代,被当时日本米市的商人用来记录米市的行情与价格波动,后因其细腻独到的标画方式而被引入到股市及期货市场.目前,这种图表分析法在我国乃至整个东南亚地区均尤为流行.由于绘制 ...

  6. How can i use iptables on centos 7?

    I installed CentOS 7 with minimal configuration (os + dev tools). I am trying to open 80 port for ht ...

  7. NSNotificationCenter消息注册与撤销

    苹果的消息机制是个非常好用的东西,当需要在类的各个实例之间传递消息或者写一些事件驱动的程序时,绝对是个不错的工具.但是使用时一不小心就会造成引用已经被dealloc的对象的错误,引起程序崩溃.于是,在 ...

  8. Ant build.xml

    Ant的概念可能有些读者并不连接什么是Ant以及入可使用它,但只要使用通过Linux系统得读者,应该知道make这个命令.当编译Linux内核及一些软件的源程序时,经常要用这个命令.Make命令其实就 ...

  9. 如何删除mac keeper

    如果不小心安装了mac keeper,基本是无法删除的,而且16年以前的方法都不管用.可以这样删除,我已经测试过了,下载https://data-cdn.mbamupdates.com/web/mba ...

  10. @Transactional事务几点注意

    这里面有几点需要大家留意:A. 一个功能是否要事务,必须纳入设计.编码考虑.不能仅仅完成了基本功能就ok.B. 如果加了事务,必须做好开发环境测试(测试环境也尽量触发异常.测试回滚),确保事务生效.C ...