Go语言之进阶篇实现并发聊天功能
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语言之进阶篇实现并发聊天功能的更多相关文章
- go语言之进阶篇接口转换
1.go语音之进阶篇 示例: package main import "fmt" type Humaner interface { //子集 sayhi() } type Pers ...
- go语言之进阶篇并行和并发的区别与go语言并发优势
1.并行和并发的概念 并行(parallel):指在同一时刻,有多条指令在多个处理器上同时执行. 并发(concurrency):指在同一时刻只能有一条指令执行,但多个进程指令被快速的轮换执行,使得在 ...
- Go语言之进阶篇爬百度贴吧并发版
1.爬百度贴吧并发版 示例: package main import ( "fmt" "net/http" "os" "strco ...
- Go语言之进阶篇简单版并发服务器
1.简单版并发服务器 示例1: package main import ( "fmt" "net" "strings" ) //处理用户请求 ...
- go语言之进阶篇创建goroutine协程
1.goroutine是什么 goroutine是Go并行设计的核心.goroutine说到底其实就是协程,但是它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现 ...
- Go语言之进阶篇爬捧腹网
1.爬捧腹网 网页规律: https://www.pengfu.com/xiaohua_1.html 下一页 +1 https://www.pengfu.com/xiaohua_2.html 主页 ...
- go语言之进阶篇通过select实现斐波那契数列
一.select作用 Go里面提供了一个关键字select,通过select可以监听channel上的数据流动. select的用法与switch语言非常类似,由select开始一个新的选择块,每个选 ...
- go语言之进阶篇JSON处理
一.JSON处理 JSON (JavaScript Object Notation)是一种比XML更轻量级的数据交换格式,在易于人们阅读和编写的同时,也易于程序解析和生成.尽管JSON是JavaScr ...
- go语言之进阶篇正则表达式
正则表达式是一种进行模式匹配和文本操纵的复杂而又强大的工具.虽然正则表达式比纯粹的文本匹配效率低,但是它却更灵活.按照它的语法规则,随需构造出的匹配模式就能够从原始文本中筛选出几乎任何你想要得到的字符 ...
随机推荐
- 初识thinkphp(2)
thinkphp的url路径的表示格式为 http://ip/tp/public/index.php/模块/控制器/操作 这里url最后的操作就是类里面的函数. 0x01:url访问格式 官方文档中有 ...
- 【BZOJ-2329&2209】括号修复&括号序列 Splay
2329: [HNOI2011]括号修复 Time Limit: 40 Sec Memory Limit: 128 MBSubmit: 1007 Solved: 476[Submit][Statu ...
- Java内存泄露分析和解决方案及Windows自带查看工具
Java内存泄漏是每个Java程序员都会遇到的问题,程序在本地运行一切正常,可是布署到远端就会出现内存无限制的增长,最后系统瘫痪,那么如何最快最好的检测程序的稳定性,防止系统崩盘,作者用自已的亲身经历 ...
- spring-boot 速成(8) 集成druid+mybatis
spring-boot与druid.mybatis集成(包括pageHelper分页插件), 要添加以下几个依赖项: compile('mysql:mysql-connector-java:6.0.5 ...
- div光标
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...
- SPI中的极性CPOL和相位CPHA
详解SPI中的极性CPOL和相位CPHA SPI由于接口相对简单(只需要4根线),用途算是比较广泛,主要应用在 EEPROM,FLASH, 实时时钟,AD转换器,还有数字信号处理器和数字信号解码器之间 ...
- 微信公众号 JSSDK 提示:invalid signature
要命的invalid signature.其实腾讯的文档已经写了,只能怪我自己理解能力太差,掉了好几次坑. 签名要用到的jsapi_ticket需要保存的,2小时有效期.如果在2小时内出现问题需要删除 ...
- Android论坛
APKBUS:http://www.apkbus.com/forum.php 看雪ANDROID:http://bbs.pediy.com http://www.52pojie.cn http://w ...
- MTK65XX平台充电调试总结
MTK平台充电调试总结 摘要:调试电池的充放电管理,首先须要深入了解锂电池的电池原理和特点.充放电特性以及主要的电池安全问题.然后须要对MTK的电池管理驱动程序有深入的了解.理解电池充放电算法的基本原 ...
- C#网络编程技术SuperSocket实战项目演练
一.SuperSocket课程介绍 1.1.本期<C#网络编程技术SuperSocket实战项目演练>课程阿笨给大家带来三个基于SuperSocket通讯组件的实战项目演示实例: ● 基于 ...