开发者学堂课程【Go 语言核心编程 - 面向对象、文件、单元测试、反射、TCP 编程:海量用户通讯系统-服务器接收消息1 】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/626/detail/9800
海量用户通讯系统-服务器接收消息1
内容介绍:
一、思路分析
二、代码实现
一、 思路分析
对上节课的代码进行一下修改,服务器部分,
将“buf := make([ ]byte, 8096)”提至“defer conn.Close()”与“//循环的客户端发送的信息” 之间,有一个缓冲就可以了;“n,err := conn. Read (buf [:4])”中的n的判断意义不大,可以去掉,指定读4个字节。
Read的含义:
net中找到func Fileconn(f *os.File)(c Conn.err error)
// Read从连接中读取数据
// Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
Read(b [ ]byte) (n int, err error)
其中n值代表实际上读了多少个字节
2. 完成客户端可以完成发送消息本身,服务器端可以正常接收消息,并根据客户端发送的消息(loginmes),判断用户的合法性,并返回相应的loginresmes。
思路分析:
(1)让客户端发送消息本身
(2)服务器端接收到消息,然后反序列化成对应的消息结构体
(3)服务器端根据反序列化成对应的消息,判断是否登录用户是合法的用户,返回LoginResMes
(4)客户端解析返回的LoginResMes显示对应界面
(5)这里我们需要做函数的封装
客户端发送的消息就是登录消息,服务端接收到的消息是字节、字符串,将其反序列化成对应的消息,结构体;
服务器端根据反序列化的消息结构体,判断登录用户是否为合法用户,返回LoginResMes,客户端解析返回的LoginResMes显示对应界面(发送——返回——处理——再处理)。
二、代码实现:
package main
import (
"fmt"
"net"
"go_code/chatroom/common/message”
"encoding/binary"
"encoding/josn"
“errors”
)
func readPkg(conn net.conn)(mes message.Message, err error) {
buf:=make([ ]byte,8096)
fmt.Print1n("读取客户端发送的数据...")
_,err := conn,Read(buf[:4])
If err !=nil {
fmt.Print1n( "conn.Read err=", err)
err = errors.New("read pkg header error")
return
}
//根据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.Print1n( "conn. Read fail err=", err)
return
}
//把pkgLen反序列化成-> message. Message
//技术就是一层窗户纸&mes!!
err=json. Unmarshal(buf[ : pkgLen], &mes)
if err != nil {
fmt.Print1n( "json.Unmarsha err=", err)
return
}
return
}
//处理和客户端的通讯
func process( conn net.conn) {
//这里需要延时关闭conn
defer conn.close()
buf := make([ ]byte, 8096)
//循环的客户端发送的信息
for {
//这里我们将读取数据包,直接封装成一个函数readPkg(),返回Message,Err
mes, err :=readPkg(conn)
if err != nil {
fmt.Print1n( "readPkg err=", err)
}
fmt.Print1n("mes=", mes)
}
}
Func main() {
//提示信息
fmt.Print1n(“服务器在8889端口监听...."")
listen, err:= net.Listen( "tcp", "0.0.0.0:8889")
defer listen.c1ose()
if err != nil {
Buf:=make([ ]byte,8096)
fmt.Print1n("读取客户端发送的数据...")
_,err := conn,Read(buf[:4])
If err !=nil {
fmt.Print1n( "conn.Read err=", err)
return
}
fmt.Print1n("读到的buf=",buf[:4])
代码分析:
发送长度之后,服务端接收到客户端发送的长度,然后客户端发送消息本身,发送的内容就是data,n的作用不大,判断是否产生error,发送消息本身过后,这里还需要处理服务器端返回的消息,要一步一步来进行,读完链接,将其序列化,返回其长度,同时返回一个message,有错误的返回error,这里我们将读取数据包,直接封装成一个函数readPkg(),返回Message,Error。
根据buf[ :4]转成一个uint32类型,
binary.BigEndian.uint32(buf[0:4]),
encoding/binary中有一个uint32([ ]byte) uint32函数,根据pkgLen读取消息内容,理解为从conn中读pkglen个字节放到buf中,希望读pkglen个字节,但实际的读取量是不确定的,如果n不等于pkglen或者管道关闭,就会发生错误信息。
把pkgLen反序列化成-> message. Message类型,err=json. Unmarshal(buf[ : pkgLen], &mes)。
很重要的细节:如果不加&mes,则message是空的。反序列化失败要做一个判断,而后返回message。
记得在最前面引进
"go_code/chatroom/common/message”
"encoding/binary"
"encoding/josn"
自定义出错的部分err = errors.New
在最前边引进“errors”包
可以正常接收消息:加入fmt.Print1n( "readPkg err=", err)
编译结果
读取客户端发送的数据时发生错误: