服务器的目录结构
服务器main.go
主函数只负责监听,等待客户端连接和初始化的工作,然后启一个协程进行业务处理
func main() {
fmt.Println("服务器在新的8889端口监听....")
listen, err := net.Listen("tcp", "0.0.0.0:8889")
defer listen.Close()
if err != nil {
fmt.Println("net.Listen err=", err)
return
}
for {
fmt.Println("等待客户端来链接服务器.....")
conn, err := listen.Accept()
if err != nil {
fmt.Println("listen.Accepe err=", err)
}
go process(conn)
}
}
process函数启了一个processor模块
func process(conn net.Conn) {
defer conn.Close()
processor := &Processor{
Conn: conn,
}
processor.process2()
}
在同一个目录下有一个processor函数模块, processor模块接收数据,启动服务函数serverProcessMes函数
package main
import (
"fmt"
"go_dev/chatRoom/common/message"
process2 "go_dev/chatRoom/server/process"
"go_dev/chatRoom/server/utils"
"io"
"net"
)
type Processor struct {
Conn net.Conn
}
func (this *Processor) serverProcessMes(mes *message.Message) (err error) {
switch mes.Type {
case message.LoginMesType:
// 处理登陆
up := &process2.UserProcess{
Conn: this.Conn,
}
err = up.ServerProcessLogin(mes)
case message.RegisterMesType:
// 处理注册
default:
fmt.Println("消息类型不存在, 无法处理...")
}
return
}
func (this *Processor) process2() {
for {
tf := &utils.Transfer{
Conn: this.Conn,
}
mes, err := tf.ReadPkg()
if err != nil {
if err == io.EOF {
fmt.Println("客户端退出, 服务器端也退出..")
return
} else {
fmt.Println("readPkg err=", err)
return
}
}
fmt.Println("mes=", mes)
err = this.serverProcessMes(&mes)
if err != nil {
return
}
}
}
对应的处理登陆消息的函数
package process2
import (
"encoding/json"
"fmt"
"go_dev/chatRoom/common/message"
"go_dev/chatRoom/server/utils"
"net"
)
type UserProcess struct {
Conn net.Conn
}
func (this *UserProcess) ServerProcessLogin(mes *message.Message) (err error) {
var loginMes message.LoginMes
err = json.Unmarshal([]byte(mes.Data), &loginMes)
if err != nil {
fmt.Println("json.Unmarshall fail err=", err)
return
}
// 先声明一个 resMes
var resMes message.Message
resMes.Type = message.LoginResMesType
// 在声明一个 LoginResMes
var loginResMes message.LoginResMes
// 如果用户id=100, 密码=123456认为合法, 否则不合法
if loginMes.UserId == 100 && loginMes.UserPwd == "123456" {
// 合法
loginResMes.Code = 200
} else {
// 不合法
loginResMes.Code = 500
loginResMes.Error = "该用户不存在, 请注册再使用"
}
// 将loginResMes 序列化
data, err := json.Marshal(loginResMes)
if err != nil {
fmt.Println("json.Marshal fail", err)
return
}
// 将data 赋值给 resMes
resMes.Data = string(data)
// 对resMes 进行序列化,准备发送
data, err = json.Marshal(resMes)
if err != nil {
fmt.Println("json.Marshal fail", err)
return
}
tf := &utils.Transfer{
Conn: this.Conn,
}
err = tf.WritePkg(data)
return
}
其中读数据和写数据需要用到工具包
package utils
import (
"encoding/binary"
"encoding/json"
"fmt"
"go_dev/chatRoom/common/message"
"net"
)
type Transfer struct {
Conn net.Conn
Buf [8096]byte
}
func (this *Transfer) ReadPkg() (mes message.Message, err error) {
//buf := make([]byte, 8096)
fmt.Println("读取客户端发送的数据...")
// 先读取一个数据的长度
_, err = this.Conn.Read(this.Buf[:4])
if err != nil {
//fmt.Println("conn.Read err=", err)
return
}
// 根据buf[:4] 转化成一个 uint32类型
var pkgLen uint32
pkgLen = binary.BigEndian.Uint32(this.Buf[0:4])
// 根据 pkgLen 读取消息内容
n, err := this.Conn.Read(this.Buf[:pkgLen])
if n != int(pkgLen) || err != nil {
fmt.Println("conn.Read fail err=", err)
return
}
err = json.Unmarshal(this.Buf[:pkgLen], &mes)
if err != nil {
fmt.Println("json.Unmarsha err=", err)
return
}
return
}
func (this *Transfer) WritePkg(data []byte) (err error) {
var pkgLen uint32
pkgLen = uint32(len(data))
//var buf [4]byte
binary.BigEndian.PutUint32(this.Buf[0:4], pkgLen)
// 发送一个长度过去
n, err := this.Conn.Write(this.Buf[:4])
if n != 4 || err != nil {
fmt.Println("conn.Write(bytes) fail", err)
return
}
// 发送data本身
n, err = this.Conn.Write(data)
if n != int(pkgLen) || err != nil {
fmt.Println("conn.Write(bytes) fail", err)
return
}
return
}
客户端比服务器稍微简单一些
main.go 提供了一个终端界面主函数
package main
import (
"fmt"
"go_dev/chatRoom/client/process"
"os"
)
var userId int
var userPwd string
func main() {
var key int
for {
fmt.Println("----------欢迎登陆多人聊天系统--------------")
fmt.Println("ttt 1 登陆聊天室")
fmt.Println("ttt 2 注册用户")
fmt.Println("ttt 3 退出系统")
fmt.Println("ttt 请选择(1-3):")
fmt.Scanf("%dn", &key)
switch key {
case 1:
fmt.Println("登陆聊天室")
fmt.Println("请输入用户的id")
fmt.Scanf("%dn", &userId)
fmt.Println("请输入用户的密码")
fmt.Scanf("%sn", &userPwd)
up := &process.UserProcess{}
up.Login(userId, userPwd)
case 2:
fmt.Println("注册用户")
case 3:
fmt.Println("退出系统")
os.Exit(0)
default:
fmt.Println("你的输入有误, 请重新输入")
}
}
/*if key == 1 {
fmt.Println("请输入用户的id")
fmt.Scanf("%dn", &userId)
fmt.Println("请输入用户的密码")
fmt.Scanf("%sn", &userPwd)
err := login.Login(userId, userPwd)
if err != nil {
fmt.Println("登陆失败")
} else {
fmt.Println("登陆成功")
}
} else if key == 2 {
fmt.Println("进行用户注册逻辑")
}*/
}
客户端登陆成功后,开始处理逻辑
package process
import (
"encoding/binary"
"encoding/json"
"fmt"
"go_dev/chatRoom/client/utils"
"go_dev/chatRoom/common/message"
"net"
)
type UserProcess struct {
// 暂时没有字段
}
func (this *UserProcess) Login(userId int, userPwd string) (err error) {
//fmt.Printf("userId = %d userPwd=%sn", userId, userPwd)
//return nil
conn, err := net.Dial("tcp", "localhost:8889")
if err != nil {
fmt.Println("net.Dial err=", err)
return
}
// 延时关闭
var mes message.Message
mes.Type = message.LoginMesType
var loginMes message.LoginMes
loginMes.UserId = userId
loginMes.UserPwd = userPwd
data, err := json.Marshal(loginMes)
if err != nil {
fmt.Println("json.Marshal err=", err)
return
}
mes.Data = string(data)
data, err = json.Marshal(mes)
if err != nil {
fmt.Println("json.Marshal err=", err)
return
}
// 延时关闭
defer conn.Close()
var pkgLen uint32
pkgLen = uint32(len(data))
var buf [4]byte
binary.BigEndian.PutUint32(buf[0:4], pkgLen)
n, err := conn.Write(buf[:4])
if n != 4 || err != nil {
fmt.Println("conn.Write(bytes) fail", err)
return
}
// fmt.Printf("客户端, 发送消息长度成功=%d", len(data))
_, err = conn.Write(data)
if err != nil {
fmt.Println("conn.Write(bytes) fail", err)
return
}
// 这里还需要处理接收
tf := &utils.Transfer{
Conn: conn,
}
mes, err = tf.ReadPkg()
if err != nil {
fmt.Println("readPkg(conn) err=", err)
return
}
var loginResMes message.LoginResMes
err = json.Unmarshal([]byte(mes.Data), &loginResMes)
if loginResMes.Code == 200 {
fmt.Println("登陆成功")
go serverProcessMes(conn)
for {
showMenu()
}
} else if loginResMes.Code == 500 {
fmt.Println(loginResMes.Error)
}
return
}
package process
import (
"fmt"
"go_dev/chatRoom/client/utils"
"net"
"os"
)
func showMenu() {
fmt.Println("-----恭喜xxx登陆成功------")
fmt.Println("-----1. 显示在线用户列表------")
fmt.Println("-----2. 发送消息-------")
fmt.Println("-----3. 信息列表-------")
fmt.Println("-----4. 退出系统-------")
fmt.Println("请选择(1-4):")
var key int
fmt.Scanf("%dn", &key)
switch key {
case 1:
fmt.Println("显示在线用户列表-")
case 2:
fmt.Println("发送消息")
case 3:
fmt.Println("消息列表")
case 4:
fmt.Println("你选择退出了系统...")
os.Exit(0)
default:
fmt.Println("你输入的选项不正确..")
}
}
// 和服务器保持通讯
func serverProcessMes(conn net.Conn) {
tf := &utils.Transfer{
Conn: conn,
}
for {
fmt.Println("客户端正在等待读取服务器发送的消息")
mes, err := tf.ReadPkg()
if err != nil {
fmt.Println("tf.ReadPkg=", err)
return
}
fmt.Printf("mes=%vn", mes)
}
}