Go语言实践_实现一(服务器端)对多(客户端)在线聊天室
一、目的
运用Go语言中的goroutine和通道实现一个简单的一个服务器端对多个客户端的在线聊天
软件环境:Goland,Go1.9
二、设计思路
与一对一的设计思路类似,就是加了个线程的操作。
1,服务器端声明一个map,并打开监听端口;
2,客户端打开监听端口,同时连入服务器端;
3,在客户端上给自己起一个昵称,并输出,同时启动一个线程;
4,服务器端接收一个昵称,并存入map;
5,声明一个空的字符串,并写入要群发的消息;
6,服务器端解析发送的消息(msg_str[0]的值):
- nick:使该客户端加入聊天室并广播连上服务器端的所有其他客户端;
- say:广播客户端发出的消息;
- quit:使该客户端退出,断开与服务器端的连接,并将退出消息广播给其他连上服务器端的所有其他客户端;
三、Go代码
Server端
- // one sever to more client chat room
- //This is chat sever
- package main
- import (
- "fmt"
- "net"
- "strings"
- )
- var ConnMap map[string]net.Conn = make(map[string]net.Conn) //声明一个集合
- //ConnMap := make(map[string]net.Conn)
- func main() {
- listen_socket, err := net.Listen("tcp", "127.0.0.1:8000") //打开监听接口
- if err != nil {
- fmt.Println("server start error")
- }
- defer listen_socket.Close()
- fmt.Println("server is wating ....")
- for {
- conn, err := listen_socket.Accept() //收到来自客户端发来的消息
- if err != nil {
- fmt.Println("conn fail ...")
- }
- fmt.Println(conn.RemoteAddr(), "connect successed")
- go handle(conn) //创建线程
- }
- }
- func handle(conn net.Conn) {
- for {
- data := make([]byte, ) //创建字节流 (此处同 一对一 通信)
- msg_read, err := conn.Read(data) //声明并将从客户端读取的消息赋给msg_read 和err
- if msg_read == || err != nil {
- continue
- }
- //解析协议
- msg_str := strings.Split(string(data[:msg_read]), "|") //将从客户端收到的字节流分段保存到msg_str这个数组中
- switch msg_str[] {
- case "nick": //加入聊天室
- fmt.Println(conn.RemoteAddr(), "-->", msg_str[]) //nick占在数组下标0上,客户端上写的昵称占在数组下标1上
- for k, v := range ConnMap { //遍历集合中存储的客户端消息
- if k != msg_str[] {
- v.Write([]byte("[" + msg_str[] + "]: join..."))
- }
- }
- ConnMap[msg_str[]] = conn
- case "say": //转发消息
- for k, v := range ConnMap { //k指客户端昵称 v指客户端连接服务器端后的地址
- if k != msg_str[] { //判断是不是给自己发,如果不是
- fmt.Println("Send "+msg_str[]+" to ", k) //服务器端将消息转发给集合中的每一个客户端
- v.Write([]byte("[" + msg_str[] + "]: " + msg_str[])) //给除了自己的每一个客户端发送自己之前要发送的消息
- }
- }
- case "quit": //退出
- for k, v := range ConnMap { //遍历集合中的客户端昵称
- if k != msg_str[] { //如果昵称不是自己
- v.Write([]byte("[" + msg_str[] + "]: quit")) //给除了自己的其他客户端昵称发送退出的消息,并使Write方法阻塞
- }
- }
- delete(ConnMap, msg_str[]) //退出聊天室
- }
- }
- }
Client端
- // one sever to more client chat room
- //This is chat client
- package main
- import (
- "fmt"
- "net"
- )
- var nick string = "" //声明聊天室的昵称
- func main() {
- conn, err := net.Dial("tcp", "127.0.0.1:8000") //打开监听端口
- if err != nil {
- fmt.Println("conn fail...")
- }
- defer conn.Close()
- fmt.Println("client connect server successed \n")
- //给自己取一个聊天室的昵称
- fmt.Printf("Make a nickname:")
- fmt.Scanf("%s", &nick) //输入昵称
- fmt.Println("hello : ", nick) //客户端输出
- conn.Write([]byte("nick|" + nick)) //将信息发送给服务器端
- go Handle(conn) //创建线程
- var msg string
- for {
- msg = "" //声明一个空的消息
- fmt.Scan(&msg) //输入消息
- conn.Write([]byte("say|" + nick + "|" + msg)) //三段字节流 say | 昵称 | 发送的消息
- if msg == "quit" { //如果消息为quit
- conn.Write([]byte("quit|" + nick)) //将quit字节流发送给服务器端
- break //程序结束运行
- }
- }
- }
- func Handle(conn net.Conn) {
- for {
- data := make([]byte, ) //创建一个字节流
- msg_read, err := conn.Read(data) //将读取的字节流赋值给msg_read和err
- if msg_read == || err != nil { //如果字节流为0或者有错误
- break
- }
- fmt.Println(string(data[:msg_read])) //把字节流转换成字符串
- }
- }
四、参考资料
五、总结与感受
着重关注收发消息的判定,收消息后的解包过程和开多线程;注意发消息与收消息时字节流与字符串的转换。
从初学Go到一对一再到一对多,我已经逐渐体会到使用Go语言做服务器端的方便与强大。
六、补充:还存在的问题
昨天把代码发给服务器主程大佬看,他看过后提出以下需要考虑和完善的问题,先忽略程序设计上的问题:
程序正确性无法保证
- Read可能一次性收到两个包,也可能收到半包。出现以上两种情况的时候协议解析都会出现问题。
- Write不保证一次调用时全部写完,存在短写的情况。
- ConnMap非线程安全。func handle(conn net.Conn)是多线程环境运行的。
- 连接出错及正常短开的情况未处理。
Go语言实践_实现一(服务器端)对多(客户端)在线聊天室的更多相关文章
- Go语言实践_实现一(客户端)对一(服务器端)聊天室
一.目的 使用Go语言实现一个服务器端与客户端的聊天室. 软件:Goland,Go1.9 代码仓库地址 二.思路 1,首先启动服务器端,使用listen_socket函数监听IP地址上的客户端连接: ...
- Go语言学习之9 网络协议TCP、Redis与聊天室
主要内容 1. Tcp编程2. redis使用 1. Tcp编程 (1)简介 Golang是谷歌设计开发的语言,在Golang的设计之初就把高并发的性能作为Golang的主要特性之一,也是 ...
- 实践:Backbone作前端,Django+Tastypie作后端的简单Web在线聊天室
一.界面设计: 二.数据模型设计 id 每个发言都有一个独立的id由tastypie自动生成 content 发言的内容 username 发言者 date 发言时间 三.前端制作 这里没有用到Bac ...
- memcached vs MySQL Memory engine table 速度比较_XMPP Jabber即时通讯开发实践_百度空间
memcached vs MySQL Memory engine table 速度比较_XMPP Jabber即时通讯开发实践_百度空间 memcached vs MySQL Memory engin ...
- 提高mysql memory(heap) engine内存性能的开源补丁_XMPP Jabber即时通讯开发实践_百度空间
提高mysql memory(heap) engine内存性能的开源补丁_XMPP Jabber即时通讯开发实践_百度空间 提高mysql memory(heap) engine内存性能的开源补丁
- 《程序设计语言——实践之路》【PDF】下载
程序设计语言--实践之路>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382240 内容简介 本书在美国大学已有使用了十余年,目前被欧 ...
- 《程序设计语言——实践之路(英文第三版)》【PDF】下载
<程序设计语言--实践之路(英文第三版)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382234 内容简介 <程序设计语 ...
- 《程序设计语言——实践之路【PDF】下载
<程序设计语言--实践之路[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382240 内容简介 <程序设计语言--实践之路(第3版 ...
- R语言︱H2o深度学习的一些R语言实践——H2o包
每每以为攀得众山小,可.每每又切实来到起点,大牛们,缓缓脚步来俺笔记葩分享一下吧,please~ --------------------------- R语言H2o包的几个应用案例 笔者寄语:受启发 ...
随机推荐
- bzoj4503: 两个串 bitset
目录 题目链接 题解 代码 题目链接 bzoj4503: 两个串 题解 暴一发bitset f[i][j] 表示 S[1..i] 是否有个后缀能匹配 T[1..j] 那么假设 S[i+1] 能匹配 T ...
- 2827: 千山鸟飞绝 非旋treap
国际惯例的题面:看起来很不可做的样子,我们先来整理一下题意吧.就是,维护每个点曾经拥有过的最大的两个属性值,支持把点的位置移动.我们用map对每个位置进行离散化,对每个位置建立一个平衡树.为了方便分离 ...
- Python图形编程探索系列-07-程序登录界面设计
设计任务 初步设计程序登录界面,详细分析设计步骤. 程序详细分析 基本框架设计 import tkinter as tk import tkinter.messagebox root = tk.Tk( ...
- php中对Mysql数据库的访问操作
一: PHP-MySQL 是 PHP 操作 MySQL 资料库最原始的 Extension ,PHP-MySQLi 的 i 代表 Improvement ,提更了相对进阶的功能,就 Extensio ...
- python 字符串的一些方法
总结:# split 分割 ********# strip 脱 默认脱头尾的空格 ********# replace 替换 ********# join 插入 拼接 ********# format ...
- 吐槽下intellij idea 2018.3这个版本
众所周知Springboot的@Service,@Controller,@Component,@Repository,@Configuration都是能扫描的,这些标签功能有完全一致的也有有区别的此处 ...
- LeetCode全文解锁 √
分享一波大牛整理leetcode,方便整理思路 可以点击下载
- PLSQL连接Oracle 数据库配置详解
1. 下载instantclient-basic-win32-11.2.0.1.0 (oracle官网下载地址:http://www.oracle.com/technetwork/topics/win ...
- 斯坦福CS231n学习--初识
课程主页:CS231n: Convolutional Neural Networks for Visual Recognition 关注其:Course Project主页 视频学习:云课堂 斯坦福C ...
- Always run a program in administrator mode in Windows 10
From: https://www.cnet.com/how-to/always-run-a-program-in-administrator-mode-in-windows-10/ If you'r ...