zoukankan      html  css  js  c++  java
  • tcp编程

    一、端口分类

    0号是保留端口

    1-1024是固定端口(有名端口),被某些程序使用。

    7:echo服务

    21:ftp使用

    22:ssh远程登录协议

    23:telnet使用

    25:smtp服务使用

    80:iis使用

    1025-65535是动态端口

    端口使用注意事项:

    计算机(尤其是做服务器)要尽可能少开端口

    一个端口只能被一个程序监听

    netstat -an可以查看机器有哪些端口在监听

    netsta -anb可以查看监听端口的pid

    二、服务端和客户端

    服务端的处理流程:

    (1)、监听端口

    (2)、接收客户端的tcp链接,建立客户端和服务端的链接

    (3)、创建goroutine,处理该链接的请求(通常客户端会通过链接发送请求包)

    客户端的处理流程:

    (1)、建立与服务端的链接

    (2)、发送请求数据,接收服务端返回的结果数据

    (3)、关闭链接

    服务端server.go

    package main
    
    import (
    	"fmt"
    	"net"
    )
    
    //服务器端功能:在8888端口监听,可以和多个客户端创建链接,链接成功后,客户端可以发送数据,服务器端接受数据,并显示在终端上
    func process(conn net.Conn) {
    	defer conn.Close()
    
    	for {
    		buf := make([]byte, 1024)
    
    		//等待客户端通过conn发送信息,如果客户端没有发送消息,那么协程就阻塞
    		fmt.Printf("服务器在等待客户端%s发送信息
    ", conn.RemoteAddr().String())
    
    		n, err := conn.Read(buf)
    
    		if err != nil {
    			fmt.Printf("客户端退出 err=%v", err)
    			return
    		}
    
    		//显示客户端发送的内容到服务器的终端
    		fmt.Println(string(buf[:n]))
    	}
    }
    
    func main() {
    	fmt.Println("服务器开始监听...")
    
    	//tcp表示使用的网络协议是tcp,0.0.0.0:8888表示在本地监听8888端口
    	listen, err := net.Listen("tcp", "0.0.0.0:8888")
    
    	if err != nil {
    		fmt.Println("listen err=", err)
    		return
    	}
    
    	defer listen.Close()
    
    	//循环等待客户端来链接
    	for {
    		fmt.Println("等待客户端来链接...")
    		conn, err := listen.Accept()
    		if err != nil {
    			fmt.Println("Accept() err=", err)
    		} else {
    			fmt.Printf("Accept() success conn=%v 客户端ip=%v
    ", conn, conn.RemoteAddr().String())
    		}
    		go process(conn)
    	}
    }
    

    客户端client.go

    package main
    
    import (
    	"bufio"
    	"fmt"
    	"net"
    	"os"
    )
    
    //客户端功能:能链接到服务器端的8888端口
    //客户端可以发送单行数据,然后就退出
    //能通过终端输入数据(输入一行发送一行), 并发送给服务器端
    //在终端输入 exit,表示退出程序
    
    func main() {
    	conn, err := net.Dial("tcp", "10.92.120.203:8888")
    
    	if err != nil {
    		fmt.Println("client dial err=", err)
    		return
    	}
    
    	//客户端可以发送单行数据,然后就退出
    	reader := bufio.NewReader(os.Stdin) //io.Stdin代表标准输入
    
    	//从终端读取一行用户输入,并准备发送服务器
    	line, err := reader.ReadString('
    ')
    	if err != nil {
    		fmt.Println("readString err=", err)
    	}
    
    	//将line发送给服务器
    	n, err := conn.Write([]byte(line))
    	if err != nil {
    		fmt.Println("conn.Write err=", err)
    	}
    	fmt.Printf("客户端发送了%d字节的数据,并退出", n)
    }
    

     对客户端client.go做改进:

    package main
    
    import (
    	"fmt"
    	"net"
    )
    
    //服务器端功能:在8888端口监听,可以和多个客户端创建链接,链接成功后,客户端可以发送数据,服务器端接受数据,并显示在终端上
    func process(conn net.Conn) {
    	defer conn.Close()
    
    	for {
    		buf := make([]byte, 1024)
    
    		//等待客户端通过conn发送信息,如果客户端没有发送消息,那么协程就阻塞
    		fmt.Printf("服务器在等待客户端%s发送信息
    ", conn.RemoteAddr().String())
    
    		n, err := conn.Read(buf)
    
    		if err != nil {
    			fmt.Printf("客户端退出 err=%v", err)
    			return
    		}
    
    		//显示客户端发送的内容到服务器的终端
    		fmt.Println(string(buf[:n]))
    	}
    }
    
    func main() {
    	fmt.Println("服务器开始监听...")
    
    	//tcp表示使用的网络协议是tcp,0.0.0.0:8888表示在本地监听8888端口
    	listen, err := net.Listen("tcp", "0.0.0.0:8888")
    
    	if err != nil {
    		fmt.Println("listen err=", err)
    		return
    	}
    
    	defer listen.Close()
    
    	//循环等待客户端来链接
    	for {
    		fmt.Println("等待客户端来链接...")
    		conn, err := listen.Accept()
    		if err != nil {
    			fmt.Println("Accept() err=", err)
    		} else {
    			fmt.Printf("Accept() success conn=%v 客户端ip=%v
    ", conn, conn.RemoteAddr().String())
    		}
    		go process(conn)
    	}
    }
    

    三、海量用户即时通讯系统

    1、需求

    用户注册

    用户登录

    显示在线用户列表

    群聊(广播)

    点对点聊天

    离线留言

    2、实现功能

    (1)、显示客户端登录菜单

    main.go

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    var userId int
    var userPwd string
    
    func main() {
    	//接收用户的选择
    	var key int
    	//判断是否还继续显示菜单
    	var loop = true
    
    	for loop {
    		fmt.Println("-------------------欢迎登录多人聊天系统---------------------")
    		fmt.Println("			 1 登录聊天室")
    		fmt.Println("			 2 注册用户")
    		fmt.Println("			 3 退出系统")
    		fmt.Println("			 请选择(1-3)")
    
    		fmt.Scanf("%d
    ", &key)
    		switch key {
    		case 1:
    			fmt.Println("登录聊天室")
    			loop = false
    		case 2:
    			fmt.Println("注册用户")
    			loop = false
    		case 3:
    			fmt.Println("退出系统")
    			//loop = false
    			os.Exit(0)
    		default:
    			fmt.Println("你的输入有误,请重新输入")
    		}
    	}
    
    	//接收用户的输入,显示新的提示信息
    	if key == 1 {
    		fmt.Println("请输入用户的id")
    		fmt.Scanf("%d
    ", &userId)
    		fmt.Println("请输入用户的密码")
    		fmt.Scanf("%d
    ", &userPwd)
    		//登录函数在login.go文件中
    		err := login(userId, userPwd)
    		if err != nil {
    			fmt.Println("登录失败")
    		} else {
    			fmt.Println("登录成功")
    		}
    	} else if key == 2 {
    		fmt.Println("进行用户注册的逻辑......")
    	}
    }
    

    client.go

    package main
    
    import "fmt"
    
    //登录函数
    func login(userId int, userPwd string) (err error) {
    	fmt.Printf(" userId = %d userPwd = %s
    ", userId, userPwd)
    	return nil
    }
    

    (2)、完成用户登录

    客户端发送消息长度,服务器端正常接收长度值。

    common/message/message.go

    package message
    
    const (
    	LoginMesType    = "LoginMes"
    	LoginResMesType = "LoginResMes"
    )
    
    type Message struct {
    	Type string `json:"type"` //消息类型
    	Data string `json:"data"`
    }
    
    type LoginMes struct {
    	UserId   int    `json:"userId"`
    	UserPwd  string `json:"userPwd"`
    	UserName string `json:"userName"`
    }
    
    type LoginResMes struct {
    	Code  int    `json:"code"`  //返回状态码 500表示用户未注册 200表示登录成功
    	Error string `json:"error"` //返回错误信息
    }
    

    server/main.go

    package main
    
    import (
    	"fmt"
    	"net"
    )
    
    func process(conn net.Conn) {
    	defer conn.Close()
    	//读客户端发送的信息
    	for {
    		buf := make([]byte, 1024*4)
    		n, err := conn.Read(buf[:4])
    		if n != 4 || err != nil {
    			fmt.Println("conn.Read err=", err)
    			return
    		}
    		fmt.Println("读到的buf=", buf)
    	}
    }
    
    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.Accept err=", err)
    		}
    
    		//一旦链接成功,则启动一个协程和客户端保持通讯
    		go process(conn)
    	}
    }
    

    client/login.go

    package main
    
    import (
    	"common/message"
    	"encoding/binary"
    	"encoding/json"
    	"fmt"
    	"net"
    )
    
    //登录函数
    func login(userId int, userPwd string) (err error) {
    	fmt.Printf("userId = %d userPwd = %s
    ", userId, userPwd)
    
    	//链接到服务器
    	conn, err := net.Dial("tcp", "localhost:8889")
    	if err != nil {
    		fmt.Println("net.Dial err=", err)
    		return
    	}
    
    	//准备通过conn发送消息给服务器
    	var mes message.Message
    	mes.Type = message.LoginMesType
    
    	//创建一个LoginMes结构体
    	var loginMes message.LoginMes
    	loginMes.UserId = userId
    	loginMes.UserPwd = userPwd
    
    	//将loginMes序列化
    	data, err := json.Marshal(loginMes)
    
    	if err != nil {
    		fmt.Println("json.Marshal err=", err)
    		return
    	}
    
    	//data赋值给mes.Data字段
    	mes.Data = string(data)
    
    	data, err = json.Marshal(mes)
    	if err != nil {
    		fmt.Println("json.Marshal err=", err)
    		return
    	}
    
    	//data就是要发送的消息
    	//先把data的长度发送给服务器
    	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 内容=%s", len(data), string(data))
    	return
    }
    

    client/main.go

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    var userId int
    var userPwd string
    
    func main() {
    	//接收用户的选择
    	var key int
    	//判断是否还继续显示菜单
    	var loop = true
    
    	for loop {
    		fmt.Println("-------------------欢迎登录多人聊天系统---------------------")
    		fmt.Println("			 1 登录聊天室")
    		fmt.Println("			 2 注册用户")
    		fmt.Println("			 3 退出系统")
    		fmt.Println("			 请选择(1-3)")
    
    		fmt.Scanf("%d
    ", &key)
    		switch key {
    		case 1:
    			fmt.Println("登录聊天室")
    			loop = false
    		case 2:
    			fmt.Println("注册用户")
    			loop = false
    		case 3:
    			fmt.Println("退出系统")
    			//loop = false
    			os.Exit(0)
    		default:
    			fmt.Println("你的输入有误,请重新输入")
    		}
    	}
    
    	//接收用户的输入,显示新的提示信息
    	if key == 1 {
    		fmt.Println("请输入用户的id")
    		fmt.Scanf("%d
    ", &userId)
    		fmt.Println("请输入用户的密码")
    		fmt.Scanf("%s
    ", &userPwd)
    		//登录函数在login.go文件中
    		err := login(userId, userPwd)
    		if err != nil {
    			fmt.Println("登录失败")
    		} else {
    			fmt.Println("登录成功")
    		}
    	} else if key == 2 {
    		fmt.Println("进行用户注册的逻辑......")
    	}
    }
    

    客户端发送消息本身,服务端正常接收消息,并根据客户端发送的消息判断用户的合法性。

    server/main.go

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"io"
    	"net"
    	"tcp/common/message"
    	"tcp/common/utils"
    )
    
    //处理登录请求
    func serverProcessLogin(conn net.Conn, mes *message.Message) (err error) {
    	//1.从mes中取出mes.Data,并直接反序列化成LoginMes
    	var loginMes message.LoginMes
    	err = json.Unmarshal([]byte(mes.Data), &loginMes)
    	if err != nil {
    		fmt.Println("json.Unmarshal fail err=", err)
    		return
    	}
    
    	var resMes message.Message
    	resMes.Type = message.LoginResMesType
    
    	var loginResMes message.LoginResMes
    
    	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
    	}
    
    	//发送data
    	err = utils.WritePkg(conn, data)
    	return
    }
    
    //根据客户端发送消息种类不同,决定调用哪个函数来处理
    func serverProcessMes(conn net.Conn, mes *message.Message) (err error) {
    	switch mes.Type {
    	case message.LoginMesType:
    		//处理登录
    		err = serverProcessLogin(conn, mes)
    	case message.RegisterMesType:
    		//处理注册
    	default:
    		fmt.Println("消息类型不存在,无法处理......")
    	}
    	return
    }
    
    func process(conn net.Conn) {
    	defer conn.Close()
    	//读客户端发送的信息
    	for {
    		//读取数据包,直接封装成一个函数readPkg()
    		mes, err := utils.ReadPkg(conn)
    		if err != nil {
    			if err == io.EOF {
    				fmt.Println("客户端退出,服务器也退出...")
    				return
    			} else {
    				fmt.Println("readPkg err=", err)
    				return
    			}
    		}
    		fmt.Println("mes=", mes)
    
    		err = serverProcessLogin(conn, &mes)
    		if err != nil {
    			return
    		}
    	}
    }
    
    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.Accept err=", err)
    		}
    
    		//一旦链接成功,则启动一个协程和客户端保持通讯
    		go process(conn)
    	}
    }
    

    client/login.go

    package main
    
    import (
    	"encoding/binary"
    	"encoding/json"
    	"fmt"
    	"net"
    	"tcp/common/message"
    	"tcp/common/utils"
    )
    
    //登录函数
    func login(userId int, userPwd string) (err error) {
    	fmt.Printf("userId = %d userPwd = %s
    ", userId, userPwd)
    
    	//链接到服务器
    	conn, err := net.Dial("tcp", "localhost:8889")
    	if err != nil {
    		fmt.Println("net.Dial err=", err)
    		return
    	}
    
    	//准备通过conn发送消息给服务器
    	var mes message.Message
    	mes.Type = message.LoginMesType
    
    	//创建一个LoginMes结构体
    	var loginMes message.LoginMes
    	loginMes.UserId = userId
    	loginMes.UserPwd = userPwd
    
    	//将loginMes序列化
    	data, err := json.Marshal(loginMes)
    
    	if err != nil {
    		fmt.Println("json.Marshal err=", err)
    		return
    	}
    
    	//data赋值给mes.Data字段
    	mes.Data = string(data)
    
    	data, err = json.Marshal(mes)
    	if err != nil {
    		fmt.Println("json.Marshal err=", err)
    		return
    	}
    
    	//data就是要发送的消息
    	//先把data的长度发送给服务器
    	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 内容=%s", len(data), string(data))
    
    	//发送消息本身
    	_, err = conn.Write(data)
    	if err != nil {
    		fmt.Println("conn.Write(data) fail", err)
    		return
    	}
    
    	//休眠20
    	//time.Sleep(20 * time.Second)
    	//fmt.Println("休眠20秒...")
    
    	//处理服务器端返回的消息
    	mes, err = utils.ReadPkg(conn)
    
    	if err != nil {
    		fmt.Println("readPkg(conn) err=", err)
    		return
    	}
    
    	//将mes的Data部分反序列化成LoginResMes
    	var loginResMes message.LoginResMes
    	err = json.Unmarshal([]byte(mes.Data), &loginResMes)
    	if loginResMes.Code == 200 {
    		fmt.Println("登录成功")
    	} else if loginResMes.Code == 500 {
    		fmt.Println(loginResMes.Error)
    	}
    
    	return
    }
    

    client/main.go

    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    var userId int
    var userPwd string
    
    func main() {
    	//接收用户的选择
    	var key int
    	//判断是否还继续显示菜单
    	var loop = true
    
    	for loop {
    		fmt.Println("-------------------欢迎登录多人聊天系统---------------------")
    		fmt.Println("			 1 登录聊天室")
    		fmt.Println("			 2 注册用户")
    		fmt.Println("			 3 退出系统")
    		fmt.Println("			 请选择(1-3)")
    
    		fmt.Scanf("%d
    ", &key)
    		switch key {
    		case 1:
    			fmt.Println("登录聊天室")
    			loop = false
    		case 2:
    			fmt.Println("注册用户")
    			loop = false
    		case 3:
    			fmt.Println("退出系统")
    			//loop = false
    			os.Exit(0)
    		default:
    			fmt.Println("你的输入有误,请重新输入")
    		}
    	}
    
    	//接收用户的输入,显示新的提示信息
    	if key == 1 {
    		fmt.Println("请输入用户的id")
    		fmt.Scanf("%d
    ", &userId)
    		fmt.Println("请输入用户的密码")
    		fmt.Scanf("%s
    ", &userPwd)
    		//登录函数在login.go文件中
    		login(userId, userPwd)
    		//if err != nil {
    		//	fmt.Println("登录失败")
    		//} else {
    		//	fmt.Println("登录成功")
    		//}
    	} else if key == 2 {
    		fmt.Println("进行用户注册的逻辑......")
    	}
    }
    

    common/utils/utils.go

    package utils
    
    import (
    	"encoding/binary"
    	"encoding/json"
    	"fmt"
    	"net"
    	"tcp/common/message"
    )
    
    func ReadPkg(conn net.Conn) (mes message.Message, err error) {
    	buf := make([]byte, 1024*4)
    	fmt.Println("读取客户端发送的数据")
    	_, err = conn.Read(buf[:4])
    	if err != nil {
    		//fmt.Println("conn.Read err=", err)
    		//err=errors.New("read pkg header error")
    		return
    	}
    	fmt.Println("读到的buf=", buf)
    
    	//根据buf[:4]转成一个uint32类型
    	var pkgLen uint32
    	pkgLen = binary.BigEndian.Uint32(buf[0:4])
    
    	//根据pkgLen读取消息内容
    	n, err := conn.Read(buf[:pkgLen])
    
    	if n != int(pkgLen) || err != nil {
    		//fmt.Println("conn.Read fail err=", err)
    		//err=errors.New("read pkg header error")
    		return
    	}
    
    	//pkgLen反序列化成message.Message
    	err = json.Unmarshal(buf[:pkgLen], &mes)
    	if err != nil {
    		fmt.Println("json.Unmarsha err=", err)
    		return
    	}
    	return
    }
    
    func WritePkg(conn net.Conn, data []byte) (err error) {
    	//先发送一个长度给对方
    	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
    	}
    
    	//发送data本身
    	n, err = conn.Write(data)
    	if n != int(pkgLen) || err != nil {
    		fmt.Println("conn.Write(bytes) fail", err)
    		return
    	}
    	return
    }
    

     common/message/message.go

    package message
    
    const (
    	LoginMesType    = "LoginMes"
    	LoginResMesType = "LoginResMes"
    	RegisterMesType = "RegisterMes"
    )
    
    type Message struct {
    	Type string `json:"type"` //消息类型
    	Data string `json:"data"`
    }
    
    type LoginMes struct {
    	UserId   int    `json:"userId"`
    	UserPwd  string `json:"userPwd"`
    	UserName string `json:"userName"`
    }
    
    type LoginResMes struct {
    	Code  int    `json:"code"`  //返回状态码 500表示用户未注册 200表示登录成功
    	Error string `json:"error"` //返回错误信息
    }
    
    type RegisterMes struct {
    }
    

    代码结构: 

    client/main/main.go

    package main
    
    import (
    	"fmt"
    	"os"
    	"tcp/client/process"
    )
    
    var userId int
    var userPwd string
    var userName string
    
    func main() {
    	//接收用户的选择
    	var key int
    	//判断是否还继续显示菜单
    	//var loop = true
    
    	for true {
    		fmt.Println("-------------------欢迎登录多人聊天系统---------------------")
    		fmt.Println("			 1 登录聊天室")
    		fmt.Println("			 2 注册用户")
    		fmt.Println("			 3 退出系统")
    		fmt.Println("			 请选择(1-3)")
    
    		fmt.Scanf("%d
    ", &key)
    		switch key {
    		case 1:
    			fmt.Println("登录聊天室")
    			fmt.Println("请输入用户的id")
    			fmt.Scanf("%d
    ", &userId)
    			fmt.Println("请输入用户的密码")
    			fmt.Scanf("%s
    ", &userPwd)
    			//loop = false
    
    			up := &process.UserProcess{}
    			up.Login(userId, userPwd)
    		case 2:
    			fmt.Println("注册用户")
    			fmt.Println("请输入用户ID:")
    			fmt.Scanf("%d
    ", &userId)
    			fmt.Println("请输入用户密码:")
    			fmt.Scanf("%s
    ", &userPwd)
    			fmt.Println("请输入用户名字(nickname):")
    			fmt.Scanf("%s
    ", &userName)
    
    			up := process.UserProcess{}
    			up.Register(userId, userPwd, userName)
    			//loop = false
    		case 3:
    			fmt.Println("退出系统")
    			//loop = false
    			os.Exit(0)
    		default:
    			fmt.Println("你的输入有误,请重新输入")
    		}
    	}
    }
    

    client/model/curUser.go

    package model
    
    import (
    	"net"
    	"tcp/common/message"
    )
    
    //在客户端很多地方会使用到curUser,将其作为全局的
    type CurUser struct {
    	Conn net.Conn
    	message.User
    }
    

    client/process/server.go

    package process
    
    import (
    	"encoding/json"
    	"fmt"
    	"net"
    	"os"
    	"tcp/client/utils"
    	"tcp/common/message"
    )
    
    //显示登录成功后的界面
    func ShowMenu() {
    	fmt.Println("--------恭喜XXX登录成功---------")
    	fmt.Println("--------1 显示在线用户列表---------")
    	fmt.Println("--------2 发送消息---------")
    	fmt.Println("--------3 信息列表---------")
    	fmt.Println("--------4 退出系统---------")
    	fmt.Println("--------请选择(1-4)---------")
    
    	var key int
    	var content string
    	fmt.Scanf("%d
    ", &key)
    
    	//创建SmsProcess实例:总会使用到SmsProcess实例,所以将其定义在switch外部
    	smsProcess := &SmsProcess{}
    
    	switch key {
    	case 1:
    		//fmt.Println("显示在线用户列表")
    		outputOnlineUser()
    	case 2:
    		fmt.Println("请输入群聊消息")
    		fmt.Scanf("%s
    ", &content)
    		smsProcess.SendGroupMes(content)
    	case 3:
    		fmt.Println("信息列表")
    	case 4:
    		fmt.Println("你选择了退出系统")
    		os.Exit(0)
    	default:
    		fmt.Println("你输入的选项不正确..")
    	}
    }
    
    //和服务器保持通讯
    func serverProcessMes(Conn net.Conn) {
    	//创建transfer实例,不停的读取服务器发送的消息
    	tf := &utils.Transfer{
    		Conn: Conn,
    	}
    	for {
    		//fmt.Println("客户端正在等待读取服务器发送的消息")
    		mes, err := tf.ReadPkg()
    		if err != nil {
    			fmt.Println("tf.ReadPkg err=", err)
    			return
    		}
    
    		//fmt.Printf("mes=%v
    ", mes)
    
    		switch mes.Type {
    		case message.NotifyUserStatusMesType:
    			//1. 取出.NotifyUserStatusMes
    			var notifyUserStatusMes message.NotifyUserStatusMes
    			json.Unmarshal([]byte(mes.Data), &notifyUserStatusMes)
    			//2. 把这个用户的信息,状态保存到客户map[int]User中
    			updateUserStatus(&notifyUserStatusMes)
    			//处理群发消息
    		case message.SmsMesType:
    			outputGroupMes(&mes)
    		default:
    			fmt.Println("服务器端返回了一个未知消息类型")
    		}
    	}
    }
    

    client/process/smsMgr.go

    package process
    
    import (
    	"encoding/json"
    	"fmt"
    	"tcp/common/message"
    )
    
    func outputGroupMes(mes *message.Message) {
    	//显示即可
    	var smsMes message.SmsMes
    
    	err := json.Unmarshal([]byte(mes.Data), &smsMes)
    	if err != nil {
    		fmt.Println("json.UnMarshal err=", err.Error())
    		return
    	}
    
    	//显示消息
    	info := fmt.Sprintf("用户ID:	%d 对大家说:	%s", smsMes.UserId, smsMes.Content)
    	fmt.Println(info)
    	fmt.Println()
    }
    

    client/process/smsProcess.go

    package process
    
    import (
    	"encoding/json"
    	"fmt"
    	"tcp/client/utils"
    	"tcp/common/message"
    )
    
    type SmsProcess struct {
    }
    
    //发送群聊消息
    func (this *SmsProcess) SendGroupMes(content string) (err error) {
    	var mes message.Message
    	mes.Type = message.SmsMesType
    
    	var smsMes message.SmsMes
    	smsMes.Content = content
    	smsMes.UserId = CurUser.UserId
    	smsMes.UserStatus = CurUser.UserStatus
    
    	data, err := json.Marshal(smsMes)
    	if err != nil {
    		fmt.Println("SendGroupMes json.Marshal fail =", err.Error())
    		return
    	}
    
    	mes.Data = string(data)
    
    	data, err = json.Marshal(mes)
    	if err != nil {
    		fmt.Println("SendGroupMes json.Marshal fail =", err.Error())
    		return
    	}
    
    	tf := &utils.Transfer{
    		Conn: CurUser.Conn,
    	}
    
    	err = tf.WritePkg(data)
    
    	if err != nil {
    		fmt.Println("SendGroupMes err=", err.Error())
    		return
    	}
    	return
    }
    

    client/process/userMgr.go

    package process
    
    import (
    	"fmt"
    	"tcp/client/model"
    	"tcp/common/message"
    )
    
    //客户端维护的map
    var onlineUsers map[int]*message.User = make(map[int]*message.User, 10)
    
    //在用户登录成功后完成对CurUser初始化
    var CurUser model.CurUser
    
    //显示当前在线的用户
    func outputOnlineUser() {
    	//遍历onlineUser
    	fmt.Println("当前在线用户列表:")
    	for id, _ := range onlineUsers {
    		fmt.Println("用户ID:	", id)
    	}
    }
    
    //处理返回的NotifyUserStatusMes
    func updateUserStatus(notifyUserStatusMes *message.NotifyUserStatusMes) {
    	user, ok := onlineUsers[notifyUserStatusMes.UserId]
    	if !ok {
    		user = &message.User{
    			UserId: notifyUserStatusMes.UserId,
    		}
    	}
    	user.UserStatus = notifyUserStatusMes.Status
    
    	onlineUsers[notifyUserStatusMes.UserId] = user
    
    	outputOnlineUser()
    }
    

    client/process/userProcess.go

    package process
    
    import (
    	"encoding/binary"
    	"encoding/json"
    	"fmt"
    	"net"
    	"os"
    	"tcp/client/utils"
    	"tcp/common/message"
    )
    
    type UserProcess struct {
    }
    
    //注册函数
    func (this *UserProcess) Register(userId int, userPwd, userName string) (err error) {
    	//链接到服务器
    	conn, err := net.Dial("tcp", "localhost:8889")
    	if err != nil {
    		fmt.Println("net.Dial err=", err)
    		return
    	}
    
    	defer conn.Close()
    
    	var mes message.Message
    	mes.Type = message.RegisterMesType
    
    	var registerMes message.RegisterMes
    	registerMes.User.UserId = userId
    	registerMes.User.UserPwd = userPwd
    	registerMes.User.UserName = userName
    
    	data, err := json.Marshal(registerMes)
    	if err != nil {
    		fmt.Println("json.Marshal err=", err)
    		return
    	}
    
    	mes.Data = string(data)
    
    	//将mes进行序列化
    	data, err = json.Marshal(mes)
    	if err != nil {
    		fmt.Println("json.Marshal err=", err)
    		return
    	}
    
    	tf := &utils.Transfer{
    		Conn: conn,
    	}
    	//发送data给服务器端
    	err = tf.WritePkg(data)
    	if err != nil {
    		fmt.Println("注册发送信息错误 err=", err)
    	}
    
    	mes, err = tf.ReadPkg()
    	if err != nil {
    		fmt.Println("ReadPkg err=", err)
    		return
    	}
    
    	var registerResMes message.RegisterResMes
    	err = json.Unmarshal([]byte(mes.Data), &registerResMes)
    	if registerResMes.Code == 200 {
    		fmt.Println("注册成功,你需要登录")
    		os.Exit(0)
    	} else {
    		fmt.Println(registerResMes.Error)
    		os.Exit(0)
    	}
    	return
    }
    
    //登录函数
    func (this *UserProcess) Login(userId int, userPwd string) (err error) {
    	fmt.Printf("userId = %d userPwd = %s
    ", userId, userPwd)
    
    	//链接到服务器
    	conn, err := net.Dial("tcp", "localhost:8889")
    	if err != nil {
    		fmt.Println("net.Dial err=", err)
    		return
    	}
    
    	//延时关闭
    	defer conn.Close()
    
    	//准备通过conn发送消息给服务器
    	var mes message.Message
    	mes.Type = message.LoginMesType
    
    	//创建一个LoginMes结构体
    	var loginMes message.LoginMes
    	loginMes.UserId = userId
    	loginMes.UserPwd = userPwd
    
    	//将loginMes序列化
    	data, err := json.Marshal(loginMes)
    
    	if err != nil {
    		fmt.Println("json.Marshal err=", err)
    		return
    	}
    
    	//data赋值给mes.Data字段
    	mes.Data = string(data)
    
    	data, err = json.Marshal(mes)
    	if err != nil {
    		fmt.Println("json.Marshal err=", err)
    		return
    	}
    
    	//data就是要发送的消息
    	//先把data的长度发送给服务器
    	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 内容=%s
    ", len(data), string(data))
    
    	//发送消息本身
    	_, err = conn.Write(data)
    	if err != nil {
    		fmt.Println("conn.Write(data) fail", err)
    		return
    	}
    
    	//处理服务器端返回的消息
    	tf := &utils.Transfer{
    		Conn: conn,
    	}
    	mes, err = tf.ReadPkg()
    	fmt.Println("登录时获取到的服务端返回的消息mes=", mes)
    
    	if err != nil {
    		fmt.Println("readPkg(conn) err=", err)
    		return
    	}
    
    	//将mes的Data部分反序列化成 LoginResMes
    	var loginResMes message.LoginResMes
    	err = json.Unmarshal([]byte(mes.Data), &loginResMes)
    	if loginResMes.Code == 200 {
    		//初始化CurUser
    		CurUser.Conn = conn
    		CurUser.UserId = userId
    		CurUser.UserStatus = message.UserOnline
    
    		//fmt.Println("登录成功")
    		//可以显示当前在线用户列表,遍历loginResMes.UsersId
    		fmt.Println("当前在线用户列表如下:")
    		for _, v := range loginResMes.UsersId {
    			//如果我们要求不显示自己在线,下面我们增加一个代码
    			if v == userId {
    				continue
    			}
    
    			fmt.Println("用户id:	", v)
    			//完成 客户端的 onlineUsers 完成初始化
    			user := &message.User{
    				UserId:     v,
    				UserStatus: message.UserOnline,
    			}
    			onlineUsers[v] = user
    		}
    		fmt.Print("
    
    ")
    
    		//启动一个协程,该协程保持和服务器端的通讯.如果服务器有数据推送给客户端,则接收并显示在客户端的终端.
    		go serverProcessMes(conn)
    
    		//1. 显示我们的登录成功的菜单[循环]..
    		for {
    			ShowMenu()
    		}
    
    	} else {
    		fmt.Println("登录失败", loginResMes.Error)
    	}
    	return
    }

    client/utils/utils.go

    package utils
    
    import (
    	"encoding/binary"
    	"encoding/json"
    	"fmt"
    	"net"
    	"tcp/common/message"
    )
    
    type Transfer struct {
    	Conn net.Conn
    	Buf  [8096]byte //传输时使用的缓冲
    }
    
    func (this *Transfer) ReadPkg() (mes message.Message, err error) {
    	_, err = this.Conn.Read(this.Buf[:4])
    	if err != nil {
    		//fmt.Println("conn.Read err=", err)
    		//err=errors.New("read pkg header error")
    		return
    	}
    	//fmt.Println("读到的buf=", this.Buf)
    
    	//根据buf[:4]转成一个uint32类型
    	var pkgLen uint32
    	pkgLen = binary.BigEndian.Uint32(this.Buf[:4])
    
    	//根据pkgLen读取消息内容
    	n, err := this.Conn.Read(this.Buf[:pkgLen])
    
    	if n != int(pkgLen) || err != nil {
    		//fmt.Println("conn.Read fail err=", err)
    		//err=errors.New("read pkg header error")
    		return
    	}
    
    	//pkgLen反序列化成message.Message
    	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[: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
    }
    

    common/message/message.go

    package message
    
    const (
    	LoginMesType            = "LoginMes"
    	LoginResMesType         = "LoginResMes"
    	RegisterMesType         = "RegisterMes"
    	RegisterResMesType      = "RegisterResMes"
    	NotifyUserStatusMesType = "NotifyUserStatusMes"
    	SmsMesType              = "SmsMes"
    )
    
    //定义几个用户状态的常量
    const (
    	UserOnline = iota
    	UserOffline
    	UserBusyStatus
    )
    
    type Message struct {
    	Type string `json:"type"` //消息类型
    	Data string `json:"data"`
    }
    
    type LoginMes struct {
    	UserId   int    `json:"userId"`
    	UserPwd  string `json:"userPwd"`
    	UserName string `json:"userName"`
    }
    
    type LoginResMes struct {
    	Code    int    `json:"code"`  //返回状态码 500表示用户未注册 200表示登录成功
    	Error   string `json:"error"` //返回错误信息
    	UsersId []int                 //保存用户ID的切片
    }
    
    type RegisterMes struct {
    	User User `json:"user"`
    }
    
    type RegisterResMes struct {
    	Code  int    `json:"code"`  //返回状态码 400表示用户名已存在 200表示注册成功
    	Error string `json:"error"` //返回错误信息
    }
    
    //为了配合服务器推送用户状态变化的消息
    type NotifyUserStatusMes struct {
    	UserId int `json:"userId"`
    	Status int `json:"status"` //用户状态
    }
    
    //增加一个SmsMes发送消息
    type SmsMes struct {
    	User                            //匿名的结构体,继承
    	Content string `json:"content"` //消息内容
    }
    

    common/message/user.go

    package message
    
    //用户结构体
    type User struct {
    	//为了序列化和反序列化成功,必须保证用户信息的json字符串的key和结构体的字段对应的tag名字一致
    	UserId     int    `json:"userId"`
    	UserPwd    string `json:"userPwd"`
    	UserName   string `json:"userName"`
    	UserStatus int    `json:"userStatus"`
    	Sex        string `json:"sex"`
    }
    

    server/main/main.go

    package main
    
    import (
    	"fmt"
    	"net"
    	"tcp/server/model"
    )
    
    func processor(conn net.Conn) {
    	defer conn.Close()
    
    	//创建一个总控
    	processor := &Processor{
    		Conn: conn,
    	}
    	err := processor.Process2()
    	if err != nil {
    		fmt.Println("客户端和服务器端通信的协程错误err=", err)
    		return
    	}
    }
    
    //对UserDao初始化
    func initUserDao() {
    	//pool本身就是全局变量
    	//需要注意初始化顺序问题:initPool在initUserDao之前初始化
    	model.MyUserDao = model.NewUserDao(pool)
    }
    
    func init() {
    	//当服务器启动时,就初始化Redis链接池
    	initPool("localhost:6379", 16, 0, 300)
    	initUserDao()
    }
    
    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.Accept err=", err)
    		}
    
    		//一旦链接成功,则启动一个协程和客户端保持通讯
    		go processor(conn)
    	}
    }
    

     server/main/processor.go

    package main
    
    import (
    	"fmt"
    	"io"
    	"net"
    	"tcp/common/message"
    	"tcp/server/process"
    	"tcp/server/utils"
    )
    
    type Processor struct {
    	Conn net.Conn
    }
    
    //根据客户端发送消息种类不同,决定调用哪个函数来处理
    func (this *Processor) ServerProcessMes(mes *message.Message) (err error) {
    	//验证是否能够收到客户端发送的群聊消息
    	fmt.Println("mes=", mes)
    
    	switch mes.Type {
    	case message.LoginMesType:
    		//处理登录
    		up := &process.UserProcess{
    			Conn: this.Conn,
    		}
    		err = up.ServerProcessLogin(mes)
    	case message.RegisterMesType:
    		//处理注册
    		up := &process.UserProcess{
    			Conn: this.Conn,
    		}
    		err = up.ServerProcessRegister(mes)
    	case message.SmsMesType:
    		//创建一个SmsProcess实例完成转发群聊消息
    		smsProcess := &process.SmsProcess{}
    		smsProcess.SendGroupMes(mes)
    
    	default:
    		fmt.Println("消息类型不存在,无法处理......")
    	}
    	return
    }
    
    func (this *Processor) Process2() (err error) {
    	//读客户端发送的信息
    	for {
    		//读取数据包,直接封装成一个函数readPkg()
    		tf := &utils.Transfer{
    			Conn: this.Conn,
    		}
    		mes, err := tf.ReadPkg()
    		if err != nil {
    			if err == io.EOF {
    				fmt.Println("客户端退出,服务器也退出...")
    				return err
    			} else {
    				fmt.Println("readPkg err=", err)
    				return err
    			}
    		}
    		fmt.Println("mes=", mes)
    
    		err = this.ServerProcessMes(&mes)
    		if err != nil {
    			return err
    		}
    	}
    }
    

    server/main/redis.go

    package main
    
    import (
    	"github.com/garyburd/redigo/redis"
    	"time"
    )
    
    //定义全局pool
    var pool *redis.Pool
    
    func initPool(address string, maxIdle, maxActive int, idleTimeout time.Duration) {
    	pool = &redis.Pool{
    		MaxIdle:     maxIdle,     //最大空闲链接数
    		MaxActive:   maxActive,   // 表示和数据库的最大链接数,0表示没有限制
    		IdleTimeout: idleTimeout, // 最大空闲时间
    		//初始化链接的代码, 链接哪个ip的redis
    		Dial: func() (redis.Conn, error) {
    			return redis.Dial("tcp", address)
    		},
    	}
    }
    

    server/model/error.go

    package model
    
    import "errors"
    
    //根据业务逻辑的需要自定义一些错误
    var (
    	ERROR_USER_NOTEXISTS = errors.New("用户不存在")
    	ERROR_USER_EXISTS    = errors.New("用户已存在")
    	ERROR_USER_PWD       = errors.New("密码不正确")
    )
    

    server/model/userDao.go

    package model
    
    import (
    	"encoding/json"
    	"fmt"
    	"github.com/garyburd/redigo/redis"
    	"tcp/common/message"
    )
    
    //在服务器启动后就初始化一个userDao实例
    //将其做成全局的变量,在需要和Redis交互时直接使用即可
    var (
    	MyUserDao *UserDao
    )
    
    //定义UserDao结构体完成对User结构体的各种操作
    type UserDao struct {
    	pool *redis.Pool
    }
    
    //使用工厂模式,创建一个UserDao实例
    func NewUserDao(pool *redis.Pool) (userDao *UserDao) {
    	userDao = &UserDao{
    		pool: pool,
    	}
    	return
    }
    
    //1.根据用户ID返回一个User实例+err
    func (this *UserDao) getUserById(conn redis.Conn, id int) (user *message.User, err error) {
    	//通过给定的ID去Redis查询用户
    	res, err := redis.String(conn.Do("HGet", "users", id))
    	if err != nil {
    		//在users哈希中没有找到对应ID
    		if err == redis.ErrNil {
    			err = ERROR_USER_NOTEXISTS
    		}
    		return
    	}
    
    	user = &message.User{}
    	//res反序列化成User实例
    	err = json.Unmarshal([]byte(res), &user)
    	if err != nil {
    		fmt.Println("json.Unmarshal err=", err)
    		return
    	}
    	return
    }
    
    //Login完成对用户登录的校验:
    //如果用户的ID和pwd都正确,则返回一个user实例;
    //如果用户的ID或pwd有错误,则返回对应的错误信息
    func (this *UserDao) Login(userId int, userPwd string) (user *message.User, err error) {
    	//先从UserDao的链接池中取出一个链接
    	conn := this.pool.Get()
    	defer conn.Close()
    	user, err = this.getUserById(conn, userId)
    	if err != nil {
    		return
    	}
    	if user.UserPwd != userPwd {
    		err = ERROR_USER_PWD
    		return
    	}
    	return
    }
    
    //注册
    func (this *UserDao) Register(user *message.User) (err error) {
    	//先从UserDao的链接池中取出一个链接
    	conn := this.pool.Get()
    	defer conn.Close()
    	_, err = this.getUserById(conn, user.UserId)
    	if err == nil {
    		err = ERROR_USER_EXISTS
    		return
    	}
    
    	//这时ID在Redis中还没有,可以完成注册
    	data, err := json.Marshal(user) //序列化
    	if err != nil {
    		return
    	}
    
    	//入库
    	conn.Do("HSet", "users", user.UserId, string(data))
    	if err != nil {
    		fmt.Println("保存注册用户错误 err=", err)
    		return
    	}
    	return
    }
    

    server/process/smsProcess.go

    package process
    
    import (
    	"encoding/json"
    	"fmt"
    	"net"
    	"tcp/common/message"
    	"tcp/server/utils"
    )
    
    type SmsProcess struct{}
    
    //转发消息
    func (this *SmsProcess) SendGroupMes(mes *message.Message) {
    	//遍历服务器端onlineUsers map[int]*UserProcess,将消息转发出去
    
    	//取出mes的内容SmsMes
    	var smsMes message.SmsMes
    	err := json.Unmarshal([]byte(mes.Data), &smsMes)
    	if err != nil {
    		fmt.Println("json.UnMarshal err=", err)
    		return
    	}
    
    	data, err := json.Marshal(mes)
    	if err != nil {
    		fmt.Println("json.Marshal err=", err)
    		return
    	}
    
    	for id, up := range userMgr.onlineUsers {
    		//过滤掉自己,不要把消息发送给自己
    		if id == smsMes.UserId {
    			continue
    		}
    		this.SendMesToEachOnlineUser(data, up.Conn)
    	}
    }
    
    func (this *SmsProcess) SendMesToEachOnlineUser(data []byte, conn net.Conn) {
    	tf := &utils.Transfer{
    		Conn: conn,
    	}
    
    	err := tf.WritePkg(data)
    	if err != nil {
    		fmt.Println("转发消息失败 err=", err)
    	}
    }
    

    server/process/userMgr.go

    package process
    
    import "fmt"
    
    //UserMgr实例在服务器端有且只有一个,在很多地方都会使用到,因此将其定义为全局变量
    var (
    	userMgr *UserMgr
    )
    
    type UserMgr struct {
    	onlineUsers map[int]*UserProcess
    }
    
    //完成对userMgr初始化工作
    func init() {
    	userMgr = &UserMgr{
    		onlineUsers: make(map[int]*UserProcess, 1024),
    	}
    }
    
    //完成对onlineUser添加
    func (this *UserMgr) AddOnlineUser(up *UserProcess) {
    	this.onlineUsers[up.UserId] = up
    }
    
    //删除
    func (this *UserMgr) DelOnlineUser(userId int) {
    	delete(this.onlineUsers, userId)
    }
    
    //返回当前所有在线的用户
    func (this *UserMgr) GetAllOnlineUser() map[int]*UserProcess {
    	return this.onlineUsers
    }
    
    //根据ID返回对应的值
    func (this *UserMgr) GetOnlineUserById(userId int) (up *UserProcess, err error) {
    	//从map中取出一直
    	up, ok := this.onlineUsers[userId]
    	//要查找的用户的当前不在线
    	if !ok {
    		err = fmt.Errorf("用户%d不存在", userId)
    		return
    	}
    	return
    }
    

    server/process/userProcess.go

    package process
    
    import (
    	"encoding/json"
    	"fmt"
    	"net"
    	"tcp/common/message"
    	"tcp/server/model"
    	"tcp/server/utils"
    )
    
    type UserProcess struct {
    	Conn net.Conn
    	//表示conn是属于哪个用户的
    	UserId int
    }
    
    //通知所有在线用户
    //userId要通知其他的在线用户,我上线
    func (this *UserProcess) NotifyOthersOnlineUser(userId int) {
    	//遍历onlineUsers,然后一个一个发送NotifyUserStatusMes
    	for id, up := range userMgr.onlineUsers {
    		//跳过自己
    		if id == userId {
    			continue
    		}
    
    		//开始通知其他在线用户
    		up.NotifyMeOnline(userId)
    	}
    }
    
    func (this *UserProcess) NotifyMeOnline(userId int) {
    	var mes message.Message
    	mes.Type = message.NotifyUserStatusMesType
    
    	var notifyUserStatusMes message.NotifyUserStatusMes
    	notifyUserStatusMes.UserId = userId
    	notifyUserStatusMes.Status = message.UserOnline
    
    	data, err := json.Marshal(notifyUserStatusMes)
    	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
    	}
    
    	tf := &utils.Transfer{
    		Conn: this.Conn,
    	}
    
    	err = tf.WritePkg(data)
    	if err != nil {
    		fmt.Println("NotifyMeOnline err=", err)
    		return
    	}
    }
    
    func (this *UserProcess) ServerProcessRegister(mes *message.Message) (err error) {
    	var registerMes message.RegisterMes
    	err = json.Unmarshal([]byte(mes.Data), &registerMes)
    	if err != nil {
    		fmt.Println("json.Unmarshal fail err=", err)
    		return
    	}
    
    	var resMes message.Message
    	resMes.Type = message.RegisterResMesType
    	var registerResMes message.RegisterResMes
    
    	err = model.MyUserDao.Register(&registerMes.User)
    
    	if err != nil {
    		if err == model.ERROR_USER_EXISTS {
    			registerResMes.Code = 505
    			registerResMes.Error = model.ERROR_USER_EXISTS.Error()
    		} else {
    			registerResMes.Code = 506
    			registerResMes.Error = "注册发生未知错误..."
    		}
    	} else {
    		registerResMes.Code = 200
    	}
    
    	data, err := json.Marshal(registerResMes)
    	if err != nil {
    		fmt.Println("json.Marshal fail", err)
    		return
    	}
    
    	resMes.Data = string(data)
    
    	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
    }
    
    //处理登录请求
    func (this *UserProcess) ServerProcessLogin(mes *message.Message) (err error) {
    	//1.从mes中取出mes.Data,并直接反序列化成LoginMes
    	var loginMes message.LoginMes
    	err = json.Unmarshal([]byte(mes.Data), &loginMes)
    	if err != nil {
    		fmt.Println("json.Unmarshal fail err=", err)
    		return
    	}
    
    	var resMes message.Message
    	resMes.Type = message.LoginResMesType
    
    	var loginResMes message.LoginResMes
    
    	//需要去Redis数据库完成用户的登录验证
    	user, err := model.MyUserDao.Login(loginMes.UserId, loginMes.UserPwd)
    	if err != nil {
    		if err == model.ERROR_USER_NOTEXISTS {
    			loginResMes.Code = 500
    			loginResMes.Error = err.Error()
    		} else if err == model.ERROR_USER_PWD {
    			loginResMes.Code = 403
    			loginResMes.Error = err.Error()
    		} else {
    			loginResMes.Code = 505
    			loginResMes.Error = "服务器内部错误..."
    		}
    	} else {
    		loginResMes.Code = 200
    		//登录成功的用户的userId赋给this
    		this.UserId = loginMes.UserId
    
    		//用户登录成功,将其放入到userMgr中
    		userMgr.AddOnlineUser(this)
    
    		writeLoginResMes(loginResMes,resMes,this)
    
    		//通知其它的在线用户, 我上线了
    		this.NotifyMeOnline(loginMes.UserId)
    
    		//将当前在线用户的ID放入到loginResMes.UserId
    		for id, _ := range userMgr.onlineUsers {
    			loginResMes.UsersId = append(loginResMes.UsersId, id)
    		}
    		fmt.Println(*user, "登录成功")
    	}
    	return
    }
    
    func writeLoginResMes(loginResMes message.LoginResMes, resMes message.Message, this *UserProcess) {
    
    	//将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,
    	}
    
    	//发送data
    	err = tf.WritePkg(data)
    }
    

    server/utils/utils.go

    package utils
    
    import (
    	"encoding/binary"
    	"encoding/json"
    	"fmt"
    	"net"
    	"tcp/common/message"
    )
    
    type Transfer struct {
    	Conn net.Conn
    	Buf  [8096]byte //传输时使用的缓冲
    }
    
    func (this *Transfer) ReadPkg() (mes message.Message, err error) {
    	//buf := make([]byte, 1024*4)
    	fmt.Println("读取客户端发送的数据")
    	_, err = this.Conn.Read(this.Buf[:4])
    	if err != nil {
    		//fmt.Println("conn.Read err=", err)
    		//err=errors.New("read pkg header error")
    		return
    	}
    	//fmt.Println("读到的buf=", this.Buf)
    
    	//根据buf[:4]转成一个uint32类型
    	var pkgLen uint32
    	pkgLen = binary.BigEndian.Uint32(this.Buf[:4])
    
    	//根据pkgLen读取消息内容
    	n, err := this.Conn.Read(this.Buf[:pkgLen])
    
    	if n != int(pkgLen) || err != nil {
    		//fmt.Println("conn.Read fail err=", err)
    		//err=errors.New("read pkg header error")
    		return
    	}
    
    	//pkgLen反序列化成message.Message
    	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[: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
    }
    
  • 相关阅读:
    JS开发框架DevExtreme v20.1.7上线
    Web开发:看如何定义Kendo UI Grid Width
    如何创建自定义DevExpress报表控件,看完你就懂了
    高性能HTML5/JavaScript开发框架DevExtreme全新发布v20.1.7|附下载
    WPF界面开发:如何将不同集合中的项目显示为同一父节点子项
    界面开发包DevExpress v20.1.7上线!即刻体验
    WPF界面开发2020:Scheduler等控件功能升级
    Winform界面开发看过来!一招教你使用属性网格自定义编辑器
    将从数据库查询出来的带有父子结构的list转换成treeList结构
    将JDBC ResultSet结果集转成List
  • 原文地址:https://www.cnblogs.com/xidian2014/p/10681647.html
Copyright © 2011-2022 走看看