服务器的目录结构

服务器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)
	}
}