开发者学堂课程【Go 语言核心编程 - 面向对象、文件、单元测试、反射、TCP 编程:海量用户通讯信息-服务器接收长度】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/626/detail/9799
海量用户通讯信息-服务器接收长度
内容介绍:
一、实现功能-完成用户登录
二、思路分析
一、实现功能-完成用户登录
要求:
先完成指定用户的验证,用户id=100,密码pwd=123456可以登录,其它用户不能登录。
这里需要先说明Message的组成(示意图),并发送一个Message的流程。
示意图为:
客户端:
1、作用:
(1)、接收的输入的id和pwd
(2)、发送id和密码
(3)、接受到服务端返回的结果
(4)、判断是失败还是成功,并显示对应的页面关键的问题是,怎么组织发送的数据
2、消息类型:
(1)、Message[struct]
-
-----------------
Type string
D
ata string
(2)、LoginMes str{
userld int
u
serPed string
}
3、发送流程:
(1)、先创建一个Message的结构体
(2)、mes.Type=登录消息类型
(3)、mes.Data=登录消息的内容(序列)
(4)、对mes进行序列化
(5)、在网络传输中,最麻烦丢包
先给服务器发送mes的长度(有多少个字节n)
再发送消息本身
服务器:
1、作用
(1)、接收用户id,pwd【goroutine】
(2)、比较
(3)、返回结果
2、消息类型
(1)、Message[struct]
------------------
Type string
Data string
(2)、LoginMes str{
code int//
error string//
}
序列化
3、接收数据的流程:
(1)、接收到客户端发送的长度.len
(2)、根据接收到的长度len,再接收消息本身
(3)、接收时要判断实际接收到的消息内容是否等于len
(4)、如果不相等,就有接错歇息[后面说]
(5)、取到到-反序列化>Message
(6)、取出message.Data(string)-反序列化>LoginMes
(7)、取出loginMes.userid和loginMes.userPwd
(8)、这时就可以比较
(9)、根据比较结果,返回Mess
(10)、发送给客户端
Message实现的功能:
实现完成客户端可以发送消息长度,服务器端可以正常收到该长度值。即先完成客户端到服务器,把发过去的长度前期缩到服务器可以接收到。
二、思路分析:
1、先确定消息Message的格式和结构。
示意图:
服务器在main.go在端口8889(可随意设置)进行监听,A客户端(main.go)/login.go/utic.go与端口8899进行数据请求连接,当有一个请求连接,服务器开启一个协程,进行与客户端交互,协程通过端口进行相互通讯
Package common消息包建消息结构体。
2、根据上图的分析完成message代码
新建两个文件夹,第一个是commom文件夹,common下再建一个文件夹专门发布消息。在server下新建main.go文件夹(目前至少需要此文件夹)
message.go下输入代码
package common
const(
LoginnesType =“LoginMes”
LoginresMesType =“LoginResMes”//登录返回消息
)
//确定消息常量
type Message struct.{
Type string ‘json:“type”’//消息类型
Data string ‘json:“data”’//消息的内容
}
//定义两个消息..后面需要再增加(目前需要两种消息,一是登录消息,二是登录返回结果消息)
type LoginMes struct {
UserId int ‘json:“userld”’//用户id
UserPwd string‘json:“userpwd”’ //用户密码
UserName string‘json:“username”’ //用户名
}
type LoginResMes struct {
Code int ‘json:“code”’// 返回状态,500 表示该用户未注册 ,200 表示登录成功
error string‘json:“error”’//返回错误信息
}
注:LoginMes、LoginResMes填充到Data,Type string需要描述消息的具体类型,LomgMes是结构体本身的名字。
下一步是发消息,构建message结构体后进行发送
3、完成main.go代码以及login.go代码
(1)、在main.go输入
package main
import(
"fmt"
"net"
)
//处理和客户端的通讯
func process(conn net.Conn ){
//读取客户端需要的信息
//提示信息
fmt.Println("服务器在8889端口监听....")
listen, err := net.Listen(itcp","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()
(注:conn是服务器与客户端之间相互作用重要的媒介)
If err!=nil{
fmt.println(“listen.Accpt err=”,err)
}
//一旦链接成功,则启动一个协程和客户端保持通讯..
go process (conn)
}
(2)、在login.go输入代码
package main
import.(
"fmt"
“encoding/json”
“encoding/binary”
“net”
“go_code/chatroom/common/message”
)
//写一个函数,完成登录
func login(userId int, userPwd string) (err error) {
//下一个就要开始定协议..
// fmt.Printf(" userId = %d userPwd=%s\n", userId, userPwd)
// return nil
//1.链接到服务器
conn, err := net.Dial("tcp","localhost:8889")
if err != nil {
fmt.Println("net.Dial err=", err)
return
)
//延时关闭
Defer conn.close
//2.准备通过conn发消息给服务
var mes message.nesssage
Mes.type=message.loginmestype
//3.创建一个LoginMes 结构体
var. loginMes message.LoginMes
loginMes.UserId =userId
loginMes.UserPwd =userPwd
//4.将loginMes序列化
data, err := json.Marshal(loginMes)
if err != nil {
fmt.Println("json.Marshal err=", err)
return
}
// 5. 把data赋给 mes.Data字段
mes.Data =string(data) 此时mes既有data又有type。
// 6.将 mes进行序列化化
data, err =json.Marshal(mes)
if err != nil {
fmt.Println("json.Marshal err=", err)
return
}
// 7.到这个时候 data就是我们要发送的消息
//7.1 先把 data的长度发送给服务器
conn.write(len(data))
此方法是错误的。
在go官网查看net包,type Conn中write实际上发送的是byte切片。
// 先获取到 data的长度->转成一个表示长度的byte切片
Package binary
binary包实现了简单的数字与字节序列的转换以及变长值的编解码,其中一个很重要的方法为ByteOrder,被定义的一个方法为putunit32([]byte,unit32),把具体的数字填到unit32,再给byte切片,那么就会把数字转移给byte切片序列。
var pkglen uint32(此处用unit32是因为发送的数据大)
pkgLen=uint32(len(data))
var buf [4]byte //定义切片
binary.BigEndian.PutUint32(buf[0:4],pkgLen)
// 发送长度
n,err := conn.Write(buf[:4])
if n != 4ll err != nil{
Fmt.println(“conn.write(buf)fail,err”)
Return
}
Fmt.println(“客户端,发送消息的长度ok
return
}
(3)、上述结束后,服务器等待接收,在main.go添加代码
//这里需要延时关闭conn
Defer conn.close()
//循环的读客户端发送的消息
For{
Buf:=make([]byte,8096)
Fmt.println(“读取客户端发送的数据
…
”)
n,err:=conn.read(buf[:4])
if n != 4 ll err != nil {
fmt.Println("conn.Read err=", err)
Return
}
fmt.Println("读到的buf=",buf[:4])
}
func main() {