Go语言项目 -- 海量用户即时通讯系统
1. 参考博客
https://blog.csdn.net/A_art_xiang/article/details/137236072
2. 分析
需求分析
- 用户注册登录
- 显示在线用户列表
- 群聊
- 点对点聊天
- 离线留言
思路分析
完成链接创建,数据发送功能
发送接受消息流程示意图

server端
package main
import (
"fmt"
"net"
)
func process(conn net.Conn){
// 延时关闭conn
defer conn.Close()
buffer := make([]byte,8096)
// 循环读取客户端发送的信息
for {
n,err := conn.Read(buffer[:4])
if n != 4 || err != nil{
fmt.Println("读取错误",err)
return
}
fmt.Println("读到",buffer[:4])
}
}
func main(){
fmt.Println("服务器在8889端口监听")
listener,err := net.Listen("tcp","127.0.0.1:8889")
defer listener.Close()
if err != nil{
fmt.Println("监听错误",err)
return
}
// 监听成功等待客户端链接服务器
for {
fmt.Println("等待链接")
conn,err := listener.Accept()
if err != nil{
fmt.Println("链接出错",err)
}
// 链接成功,启动协程和客户端保持通信
go process(conn)
}
}
client端
main.go
package main
import (
"fmt"
)
var userId int
var password string
func main() {
// 接受用户的选择
var key int
for {
fmt.Println("登录多人聊天")
fmt.Println("1 登录")
fmt.Println("2 注册")
fmt.Println("3 退出")
fmt.Println("请选择(1-3)")
fmt.Scanf("%d\n",&key)
switch key {
case 1:
fmt.Println("登录聊天室")
// 登录
fmt.Println("请输入用户ID:")
fmt.Scanf("%d\n",&userId)
fmt.Println("请输入密码:")
fmt.Scanf("%d\n",&password)
err := login(userId,password)
fmt.Println(err)
if err != nil{
fmt.Println("登录成功")
}else{
fmt.Println("登录失败")
}
case 2:
fmt.Println("注册用户")
case 3:
fmt.Println("退出系统")
// 退出系统
//os.Exit(0)
return
default:
fmt.Println("输入有误")
}
}
}
client.go
package main
import (
"encoding/binary"
"encoding/json"
"fmt"
"goproject/src/chatroom/common/message"
"net"
)
func login(userId int, password string) (err error) {
// 链接到服务端
conn, err := net.Dial("tcp", "127.0.0.1:8889")
if err != nil {
fmt.Println("链接错误", err)
return
}
// 延时关闭
defer conn.Close()
// 通过conn,发送消息给服务端
var mess message.Message
mess.Type = message.LoginMesType
// 创建一个LoginMes 结构体,并赋值
var loginMes message.LoginMes
loginMes.UserId = userId
loginMes.Password = password
loginMes.UserName = "xiaoming"
// 将结构体序列化成字符串
data,err := json.Marshal(loginMes)
if err != nil{
fmt.Println("序列化错误",err)
return
}
// 将字节切片转成字符串
mess.Data = string(data)
// 将mess 结构体序列化成字符串
data,err = json.Marshal(mess)
if err != nil{
fmt.Println("序列化错误",err)
return
}
// 先发送data 的字节长度,发送给 服务器,防止丢包
// 先获取data 长度,转换 成byte切片
var pkgLen uint32
pkgLen = uint32(len(data))
var buffer [4]byte
binary.BigEndian.PutUint32(buffer[0:4],pkgLen)
n,err := conn.Write(buffer[:4])
if n != 4 || err != nil{
fmt.Println("发送失败",err)
return
}
fmt.Println(n,string(data))
return
}
common 消息格式定义
package message
const (
LoginMesType = "LoginMes"
LoginResultMesType = "LoginResultMes"
)
type Message struct {
Type string `json:"type"` // 消息类型
Data string `json:"data"` // 消息具体内容
}
// 定义两种消息
type LoginMes struct {
UserId int `json:"user_id"`
Password string `json:"password"`
UserName string `json:"user_name"`
}
type LoginResultMes struct {
code int `json:"code"` //状态码
Error string `json:"error"` // 错误信息
}
思路分析二
完成客户端可以发送消息,服务端可以正常接收消息,并根据客户端发送的消息,判断用户的合法性,并返回对应的消息
- 客户端发送消息
- 服务器接收消息,反序列化成对应的消息结构体
- 服务端根据反序列化的消息,判断登录用户是否合法,返回消息
- 客户端解析返回的消息,显示对应界面
服务端解析客户端发送的消息
package main
import (
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"goproject/src/chatroom/common/message"
"io"
"net"
)
func readPkg(conn net.Conn)(mes message.Message,err error){
buffer := make([]byte,8096)
n,err := conn.Read(buffer[:4])
if err != nil{
if err == io.EOF{
return
}
fmt.Println("读取错误",err)
return
}
// 根据buffer[:4] 转成uint32类型,获取到需要读取多少个字节数
var pkgLen uint32
pkgLen = binary.BigEndian.Uint32(buffer[:4])
// 根据pkgLen 读取内容,存放到buffer数组中
n,err = conn.Read(buffer[:pkgLen])
if uint32(n) != pkgLen || err != nil{
// 自定义错误
err = errors.New("读取错误~")
return
}
// 将buffer 反序列化成结构体,需要传结构体的地址!!!!
err = json.Unmarshal(buffer[:pkgLen],&mes)
if err != nil{
err = errors.New("反序列化错误")
return
}
return
}
func process(conn net.Conn){
// 延时关闭conn
defer conn.Close()
// 循环读取客户端发送的信息
for {
message,err := readPkg(conn)
if err != nil{
if err == io.EOF{
return
}
fmt.Println("错误",err)
return
}
fmt.Println("客户端发送的消息为:",message)
}
}
func main(){
fmt.Println("服务器在8889端口监听")
listener,err := net.Listen("tcp","127.0.0.1:8889")
defer listener.Close()
if err != nil{
fmt.Println("监听错误",err)
return
}
// 监听成功等待客户端链接服务器
for {
fmt.Println("等待链接")
conn,err := listener.Accept()
if err != nil{
fmt.Println("链接出错",err)
}
// 链接成功,启动协程和客户端保持通信
go process(conn)
}
}
客户端发送消息
// 发送消息
_, err = conn.Write(data)
if err != nil {
fmt.Println("发送失败", err)
return
}
结构化目录重构
服务端
思维导图

目录结构

客户端
将数据缓存在客户端的文件中,以文件当做一个数据库,这样可以实现无网络也能看到消息
思维导图

python防脱发技巧

浙公网安备 33010602011771号