Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
catmes
EasyIM
提交
e3a6534c
E
EasyIM
项目概览
catmes
/
EasyIM
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
EasyIM
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
e3a6534c
编写于
2月 17, 2023
作者:
W
wuhanqing
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
拷贝初始源码
上级
变更
5
隐藏空白更改
内联
并排
Showing
5 changed file
with
488 addition
and
0 deletion
+488
-0
README.md
README.md
+4
-0
client.go
client.go
+202
-0
main.go
main.go
+6
-0
server.go
server.go
+143
-0
user.go
user.go
+133
-0
未找到文件。
README.md
0 → 100644
浏览文件 @
e3a6534c
## 简介
代码源于刘丹冰老师视频:
[
8小时转职Golang工程师
](
https://www.bilibili.com/video/BV1gf4y1r79E/
)
- 即时通讯系统
\ No newline at end of file
client.go
0 → 100644
浏览文件 @
e3a6534c
package
main
import
(
"flag"
"fmt"
"io"
"net"
"os"
)
type
Client
struct
{
ServerIp
string
ServerPort
int
Name
string
conn
net
.
Conn
flag
int
//当前client的模式
}
func
NewClient
(
serverIp
string
,
serverPort
int
)
*
Client
{
//创建客户端对象
client
:=
&
Client
{
ServerIp
:
serverIp
,
ServerPort
:
serverPort
,
flag
:
999
,
}
//链接server
conn
,
err
:=
net
.
Dial
(
"tcp"
,
fmt
.
Sprintf
(
"%s:%d"
,
serverIp
,
serverPort
))
if
err
!=
nil
{
fmt
.
Println
(
"net.Dial error:"
,
err
)
return
nil
}
client
.
conn
=
conn
//返回对象
return
client
}
//处理server回应的消息, 直接显示到标准输出即可
func
(
client
*
Client
)
DealResponse
()
{
//一旦client.conn有数据,就直接copy到stdout标准输出上, 永久阻塞监听
io
.
Copy
(
os
.
Stdout
,
client
.
conn
)
}
func
(
client
*
Client
)
menu
()
bool
{
var
flag
int
fmt
.
Println
(
"1.公聊模式"
)
fmt
.
Println
(
"2.私聊模式"
)
fmt
.
Println
(
"3.更新用户名"
)
fmt
.
Println
(
"0.退出"
)
fmt
.
Scanln
(
&
flag
)
if
flag
>=
0
&&
flag
<=
3
{
client
.
flag
=
flag
return
true
}
else
{
fmt
.
Println
(
">>>>请输入合法范围内的数字<<<<"
)
return
false
}
}
//查询在线用户
func
(
client
*
Client
)
SelectUsers
()
{
sendMsg
:=
"who
\n
"
_
,
err
:=
client
.
conn
.
Write
([]
byte
(
sendMsg
))
if
err
!=
nil
{
fmt
.
Println
(
"conn Write err:"
,
err
)
return
}
}
//私聊模式
func
(
client
*
Client
)
PrivateChat
()
{
var
remoteName
string
var
chatMsg
string
client
.
SelectUsers
()
fmt
.
Println
(
">>>>请输入聊天对象[用户名], exit退出:"
)
fmt
.
Scanln
(
&
remoteName
)
for
remoteName
!=
"exit"
{
fmt
.
Println
(
">>>>请输入消息内容, exit退出:"
)
fmt
.
Scanln
(
&
chatMsg
)
for
chatMsg
!=
"exit"
{
//消息不为空则发送
if
len
(
chatMsg
)
!=
0
{
sendMsg
:=
"to|"
+
remoteName
+
"|"
+
chatMsg
+
"
\n\n
"
_
,
err
:=
client
.
conn
.
Write
([]
byte
(
sendMsg
))
if
err
!=
nil
{
fmt
.
Println
(
"conn Write err:"
,
err
)
break
}
}
chatMsg
=
""
fmt
.
Println
(
">>>>请输入消息内容, exit退出:"
)
fmt
.
Scanln
(
&
chatMsg
)
}
client
.
SelectUsers
()
fmt
.
Println
(
">>>>请输入聊天对象[用户名], exit退出:"
)
fmt
.
Scanln
(
&
remoteName
)
}
}
func
(
client
*
Client
)
PublicChat
()
{
//提示用户输入消息
var
chatMsg
string
fmt
.
Println
(
">>>>请输入聊天内容,exit退出."
)
fmt
.
Scanln
(
&
chatMsg
)
for
chatMsg
!=
"exit"
{
//发给服务器
//消息不为空则发送
if
len
(
chatMsg
)
!=
0
{
sendMsg
:=
chatMsg
+
"
\n
"
_
,
err
:=
client
.
conn
.
Write
([]
byte
(
sendMsg
))
if
err
!=
nil
{
fmt
.
Println
(
"conn Write err:"
,
err
)
break
}
}
chatMsg
=
""
fmt
.
Println
(
">>>>请输入聊天内容,exit退出."
)
fmt
.
Scanln
(
&
chatMsg
)
}
}
func
(
client
*
Client
)
UpdateName
()
bool
{
fmt
.
Println
(
">>>>请输入用户名:"
)
fmt
.
Scanln
(
&
client
.
Name
)
sendMsg
:=
"rename|"
+
client
.
Name
+
"
\n
"
_
,
err
:=
client
.
conn
.
Write
([]
byte
(
sendMsg
))
if
err
!=
nil
{
fmt
.
Println
(
"conn.Write err:"
,
err
)
return
false
}
return
true
}
func
(
client
*
Client
)
Run
()
{
for
client
.
flag
!=
0
{
for
client
.
menu
()
!=
true
{
}
//根据不同的模式处理不同的业务
switch
client
.
flag
{
case
1
:
//公聊模式
client
.
PublicChat
()
break
case
2
:
//私聊模式
client
.
PrivateChat
()
break
case
3
:
//更新用户名
client
.
UpdateName
()
break
}
}
}
var
serverIp
string
var
serverPort
int
//./client -ip 127.0.0.1 -port 8888
func
init
()
{
flag
.
StringVar
(
&
serverIp
,
"ip"
,
"127.0.0.1"
,
"设置服务器IP地址(默认是127.0.0.1)"
)
flag
.
IntVar
(
&
serverPort
,
"port"
,
8888
,
"设置服务器端口(默认是8888)"
)
}
func
main
()
{
//命令行解析
flag
.
Parse
()
client
:=
NewClient
(
serverIp
,
serverPort
)
if
client
==
nil
{
fmt
.
Println
(
">>>>> 链接服务器失败..."
)
return
}
//单独开启一个goroutine去处理server的回执消息
go
client
.
DealResponse
()
fmt
.
Println
(
">>>>>链接服务器成功..."
)
//启动客户端的业务
client
.
Run
()
}
main.go
0 → 100644
浏览文件 @
e3a6534c
package
main
func
main
()
{
server
:=
NewServer
(
"127.0.0.1"
,
8888
)
server
.
Start
()
}
server.go
0 → 100644
浏览文件 @
e3a6534c
package
main
import
(
"fmt"
"io"
"net"
"sync"
"time"
)
type
Server
struct
{
Ip
string
Port
int
//在线用户的列表
OnlineMap
map
[
string
]
*
User
mapLock
sync
.
RWMutex
//消息广播的channel
Message
chan
string
}
//创建一个server的接口
func
NewServer
(
ip
string
,
port
int
)
*
Server
{
server
:=
&
Server
{
Ip
:
ip
,
Port
:
port
,
OnlineMap
:
make
(
map
[
string
]
*
User
),
Message
:
make
(
chan
string
),
}
return
server
}
//监听Message广播消息channel的goroutine,一旦有消息就发送给全部的在线User
func
(
this
*
Server
)
ListenMessager
()
{
for
{
msg
:=
<-
this
.
Message
//将msg发送给全部的在线User
this
.
mapLock
.
Lock
()
for
_
,
cli
:=
range
this
.
OnlineMap
{
cli
.
C
<-
msg
}
this
.
mapLock
.
Unlock
()
}
}
//广播消息的方法
func
(
this
*
Server
)
BroadCast
(
user
*
User
,
msg
string
)
{
sendMsg
:=
"["
+
user
.
Addr
+
"]"
+
user
.
Name
+
":"
+
msg
this
.
Message
<-
sendMsg
}
func
(
this
*
Server
)
Handler
(
conn
net
.
Conn
)
{
//...当前链接的业务
//fmt.Println("链接建立成功")
user
:=
NewUser
(
conn
,
this
)
user
.
Online
()
//监听用户是否活跃的channel
isLive
:=
make
(
chan
bool
)
//接受客户端发送的消息
go
func
()
{
buf
:=
make
([]
byte
,
4096
)
for
{
n
,
err
:=
conn
.
Read
(
buf
)
if
n
==
0
{
user
.
Offline
()
return
}
if
err
!=
nil
&&
err
!=
io
.
EOF
{
fmt
.
Println
(
"Conn Read err:"
,
err
)
return
}
//提取用户的消息(去除'\n')
msg
:=
string
(
buf
[
:
n
-
1
])
//用户针对msg进行消息处理
user
.
DoMessage
(
msg
)
//用户的任意消息,代表当前用户是一个活跃的
isLive
<-
true
}
}()
//当前handler阻塞
for
{
select
{
case
<-
isLive
:
//当前用户是活跃的,应该重置定时器
//不做任何事情,为了激活select,更新下面的定时器
case
<-
time
.
After
(
time
.
Second
*
300
)
:
//已经超时
//将当前的User强制的关闭
user
.
SendMsg
(
"你被踢了"
)
//销毁用的资源
close
(
user
.
C
)
//关闭连接
conn
.
Close
()
//退出当前Handler
return
//runtime.Goexit()
}
}
}
//启动服务器的接口
func
(
this
*
Server
)
Start
()
{
//socket listen
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
defer
listener
.
Close
()
//启动监听Message的goroutine
go
this
.
ListenMessager
()
for
{
//accept
conn
,
err
:=
listener
.
Accept
()
if
err
!=
nil
{
fmt
.
Println
(
"listener accept err:"
,
err
)
continue
}
//do handler
go
this
.
Handler
(
conn
)
}
}
user.go
0 → 100644
浏览文件 @
e3a6534c
package
main
import
(
"net"
"strings"
)
type
User
struct
{
Name
string
Addr
string
C
chan
string
conn
net
.
Conn
server
*
Server
}
//创建一个用户的API
func
NewUser
(
conn
net
.
Conn
,
server
*
Server
)
*
User
{
userAddr
:=
conn
.
RemoteAddr
()
.
String
()
user
:=
&
User
{
Name
:
userAddr
,
Addr
:
userAddr
,
C
:
make
(
chan
string
),
conn
:
conn
,
server
:
server
,
}
//启动监听当前user channel消息的goroutine
go
user
.
ListenMessage
()
return
user
}
//用户的上线业务
func
(
this
*
User
)
Online
()
{
//用户上线,将用户加入到onlineMap中
this
.
server
.
mapLock
.
Lock
()
this
.
server
.
OnlineMap
[
this
.
Name
]
=
this
this
.
server
.
mapLock
.
Unlock
()
//广播当前用户上线消息
this
.
server
.
BroadCast
(
this
,
"已上线"
)
}
//用户的下线业务
func
(
this
*
User
)
Offline
()
{
//用户下线,将用户从onlineMap中删除
this
.
server
.
mapLock
.
Lock
()
delete
(
this
.
server
.
OnlineMap
,
this
.
Name
)
this
.
server
.
mapLock
.
Unlock
()
//广播当前用户上线消息
this
.
server
.
BroadCast
(
this
,
"下线"
)
}
//给当前User对应的客户端发送消息
func
(
this
*
User
)
SendMsg
(
msg
string
)
{
this
.
conn
.
Write
([]
byte
(
msg
))
}
//用户处理消息的业务
func
(
this
*
User
)
DoMessage
(
msg
string
)
{
if
msg
==
"who"
{
//查询当前在线用户都有哪些
this
.
server
.
mapLock
.
Lock
()
for
_
,
user
:=
range
this
.
server
.
OnlineMap
{
onlineMsg
:=
"["
+
user
.
Addr
+
"]"
+
user
.
Name
+
":"
+
"在线...
\n
"
this
.
SendMsg
(
onlineMsg
)
}
this
.
server
.
mapLock
.
Unlock
()
}
else
if
len
(
msg
)
>
7
&&
msg
[
:
7
]
==
"rename|"
{
//消息格式: rename|张三
newName
:=
strings
.
Split
(
msg
,
"|"
)[
1
]
//判断name是否存在
_
,
ok
:=
this
.
server
.
OnlineMap
[
newName
]
if
ok
{
this
.
SendMsg
(
"当前用户名被使用
\n
"
)
}
else
{
this
.
server
.
mapLock
.
Lock
()
delete
(
this
.
server
.
OnlineMap
,
this
.
Name
)
this
.
server
.
OnlineMap
[
newName
]
=
this
this
.
server
.
mapLock
.
Unlock
()
this
.
Name
=
newName
this
.
SendMsg
(
"您已经更新用户名:"
+
this
.
Name
+
"
\n
"
)
}
}
else
if
len
(
msg
)
>
4
&&
msg
[
:
3
]
==
"to|"
{
//消息格式: to|张三|消息内容
//1 获取对方的用户名
remoteName
:=
strings
.
Split
(
msg
,
"|"
)[
1
]
if
remoteName
==
""
{
this
.
SendMsg
(
"消息格式不正确,请使用
\"
to|张三|你好啊
\"
格式。
\n
"
)
return
}
//2 根据用户名 得到对方User对象
remoteUser
,
ok
:=
this
.
server
.
OnlineMap
[
remoteName
]
if
!
ok
{
this
.
SendMsg
(
"该用户名不不存在
\n
"
)
return
}
//3 获取消息内容,通过对方的User对象将消息内容发送过去
content
:=
strings
.
Split
(
msg
,
"|"
)[
2
]
if
content
==
""
{
this
.
SendMsg
(
"无消息内容,请重发
\n
"
)
return
}
remoteUser
.
SendMsg
(
this
.
Name
+
"对您说:"
+
content
)
}
else
{
this
.
server
.
BroadCast
(
this
,
msg
)
}
}
//监听当前User channel的 方法,一旦有消息,就直接发送给对端客户端
func
(
this
*
User
)
ListenMessage
()
{
for
{
msg
:=
<-
this
.
C
this
.
conn
.
Write
([]
byte
(
msg
+
"
\n
"
))
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录