这个项目实现的内容有如下:

1.公聊
2.私聊
3.修改用户名
4.超时强踢

这个项目的缺点:

1.用的tcp协议,但没有考虑到网络传输中丢包的问题
2.没有用json格式传输数据
3.没有用数据库,只是实时的传输信息,甚至没有登录校验

这个项目的分层结构

1.客户端 client.go
2.服务端 server.go main.go user.go

源代码如下

main.go

package mainfunc main() {server := NewServer("0.0.0.0", 8888)server.Start()
}

server.go

package mainimport ("fmt""io""net""runtime""sync""time"
)type Server struct {Ip   stringPort int//user online listOnlineMap map[string]*UserMapLock   sync.RWMutex//the channel for the message broadcastMessageChan chan string
}func NewServer(ip string, port int) *Server {server := &Server{Ip:          ip,Port:        port,OnlineMap:   make(map[string]*User),MessageChan: make(chan string),}return server
}func (this *Server) Handler(conn net.Conn) {user := NewUser(conn, this)//user online,join user to OnlineMap// this.MapLock.Lock()// this.OnlineMap[user.Name] = user// this.MapLock.Unlock()//Broading the current user online message// this.BroadCast(user, "I am online")user.Online()//the channel for whether the user is activeisLive := make(chan bool)//accept the messages sent from the clientgo func() {buf := make([]byte, 4096)for {n, err := conn.Read(buf)if n == 0 {user.Outline()return}if err != nil && err != io.EOF {fmt.Println("Coon Read err:", err)return}//get rid of '\n'msg := string(buf[:n-1])user.DoMessage(msg)//user send any msg represent that is activeisLive <- true}}()//Blocking current Hander()for {select {case <-isLive:// this user is active, and do anything for reset timercase <-time.After(time.Second * 300)://time out, this user inactiveuser.SendMessage("You were forced back!")//destroy the resource for userclose(user.stringChan)//close the linkconn.Close()//drop out the goruntineruntime.Goexit()}}}func (this *Server) BroadCast(user *User, msg string) {sendMsg := "[" + user.Addr + "]" + user.Name + ":" + msgthis.MessageChan <- sendMsg
}//the goroutine of channel,listen for the broadcast message
func (this *Server) ListenMessager() {for {msg := <-this.MessageChan//send the msg for all onlined userthis.MapLock.Lock()for _, cli := range this.OnlineMap {cli.stringChan <- msg}this.MapLock.Unlock()}
}//the function that starts the server
func (this *Server) Start() {listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", this.Ip, this.Port))if err != nil {fmt.Println("net.Listen err:", err)return}//close listen socket  , run at the end of the functiondefer listener.Close()//turn on one goroutine for listening Messagego this.ListenMessager()for {conn, err := listener.Accept()if err != nil {fmt.Println("listnner accept err:", err)continue}//do handlergo this.Handler(conn)}}

user.go

package mainimport ("fmt""net""strings"
)type User struct {Name       stringAddr       stringstringChan chan stringconn       net.Conn//for users to associate with the serviceserver *Server
}//Create a user
func NewUser(conn net.Conn, server *Server) *User {userAddr := conn.RemoteAddr().String()user := &User{Name:       userAddr,Addr:       userAddr,stringChan: make(chan string),conn:       conn,server:     server,}//Start the listening user channel message goroutinego user.ListenMessage()return user
}func (this *User) ListenMessage() {for {msg := <-this.stringChanthis.conn.Write([]byte(msg + "\n"))}
}//user online service
func (this *User) Online() {//join in user mapthis.server.MapLock.Lock()this.server.OnlineMap[this.Name] = thisthis.server.MapLock.Unlock()this.server.BroadCast(this, "online")
}//user outline service
func (this *User) Outline() {//delete from mapthis.server.MapLock.Lock()delete(this.server.OnlineMap, this.Name)this.server.MapLock.Unlock()this.server.BroadCast(this, "outline")
}//
func (this *User) SendMessage(msg string) {this.conn.Write([]byte(msg))
}//user broadcast service
func (this *User) DoMessage(msg string) {//look online user listif msg == "who" {this.server.MapLock.Lock()for _, user := range this.server.OnlineMap {onlineMsg := "[" + user.Addr + "]" + user.Name + ":" + "online...\n"this.SendMessage(onlineMsg)}this.server.MapLock.Unlock()//change user name} else if len(msg) > 7 && msg[:7] == "rename|" {newName := strings.Split(msg, "|")[1]//judge whether the name exsits_, ok := this.server.OnlineMap[newName]if ok {this.SendMessage("this name was used\n")} else {this.server.MapLock.Lock()delete(this.server.OnlineMap, this.Name)this.server.OnlineMap[newName] = thisthis.server.MapLock.Unlock()this.Name = newNamethis.SendMessage("you have changed the user name:" + this.Name + "\n")}} else if len(msg) > 4 && msg[:3] == "to|" {//msg format to|name|sayword//1.get the user name that username who recevied the messagereceverName := strings.Split(msg, "|")[1]if receverName == "" {this.SendMessage("msg format \"to|name|sayword\"\n")return}//get the user object by recevied namereceverUser, ok := this.server.OnlineMap[receverName]if !ok {this.SendMessage(fmt.Sprintf("%v the user does not exist\n", receverUser))return}//get message content and send to recevercontent := strings.Split(msg, "|")[2]if content == "" {this.SendMessage("No message, please send again")return}receverUser.SendMessage("[" + this.Name + "]" + "send:" + content)} else {this.server.BroadCast(this, msg)}}

client.go

package mainimport ("flag""fmt""io""net""os"
)type Client struct {ServerIP   stringServerPort intName       stringCoon       net.Connflag       int
}func NewClient(serverIp string, serverPort int) *Client {client := &Client{ServerIP:   serverIp,ServerPort: serverPort,flag:       999,}conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", serverIp, serverPort))if err != nil {fmt.Println("net.Dial err:", err)return nil}client.Coon = connreturn client}
func (this *Client) menu() bool {var flag intfmt.Println("1.public chat mode")fmt.Println("2.private chat mode")fmt.Println("3.Change user name")fmt.Println("0.quit")fmt.Scanln(&flag)if flag >= 0 && flag <= 3 {this.flag = flagreturn true} else {fmt.Println(">>>>>Please enter number within the legal range<<<<<<<<<<<")return false}
}func (this *Client) Run() {for this.flag != 0 {for this.menu() != true {}switch this.flag {case 1:this.PublicChat()breakcase 2:this.PrivateChat()breakcase 3:this.UpdateName()break}}}func (this *Client) SelectUser() {sendMsg := "who\n"_, err := this.Coon.Write([]byte(sendMsg))if err != nil {fmt.Println("conn Write err:", err)return}
}func (this *Client) PrivateChat() {var remoteName stringvar chatMsg string//who all user firstthis.SelectUser()fmt.Println(">>>>>Please input username,and qiut when input exit : ")fmt.Scanln(&remoteName)for remoteName != "exit" {fmt.Println(">>>>please input message,and qiut when input exit :")fmt.Scanln(&chatMsg)for chatMsg != "exit" {if len(chatMsg) != 0 {sendMsg := "to|" + remoteName + "|" + chatMsg + "\n\n"_, err := this.Coon.Write([]byte(sendMsg))if err != nil {fmt.Println("Coon Write err:", err)break}}chatMsg = ""fmt.Println(">>>>please input message,and qiut when input exit :")fmt.Scanln(&chatMsg)}this.SelectUser()remoteName = ""fmt.Println(">>>>>Please input username,and qiut when input exit : ")fmt.Scanln(&remoteName)}}func (this *Client) PublicChat() {var chatMsg stringfmt.Println(">>>>>>please enter the chat connet,qiut when enter exit")fmt.Scanln(&chatMsg)for chatMsg != "exit" {//send msg to serverif len(chatMsg) != 0 {sendMsg := chatMsg + "\n"_, err := this.Coon.Write([]byte(sendMsg))if err != nil {fmt.Println("coon Write err:", err)break}}chatMsg = ""fmt.Println(">>>>>>please enter the chat connet,qiut when enter exit")fmt.Scanln(&chatMsg)}
}func (this *Client) UpdateName() bool {fmt.Println(">>>please input user name:")fmt.Scanln(&this.Name)sendMsg := "rename|" + this.Name + "\n"_, err := this.Coon.Write([]byte(sendMsg))if err != nil {fmt.Println("conn.Write err:", err)return false}return true
}//deal server send msg, and show to stdout
func (this *Client) DealResponse() {// for {// buf := make([]byte ,1024)// this.Coon.Read(buf)// fmt.Println(buf)// }io.Copy(os.Stdout, this.Coon)
}var serverIp string
var serverPort int//run before the main func
func init() {//./client -ip 127.0.0.1 -port 8888flag.StringVar(&serverIp, "ip", "159.75.91.76", "set server ip(default is 159.75.91.76)")flag.IntVar(&serverPort, "port", 8888, "set up server port(default is 8888)")
}func main() {flag.Parse()client := NewClient(serverIp, serverPort)if client == nil {fmt.Println(">>>>>>>>>>>>>>>>>>>>>Link server failed.....")return}//setup goruntine to deal with service msggo client.DealResponse()fmt.Println(">>>>>>>>>>>>>>>>>>>>>Link server success............")//Start the client business//select {}client.Run()
}

关于编译

关于运行

客户端

./client -ip 106.55.36.146 -port 8888
ip 后面的地址就是你服务器运行的地址,端口我server写死了8888

服务端

运行后就一直在这里阻塞等待客户端来连接了,你可以多加点提示

后续

我这个是学习了丹冰的8小时入门go写的cs即时聊天,我想学习完redis再写一个可以离线聊天的,需要登录的、以及考虑到丢包问题的一个版本,再学习丹冰的zinx框架再搞一个版本

刘丹冰Aceld的主页 https://space.bilibili.com/373073810/

加入go行列的一个敲门砖吗----小玩意cs多人即时聊天,没有用数据库的,没有用框架的相关推荐

  1. 微软为华为定制了一个“烂笔头小冰”,让人想起了老罗的“闪念胶囊”

    微软全球执行副总裁--沈向洋 在刚刚过去的第六代小冰发布会上,微软和华为走到了一起. 小冰产品总监徐翔宣布,微软小冰将于今日上线华为手机,所有升级至智能助手 8.2 版本及以上的华为手机(包括华为和荣 ...

  2. 基于阿里云用C/C++做了一个http协议与TCP协议的web聊天室的服务器——《干饭聊天室》

    基于阿里云用C/C++做了一个http协议与TCP协议的web聊天室的服务器--<干饭聊天室> 在这里首先感谢前端小伙伴飞鸟 前端技术请看一款基于React.C++,使用TCP/HTTP协 ...

  3. php 利用redis写一个聊天室,Redis实现多人多聊天室功能

    本文为大家分享了Redis支持多人多聊天室功能的设计代码,供大家参考,具体内容如下 设计原理 左边的一个数据域,代表两个聊天室,聊天室id分别是827,729 在聊天室827里,有2个人,分别是jas ...

  4. C语言学习之编写一个C程序,运行时输人abc三个值,输出其中值最大者。

    编写一个C程序,运行时输人abc三个值,输出其中值最大者. #include <stdio.h> void main(){int a,b,c,max;printf("请输入三个数 ...

  5. postgresql参数化查询_一个能融会贯通PostgreSQL监控的人,大概率是高手

    有一些同学觉得监控无非是针对CPU.内存 .磁盘进行一些简单的监控,其实不仅仅如此,监控涵盖了众多知识的融合,能融会贯通PostgreSQL监控的人,大概率是PostgreSQL高手. POSTGRE ...

  6. 微信上了一个新功能,吐槽的人有点多

    ‍ ‍4 月 12 日,微信官方宣布,微信农场开始营业了. 有微信养猪的 还有微信养狗的 有的人哺乳动物已经不能满足他的需求,他要开始养蟑螂了. 状态怎么开启呢? 1.点击状态 2.选择美滋滋 3.选 ...

  7. 实践:《从头到脚撸一个多人视频聊天 — 前端 WebRTC 实战(一)》

    2019独角兽企业重金招聘Python工程师标准>>> 请先阅读原文,链接:从头到脚撸一个多人视频聊天 - 前端 WebRTC 实战(一),本文只涉及实践过程中的问题 1.video ...

  8. 一个叫花子的故事(十个人看完十个人开悟)

    一个叫花子的故事(十个人看完十个人开悟) 从前有一个叫花子,每天出门乞讨,他很想过正常人的生活,于是他把乞讨粮食积攒起来.可是他积攒了好多年,他的粮仓还是只有一点米. 一天夜里,他悄悄地躲在角落,果然 ...

  9. 查看postgresql 磁盘占用_一个能融会贯通PostgreSQL监控的人,大概率是高手

    有一些同学觉得监控无非是针对CPU.内存 .磁盘进行一些简单的监控,其实不仅仅如此,监控涵盖了众多知识的融合,能融会贯通PostgreSQL监控的人,大概率是PostgreSQL高手. POSTGRE ...

  10. php和ajax实现聊天功能,怎么在PHP项目中使用jquery与ajax实现一个即时聊天功能

    怎么在PHP项目中使用jquery与ajax实现一个即时聊天功能 发布时间:2020-12-19 16:19:01 来源:亿速云 阅读:81 作者:Leah 本篇文章给大家分享的是有关怎么在PHP项目 ...

最新文章

  1. MPB:中国地大侯卫国组-​ 针对热泉原位培养矿物的低质量DNA提取方法
  2. 2010最后一篇:使用PyQt4开发的一个开源小程序QaoBa
  3. 后台编写HttpWebRequest的POST请求,必须注意的一个小细节
  4. boost::python::dict相关的测试程序
  5. python最优分箱计算iv值_GitHub - zhaoxingfeng/WOE: Weight of Evidence,基于iv值最大思想求最优分箱...
  6. Google面试题——蓄水问题
  7. SQL查询成绩前3的student
  8. mysql动态函数库_mysql自定义函数与动态查询
  9. Unity移动---朝向并移动到鼠标点击位置
  10. 在Android上加载具有AI危害检测的TensorFlow模型
  11. 后台管理页面通过点击左侧导航栏的菜单项实现右边内容的改变
  12. Photoshop中的渐变工具
  13. ios CAShapeLayer
  14. Tensorflow pb模型转uff模型方法及遇到KeyError20和expected Const问题解决
  15. Xcode11没有iOS14的真机包,Xcode12没有iOS8的真机包,用到的时候网上都要积分收费,免费的不好找,因此只有自己保存一份使用到的时候才不慌
  16. C#计算工资(派生类)
  17. [JVM]了断局: 堆外内存无法 [ -XX:MaxDirectMemorySize ] 限制
  18. 计算机一区会议_参加第一次技术会议的建议和技巧
  19. VMware16安装CentOS 7.9操作系统(Minimal版)
  20. 数据分析之numpy实例

热门文章

  1. 数字图像字符识别——数字识别
  2. 视觉SLAM摄影测量立体计算机视觉的关系
  3. C++设计模式:UML工具及常用符号
  4. Electron技术架构
  5. Linux切换jdk版本
  6. 东芝Toshiba DP-2210 打印机驱动
  7. 【Tomcat】修改密码
  8. 优化数据库的八种经典方式
  9. win10系统恢复win7的照片查看器
  10. PS下载 PS2021中文版下载 Photoshop2021中文版22.5.1 ACR14.0最新