




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第300行代码实现go语言即时通讯聊天室学了2年Java,因为工作原因需要转Golang,3天时间学习了下go的基本语法,做这样一个聊天室小项目来巩固串联一下语法。
实现的功能:公聊,私聊,修改用户名
只用到了四个类:
main.go:用来启动服务器server.go:服务器相关代码client.go:客户端相关代码,用户可以直接操作的可视化界面user.go:用户类,用来封装用户的业务逻辑
架构图
server.go
packagemain
import(
"fmt"
"io"
"net"
"sync"
"time"
typeServerstruct{
Ipstring
Portint
//在线用户列表
OnlineMapmap[string]*User
mapLocksync.RWMutex
//消息广播的Channel
Messagechanstring
funcNewServer(ipstring,portint)*Server{
server:=Server{
Ip:ip,
Port:port,
OnlineMap:make(map[string]*User),
Message:make(chanstring),
returnserver
func(s*Server)Handler(connnet.Conn){
//业务逻辑
//fmt.Println("链接建立成功")
user:=NewUser(conn,s)
user.Online()
//监听用户是否活跃
isLive:=make(chanbool)
gofunc(){
buf:=make([]byte,4096)
for{
n,error:=conn.Read(buf)
ifn==0{
user.Offline()
return
iferror!=nilerror!=io.EOF{
fmt.Println("readerror")
msg:=string(buf[:n-1])
user.DoMessage(msg)
//表示用户活跃
isLive-true
for{
select{
case-isLive:
//当前用户活跃,不做任何时,激活select,重置定时器
case-time.After(time.Second*300):
//超时,将user强制关闭
user.SendMsg("你被踢了")
close(user.C)
conn.Close()
return
func(s*Server)ListenMessager(){
for{
msg:=-s.Message
s.mapLock.Lock()
for_,user:=ranges.OnlineMap{
user.C-msg
s.mapLock.Unlock()
func(s*Server)BroadCast(user*User,msgstring){
sendMsg:="["+user.Addr+"]"+user.Name+":"+msg
s.Message-sendMsg
func(s*Server)Start(){
listener,error:=net.Listen("tcp",fmt.Sprintf("%s:%d",s.Ip,s.Port))
iferror!=nil{
fmt.Println("listenererror...")
return
deferlistener.Close()
gos.ListenMessager()
for{
conn,error:=listener.Accept()
iferror!=nil{
fmt.Println("accepterror...")
continue
gos.Handler(conn)
client.go
packagemain
import(
"flag"
"fmt"
"io"
"net"
"os"
typeClientstruct{
ServerIpstring
ServerPortint
Namestring
connnet.Conn
flagint
funcNewClient(serverIpstring,serverPortint)*Client{
client:=Client{
ServerIp:serverIp,
ServerPort:serverPort,
flag:9999,
conn,error:=net.Dial("tcp",fmt.Sprintf("%s:%d",serverIp,serverPort))
iferror!=nil{
fmt.Println("netdialerror...")
returnnil
client.conn=conn
returnclient
func(c*Client)menu()bool{
varflagint
fmt.Println("1.公聊模式")
fmt.Println("2.私聊模式")
fmt.Println("3.修改用户名")
fmt.Println("0.退出")
fmt.Scanln(flag)
ifflag=0flag=3{
c.flag=flag
returntrue
}else{
fmt.Println("请输入合法数字")
returnfalse
//修改用户名
func(c*Client)UpdateName()bool{
fmt.Println("请输入用户名")
fmt.Scanln(c.Name)
sendMsg:="rename|"+c.Name+"\n"
_,error:=c.conn.Write([]byte(sendMsg))
iferror!=nil{
fmt.Println("conn.writeerror...")
returnfalse
returntrue
func(c*Client)PublicChat(){
varchatMsgstring
fmt.Println("请输入聊天内容,输入exit退出")
fmt.Scanln(chatMsg)
forchatMsg!="exit"{
iflen(chatMsg)!=0{
msg:=chatMsg+"\n"
_,error:=c.conn.Write([]byte(msg))
iferror!=nil{
fmt.Println("conn.Writeerror....")
break
chatMsg=""
fmt.Println("请输入聊天内容,输入exit退出")
fmt.Scanln(chatMsg)
func(c*Client)PrivateChat(){
varremoteUserstring
varchatMsgstring
c.SelectUsers()
fmt.Println("请输入聊天对象的用户名,输入exit退出")
fmt.Scanln(remoteUser)
forremoteUser!="exit"{
fmt.Println("请输入聊天内容,输入exit退出")
fmt.Scanln(chatMsg)
forchatMsg!="exit"{
iflen(chatMsg)!=0{
msg:="to|"+remoteUser+"|"+chatMsg+"\n\n"
_,error:=c.conn.Write([]byte(msg))
iferror!=nil{
fmt.Println("conn.Writeerror....")
break
chatMsg=""
fmt.Println("请输入聊天内容,输入exit退出")
fmt.Scanln(chatMsg)
c.SelectUsers()
remoteUser=""
fmt.Println("请输入聊天对象的用户名,输入exit退出")
fmt.Scanln(remoteUser)
//查询在线用户
func(c*Client)SelectUsers(){
sendMsg:="who\n"
_,error:=c.conn.Write([]byte(sendMsg))
iferror!=nil{
fmt.Println("conn.Writeerror....")
return
//处理server返回的消息
func(c*Client)DealResponse(){
io.Copy(os.Stdout,c.conn)
func(c*Client)Run(){
forc.flag!=0{
forc.menu()!=true{
switchc.flag{
case1:
//公聊
c.PublicChat()
case2:
//私聊
c.PrivateChat()
case3:
//修改用户名
c.UpdateName()
varserverIpstring
varserverPortint
funcinit(){
flag.StringVar(serverIp,"ip","","设置服务器IP地址(默认为)")
flag.IntVar(serverPort,"port",8888,"设置服务器端口(默认为8888)")
funcmain(){
flag.Parse()
client:=NewClient(serverIp,serverPort)
ifclient==nil{
fmt.Println("链接服务器失败")
return
goclient.DealResponse()
fmt.Println("链接服务器成功")
client.Run()
user.go
packagemain
import(
"net"
"strings"
typeUserstruct{
Namestring
Addrstring
Cchanstring
connnet.Conn
server*Server
funcNewUser(connnet.Conn,server*Server)*User{
userAddr:=conn.RemoteAddr().String()
user:=User{
Name:userAddr,
Addr:userAddr,
C:make(chanstring),
conn:conn,
server:server,
gouser.ListenMessage()
returnuser
//用户上线
func(u*User)Online(){
u.server.mapLock.Lock()
u.server.OnlineMap[u.Name]=u
u.server.mapLock.Unlock()
u.server.BroadCast(u,"上线")
//用户下线
func(u*User)Offline(){
u.server.mapLock.Lock()
delete(u.server.OnlineMap,u.Name)
u.server.mapLock.Unlock()
u.server.BroadCast(u,"下线")
//给当前user的客户端发送消息
func(u*User)SendMsg(msgstring){
u.conn.Write([]byte(msg))
//处理消息
func(u*User)DoMessage(msgstring){
ifmsg=="who"{
//查询当前在线用户
u.server.mapLock.Lock()
for_,user:=rangeu.server.OnlineMap{
onlineMsg:="["+user.Addr+"]"+user.Name+":在线...\n"
u.SendMsg(onlineMsg)
u.server.mapLock.Unlock()
}elseiflen(msg)7msg[:7]=="rename|"{
//修改用户名rename|xxx
newName:=strings.Split(msg,"|")[1]
//判断名字是否已经存在
_,ok:=u.server.OnlineMap[newName]
ifok{
u.SendMsg("用户名已存在\n")
}else{
u.server.mapLock.Lock()
delete(u.server.OnlineMap,u.Name)
u.server.OnlineMap[newName]=u
u.server.mapLock.Unlock()
u.Name=newName
u.SendMsg
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 纺织品设计中的共享经济理念试题及答案
- 垃圾合同协议书
- 村庄保洁合同协议书
- 院子继承合同协议书
- 合同书股份协议书
- 水库合同协议书
- 供货协议书合同范本
- 雇佣送货司机合同协议书
- 图书代销合同协议书
- 储蓄合同协议书
- 《新能源材料与器件》教学课件-04电化学能源材料与器件
- 2022年同等学力人员申请硕士学位日语水平统一考试真题
- 游泳池设备操作培训课件
- 城轨道交通人因事故分析及评价研究
- T∕CADERM 2002-2018 胸痛中心(基层版)建设与评估标准
- ZYWL-4000型履带式钻机
- 脑梗死标准病历、病程记录、出院记录模板
- 50MPa路面抗折混凝土配合比
- 油阀座加工工艺与夹具设计说明
- 护士压力与情绪管理PPT课件
- 刘鸿文材料力学习题集(全且附有标准答案)
评论
0/150
提交评论