您的位置:首页 > 文旅 > 美景 > 项目计划书范文模板_网站建设多少钱实惠湘潭磐石网络_青岛网站关键词优化公司_2023b站推广大全

项目计划书范文模板_网站建设多少钱实惠湘潭磐石网络_青岛网站关键词优化公司_2023b站推广大全

2024/12/26 1:31:12 来源:https://blog.csdn.net/weixin_40548182/article/details/142788469  浏览:    关键词:项目计划书范文模板_网站建设多少钱实惠湘潭磐石网络_青岛网站关键词优化公司_2023b站推广大全
项目计划书范文模板_网站建设多少钱实惠湘潭磐石网络_青岛网站关键词优化公司_2023b站推广大全

1、登录界面

说明:这一节的内容采用 go mod 管理【GO111MODULE=‘’】的模块,从第二节开始使用【GO111MODULE=‘off’】GOPATH 管理模块。具体参见 go 包相关知识

1.1登录界面代码目录结构

代码所在目录/Users/zld/Go-project/day8/chatroom/
在这里插入图片描述

1.2登录界面代码

main.go

package mainimport ("fmt"
)var userId int
var userPwd stringfunc main() {var key intvar loop = truefor loop {fmt.Println("---------------欢迎登录多人聊天系统-------------------")fmt.Println("\t\t\t 1、登录聊天室")fmt.Println("\t\t\t 2、注册用户")fmt.Println("\t\t\t 3、退出系统")fmt.Println("\t\t\t 请选择(1-3):")fmt.Scanf("%d\n", &key)switch key {case 1:fmt.Println("登录聊天室")loop = falsecase 2:fmt.Println("注册用户")loop = falsecase 3:fmt.Println("退出系统")loop = falsedefault:fmt.Println("你的输入有误,请重新输入")}}if key == 1 {fmt.Println("请输入用户的id")fmt.Scanf("%d\n", &userId)fmt.Println("请输入用户密码")fmt.Scanf("%s\n", &userPwd)err := login(userId, userPwd)if err != nil {fmt.Println("登录失败")} else {fmt.Println("登录成功")}} else if key == 2 {fmt.Println("进行用户注册的逻辑")}
}

login.go

package mainimport ("fmt"
)func login(userId int, userPwd string) (err error) {fmt.Printf("userId = %d userPwd = %s\n", userId, userPwd)return nil
}

1.3初始化模块

go mod init client 

注意:init 后跟的名字和二进制文件名字(go build -o 后的名字)一样

go: warning: ignoring go.mod in $GOPATH /Users/zld/goproject
go: creating new go.mod: module client
go: to add module requirements and sums:go mod tidy

1.4编译

cd /Users/zld/Go-project/day8/chatroom/client/
go build -o client ./
输出
go: warning: ignoring go.mod in $GOPATH /Users/zld/goproject

1.5演示代码

go run client 
go: warning: ignoring go.mod in $GOPATH /Users/zld/goproject
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):
1
登录聊天室
请输入用户的id
123
请输入用户密码
qwe
userId = 123 userPwd = qwe
登录成功
go run client 
go: warning: ignoring go.mod in $GOPATH /Users/zld/goproject
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):
3
退出系统
go run client 
go: warning: ignoring go.mod in $GOPATH /Users/zld/goproject
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):
2
注册用户
进行用户注册的逻辑
go run client 
go: warning: ignoring go.mod in $GOPATH /Users/zld/goproject
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):
>
你的输入有误,请重新输入
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):
你的输入有误,请重新输入
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):
?
你的输入有误,请重新输入
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):
你的输入有误,请重新输入
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):
5
你的输入有误,请重新输入
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):

2、客户端服务端简单交互

2.1代码目录结构

GOPATH=‘/Users/zld/Go-project’
项目目录结构,项目在 /Users/zld/Go-project/src 【GOPATH指定的目录】下

tree
.
└── chatroom├── client│   ├── login.go│   ├── main.go  ├── common│   └── message│       └── message.go└── server└── main.go6 directories, 4 files

2.2代码

2.2.1 day8/chatroom/common/message/message.go
package messageconst (LoginMesType    = "LoginMes"LoginResMesType = "LoginResMes"
)type Message struct {Type string `josn: "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"`Error string `json: "error"`
}
2.2.2 day8/chatroom/server/main.go
package mainimport ("fmt""net"
)// 处理和客户端的通信
func process(conn net.Conn) {//这里需要延时关闭conndefer conn.Close()//循环的客户端发送的信息for {buf := make([]byte, 8096)n, err := conn.Read(buf[:4])if n != 4 || err != nil {fmt.Println("conn.Read err=", err)return}fmt.Printf("读到的buf=%d\n", buf[:4])}}func main() {//提示信息fmt.Println("服务器在 8889 端口监听......")listen, err := net.Listen("tcp", "0.0.0.0:8889")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)}
}
2.2.3 day8/chatroom/client/client.go
package mainimport ("fmt"
)var userId int
var userPwd stringfunc main() {var key intvar loop = truefor loop {fmt.Println("---------------欢迎登录多人聊天系统-------------------")fmt.Println("\t\t\t 1、登录聊天室")fmt.Println("\t\t\t 2、注册用户")fmt.Println("\t\t\t 3、退出系统")fmt.Println("\t\t\t 请选择(1-3):")fmt.Scanf("%d\n", &key)switch key {case 1:fmt.Println("登录聊天室")loop = falsecase 2:fmt.Println("注册用户")loop = falsecase 3:fmt.Println("退出系统")loop = falsedefault:fmt.Println("你的输入有误,请重新输入")}}if key == 1 {fmt.Println("请输入用户的id")fmt.Scanf("%d\n", &userId)fmt.Println("请输入用户密码")fmt.Scanf("%s\n", &userPwd)err := login(userId, userPwd)if err != nil {fmt.Println("登录失败")} else {fmt.Println("登录成功")}} else if key == 2 {fmt.Println("进行用户注册的逻辑")}
}
2.2.4 day8/chatroom/client/main.go
package mainimport ("day8/chatroom/common/message" // 这里是写 go mod init 的 chatroom,然后最后是文件夹"encoding/binary""encoding/json""fmt""net"
)func login(userId int, userPwd string) (err error) {//fmt.Printf("userId = %d userPwd = %s\n", userId, userPwd)//return nil//连接到服务器conn, err := net.Dial("tcp", "localhost:8889")if err != nil {fmt.Println("net.Dial err=", err)return}//延时关闭defer conn.Close()//准备通过 conn 发送消息给服务器var mes message.Messagemes.Type = message.LoginMesTypevar loginMes message.LoginMesloginMes.UserId = userIdloginMes.UserPwd = userPwd//将 loginMes 序列化data, err := json.Marshal(loginMes)if err != nil {fmt.Println("json.Marshal err=", err)return}//将data赋值给 message 结构体 Data 字段mes.Data = string(data)//将 mes 进行序列化data, err = json.Marshal(mes)if err != nil {fmt.Println("json.Marshal err=", err)return}//data是 我们要发送的消息,先发送 data 长度//由于 conn 接口的 Write 方法参数要求是 bytes 切片var pkgLen uint32pkgLen = uint32(len(data))var buf [4]bytebinary.BigEndian.PutUint32(buf[0:4], pkgLen)//发送长度n, err := conn.Write(buf[0:4])if n != 4 || err != nil {fmt.Println("conn.Write(bytes) fail", err)return}fmt.Printf("客户端,发送消息的长度=%d,消息内容为: %s\n", len(data), string(data))return
}

2.3 编译项目代码

注意:如果在 GO111MODULE=‘off’ 的情况下,编译代码一定要进到 $GOPATH 目录。

cd $GOPATH
go build -o server day8/chatroom/server/
go build -o client day8/chatroom/client/

2.4 演示代码

./server 
服务器在 8889 端口监听......
等待客户端连接服务器......
./client 
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):

client

1
登录聊天室
请输入用户的id
100
请输入用户密码
qwe
客户端,发送消息的长度=83,消息内容为: {"Type":"LoginMes","Data":"{\"UserId\":100,\"UserPwd\":\"qwe\",\"UserName\":\"\"}"}
登录成功

server

等待客户端连接服务器......
读到的buf=[0 0 0 83]
conn.Read err= EOF

3、判断用户输入账户密码并改进代码结构

3.1 代码目录结构

echo $PWD
/Go-project/src/day8
tree
.
└── chatroom├── client│   ├── login.go│   ├── main.go│   └── utils.go├── common│   └── message│       └── message.go└── server└── main.go6 directories, 5 files

3.2 代码

3.2.1 day8/chatroom/client/login.go
package mainimport ("day8/chatroom/common/message" // 这里是写 go mod init 的 chatroom,然后最后是文件夹"encoding/binary""encoding/json""fmt""net"//"time"
)func login(userId int, userPwd string) (err error) {//fmt.Printf("userId = %d userPwd = %s\n", userId, userPwd)//return nil//连接到服务器conn, err := net.Dial("tcp", "localhost:8889")if err != nil {fmt.Println("net.Dial err=", err)return}//延时关闭defer conn.Close()//准备通过 conn 发送消息给服务器var mes message.Messagemes.Type = message.LoginMesTypevar loginMes message.LoginMesloginMes.UserId = userIdloginMes.UserPwd = userPwd//将 loginMes 序列化data, err := json.Marshal(loginMes)if err != nil {fmt.Println("json.Marshal err=", err)return}//将data赋值给 message 结构体 Data 字段mes.Data = string(data)//将 mes 进行序列化data, err = json.Marshal(mes)if err != nil {fmt.Println("json.Marshal err=", err)return}//data是 我们要发送的消息,先发送 data 长度//由于 conn 接口的 Write 方法参数要求是 bytes 切片var pkgLen uint32pkgLen = uint32(len(data))var buf [4]bytebinary.BigEndian.PutUint32(buf[0:4], pkgLen)//发送长度n, err := conn.Write(buf[0:4])if n != 4 || err != nil {fmt.Println("conn.Write(bytes) fail", err)return}fmt.Printf("客户端,发送消息的长度=%d,消息内容为: %s\n", len(data), string(data))_, err = conn.Write(data)if err != nil {fmt.Printf("conn.Write(data) fail", err)return}//time.sleep(20*time.Second)//fmt.Println("休眠了20S")//这里还需要处理服务器返回的消息mes, err = readPkg(conn) //mes 就是if err != nil {fmt.Println("readPkg(conn) err=", err)return}//将 mes 的 data 部分反序列化成 LoginResMesvar loginResMes message.LoginResMeserr = json.Unmarshal([]byte(mes.Data), &loginResMes)if loginResMes.Code == 200 {fmt.Println("登录成功")} else if loginResMes.Code == 500 {fmt.Println(loginResMes.Error)}return
}
3.2.2 day8/chatroom/client/main.go
package mainimport ("fmt"
)var userId int
var userPwd stringfunc main() {var key intvar loop = truefor loop {fmt.Println("---------------欢迎登录多人聊天系统-------------------")fmt.Println("\t\t\t 1、登录聊天室")fmt.Println("\t\t\t 2、注册用户")fmt.Println("\t\t\t 3、退出系统")fmt.Println("\t\t\t 请选择(1-3):")fmt.Scanf("%d\n", &key)switch key {case 1:fmt.Println("登录聊天室")loop = falsecase 2:fmt.Println("注册用户")loop = falsecase 3:fmt.Println("退出系统")loop = falsedefault:fmt.Println("你的输入有误,请重新输入")}}if key == 1 {fmt.Println("请输入用户的id")fmt.Scanf("%d\n", &userId)fmt.Println("请输入用户密码")fmt.Scanf("%s\n", &userPwd)login(userId, userPwd)//err := login(userId, userPwd)// if err != nil {// 	fmt.Println("登录失败")// } else {// 	fmt.Println("登录成功")// }} else if key == 2 {fmt.Println("进行用户注册的逻辑")}
}
3.2.3 day8/chatroom/client/utils.go
package mainimport ("day8/chatroom/common/message""encoding/binary""encoding/json""errors""fmt""net"
)func readPkg(conn net.Conn) (mes message.Message, err error) {buf := make([]byte, 8096)fmt.Println("读取客户端发送的数据...")// conn.Read 在 conn 没有被关闭的情况下,才会阻塞//如果客户端关闭了conn 就不会阻塞_, err = conn.Read(buf[:4])if err != nil {//err = errors.New("read pkg header error")return}//根据读到的  buf[:4] 转成一个 unit32 类型var pkgLen uint32pkgLen = binary.BigEndian.Uint32(buf[0:4])n, err := conn.Read(buf[:pkgLen])if n != int(pkgLen) || err != nil {//err = errors.New("read pkg body error")return}//把 pkgLen 反序列化成  -> message.Messageerr = json.Unmarshal(buf[:pkgLen], &mes)if err != nil {err = errors.New("json.Unmarshal error")return}return}func writePkg(conn net.Conn, data []byte) (err error) {//先发送一个长度给对方//data是 我们要发送的消息,先发送 data 长度//由于 conn 接口的 Write 方法参数要求是 bytes 切片var pkgLen uint32pkgLen = uint32(len(data))var buf [4]bytebinary.BigEndian.PutUint32(buf[0:4], pkgLen)//发送长度n, err := conn.Write(buf[0: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
}
3.2.4 day8/chatroom/common/message/message.go
package messageconst (LoginMesType    = "LoginMes"LoginResMesType = "LoginResMes"RegisterMesType = "RegisterMes"
)type Message struct {Type string `josn: "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"`Error string `json: "error"`
}type RegisterMes struct{//
}
3.2.5 day8/chatroom/server/main.go
package mainimport ("day8/chatroom/common/message""encoding/binary""encoding/json""errors""fmt""io""net"
)func readPkg(conn net.Conn) (mes message.Message, err error) {buf := make([]byte, 8096)// conn.Read 在 conn 没有被关闭的情况下,才会阻塞//如果客户端关闭了conn 就不会阻塞_, err = conn.Read(buf[:4])if err != nil {//err = errors.New("read pkg header error")return}//根据读到的  buf[:4] 转成一个 unit32 类型var pkgLen uint32pkgLen = binary.BigEndian.Uint32(buf[0:4])n, err := conn.Read(buf[:pkgLen])if n != int(pkgLen) || err != nil {//err = errors.New("read pkg body error")return}//把 pkgLen 反序列化成  -> message.Messageerr = json.Unmarshal(buf[:pkgLen], &mes)if err != nil {err = errors.New("json.Unmarshal error")return}return}func writePkg(conn net.Conn, data []byte) (err error) {//先发送一个长度给对方//data是 我们要发送的消息,先发送 data 长度//由于 conn 接口的 Write 方法参数要求是 bytes 切片var pkgLen uint32pkgLen = uint32(len(data))var buf [4]bytebinary.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
}func serverProcessLogin(conn net.Conn, mes *message.Message) (err error) {//核心代码//先从mes 中取出 mes.Data,并直接反序列化成 LoginMesvar loginMes message.LoginMeserr = json.Unmarshal([]byte(mes.Data), &loginMes)if err != nil {fmt.Println("json.Unmarshal fail err=", err)return}// 先声明一个 resMesvar resMes message.MessageresMes.Type = message.LoginResMesType//再声明一个 LoginResMesvar loginResMes message.LoginResMes//如果用户id=100,密码=123456,认为合法,否则不合法if loginMes.UserId == 100 && loginMes.UserPwd == "123456" {//合法loginResMes.Code = 200} else {//不合法loginResMes.Code = 500loginResMes.Error = "该用户不存在,请注册再使用..."}//将 loginResMes 序列化data, err := json.Marshal(loginResMes)if err != nil {fmt.Println("json.Marshal fail", err)return}//将data赋值给resMesresMes.Data = string(data)//对 resMes 进行序列化,准备发送data, err = json.Marshal(resMes)if err != nil {fmt.Println("json.Marshal fail", err)return}//发送 data,将其封装到函数中err = 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.Printf("消息类型不存在,无法处理")}return
}// 处理和客户端的通信
func process(conn net.Conn) {//这里需要延时关闭conndefer conn.Close()//循环客户端发送信息for {mes, err := readPkg(conn)if err != nil {if err == io.EOF {fmt.Println("客户端退出,服务器端也退出..")return} else {fmt.Println("readPkg err=", err)return}}//fmt.Println("mes=", mes)err = serverProcessMes(conn, &mes)if err != nil {return}}}func main() {//提示信息fmt.Println("服务器在 8889 端口监听......")listen, err := net.Listen("tcp", "0.0.0.0:8889")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)}
}

3.3 编译项目代码

go build -o server day8/chatroom/server/
go build -o client day8/chatroom/client/

3.4 演示代码

./server 
服务器在 8889 端口监听......
等待客户端连接服务器......
./client 
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):

client

1
登录聊天室
请输入用户的id
123
请输入用户密码
123456
客户端,发送消息的长度=86,消息内容为: {"Type":"LoginMes","Data":"{\"UserId\":123,\"UserPwd\":\"123456\",\"UserName\":\"\"}"}
读取客户端发送的数据...
该用户不存在,请注册再使用...

server

等待客户端连接服务器......
客户端退出,服务器端也退出..

4、改进服务端代码结构

客户端 client 目录下的代码不变

4.1 代码目录结构

echo $PWD
/Go-project/src/day8
tree
.
└── chatroom├── client│   ├── login.go│   ├── main.go│   └── utils.go├── common│   └── message│       └── message.go└── server├── main│   ├── main.go│   └── processor.go├── model├── process│   ├── smsProcess.go│   └── userProcess.go└── utils└── utils.go10 directories, 9 files

4.2 代码

这里只展示改动的 server 目录下的代码。

4.2.1 day8/chatroom/server/main/main.go
package mainimport (// "day8/chatroom/common/message"// "encoding/binary"// "encoding/json"// "errors""fmt"// "io""net"
)// func readPkg(conn net.Conn) (mes message.Message, err error) {
// 	buf := make([]byte, 8096)
// 	// conn.Read 在 conn 没有被关闭的情况下,才会阻塞
// 	//如果客户端关闭了conn 就不会阻塞
// 	_, err = conn.Read(buf[:4])
// 	if err != nil {
// 		//err = errors.New("read pkg header error")
// 		return
// 	}
// 	//根据读到的  buf[:4] 转成一个 unit32 类型
// 	var pkgLen uint32
// 	pkgLen = binary.BigEndian.Uint32(buf[0:4])
// 	n, err := conn.Read(buf[:pkgLen])
// 	if n != int(pkgLen) || err != nil {
// 		//err = errors.New("read pkg body error")
// 		return
// 	}
// 	//把 pkgLen 反序列化成  -> message.Message
// 	err = json.Unmarshal(buf[:pkgLen], &mes)
// 	if err != nil {
// 		err = errors.New("json.Unmarshal error")
// 		return
// 	}
// 	return// }// func writePkg(conn net.Conn, data []byte) (err error) {
// 	//先发送一个长度给对方
// 	//data是 我们要发送的消息,先发送 data 长度
// 	//由于 conn 接口的 Write 方法参数要求是 bytes 切片
// 	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
// }// func serverProcessLogin(conn net.Conn, mes *message.Message) (err error) {
// 	//核心代码
// 	//先从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
// 	}// 	// 先声明一个 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
// 	}
// 	//发送 data,将其封装到函数中
// 	err = 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.Printf("消息类型不存在,无法处理")
// 	}
// 	return
// }// 处理和客户端的通信
func process(conn net.Conn) {//这里需要延时关闭conndefer conn.Close()//这里调用总控,创建一个processor := &Processor{Conn : conn,}err := processor.process2()if err != nil{fmt.Println("客户端和服务器端通讯的协程错误=err",err)return}// //循环客户端发送信息// for {// 	mes, err := readPkg(conn)// 	if err != nil {// 		if err == io.EOF {// 			fmt.Println("客户端退出,服务器端也退出..")// 			return// 		} else {// 			fmt.Println("readPkg err=", err)// 			return// 		}// 	}// 	//fmt.Println("mes=", mes)// 	err = serverProcessMes(conn, &mes)// 	if err != nil {// 		return// 	}// }}func main() {//提示信息fmt.Println("服务器[新的结构]在 8889 端口监听......")listen, err := net.Listen("tcp", "0.0.0.0:8889")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)}
}
4.2.2 day8/chatroom/server/main/processor.go
package main
import("fmt""net""day8/chatroom/common/message""day8/chatroom/server/process""day8/chatroom/server/utils""io"
)//先创建一个processor的结构体
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.Printf("消息类型不存在,无法处理")}return
}func (this *Processor) process2() (err error) {//循环客户端发送信息for {//创建一个 Transfer 实例完成读包的任务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}}
}
4.2.3 day8/chatroom/server/process/smsProcess.go
package process2
4.2.4 day8/chatroom/server/process/userProcess.go
package process2import("fmt""net""day8/chatroom/common/message""day8/chatroom/server/utils""encoding/json")type UserProcess struct{//Conn net.Conn
}
func (this *UserProcess) ServerProcessLogin(mes *message.Message) (err error) {//核心代码//先从mes 中取出 mes.Data,并直接反序列化成 LoginMesvar loginMes message.LoginMeserr = json.Unmarshal([]byte(mes.Data), &loginMes)if err != nil {fmt.Println("json.Unmarshal fail err=", err)return}// 先声明一个 resMesvar resMes message.MessageresMes.Type = message.LoginResMesType//再声明一个 LoginResMesvar loginResMes message.LoginResMes//如果用户id=100,密码=123456,认为合法,否则不合法if loginMes.UserId == 100 && loginMes.UserPwd == "123456" {//合法loginResMes.Code = 200} else {//不合法loginResMes.Code = 500loginResMes.Error = "该用户不存在,请注册再使用..."}//将 loginResMes 序列化data, err := json.Marshal(loginResMes)if err != nil {fmt.Println("json.Marshal fail", err)return}//将data赋值给resMesresMes.Data = string(data)//对 resMes 进行序列化,准备发送data, err = json.Marshal(resMes)if err != nil {fmt.Println("json.Marshal fail", err)return}//发送 data,将其封装到函数中//因为使用分层模式(mvc),我们先创建一个 Transfer 实例,然后读取tf := &utils.Transfer{Conn : this.Conn,}err = tf.WritePkg(data)return
}
4.2.5 day8/chatroom/server/utils/utils.go
package utils
import("fmt""net""day8/chatroom/common/message""encoding/binary""encoding/json""errors"
)
//这里将这些方法关联到结构体中
type Transfer struct{//分析它应该有哪些字段Conn net.ConnBuf [8096]byte // 这是传输时,使用缓冲
}func (this *Transfer) ReadPkg() (mes message.Message, err error) {//buf := make([]byte, 8096)// conn.Read 在 conn 没有被关闭的情况下,才会阻塞//如果客户端关闭了conn 就不会阻塞_, err = this.Conn.Read(this.Buf[:4])if err != nil {//err = errors.New("read pkg header error")return}//根据读到的  buf[:4] 转成一个 unit32 类型var pkgLen uint32pkgLen = binary.BigEndian.Uint32(this.Buf[0:4])n, err := this.Conn.Read(this.Buf[:pkgLen])if n != int(pkgLen) || err != nil {//err = errors.New("read pkg body error")return}//把 pkgLen 反序列化成  -> message.Messageerr = json.Unmarshal(this.Buf[:pkgLen], &mes)if err != nil {err = errors.New("json.Unmarshal error")return}return}func (this *Transfer) WritePkg(data []byte) (err error) {//先发送一个长度给对方//data是 我们要发送的消息,先发送 data 长度//由于 conn 接口的 Write 方法参数要求是 bytes 切片var pkgLen uint32pkgLen = uint32(len(data))//var buf [4]bytebinary.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
}

4.3 编译项目代码

go build -o server day8/chatroom/server/main
go build -o client day8/chatroom/client/

4.4 演示代码

./server 
服务器[新的结构]8889 端口监听......
等待客户端连接服务器......
./client 
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):

client

1
登录聊天室
请输入用户的id
100
请输入用户密码
123456
客户端,发送消息的长度=86,消息内容为: {"Type":"LoginMes","Data":"{\"UserId\":100,\"UserPwd\":\"123456\",\"UserName\":\"\"}"}
读取客户端发送的数据...
登录成功

server

等待客户端连接服务器......
客户端退出,服务器端也退出..
客户端和服务器端通讯的协程错误=err EOF

5、改进客户端代码结构

5.1 代码目录结构

echo $PWD
/Go-project/src/day8
tree
.
└── chatroom├── client│   ├── main│   │   └── main.go│   ├── model│   ├── process│   │   ├── server.go│   │   ├── smsProcess.go│   │   └── userProcess.go│   └── utils│       └── utils.go├── common│   └── message│       └── message.go└── server├── main│   ├── main.go│   └── processor.go├── model├── process│   ├── smsProcess.go│   └── userProcess.go└── utils└── utils.go14 directories, 11 files

5.2 代码

5.2.1 day8/chatroom/client/main/main.go
package mainimport ("day8/chatroom/client/process""fmt"
)var userId int
var userPwd stringfunc main() {var key intvar loop = truefor loop {fmt.Println("---------------欢迎登录多人聊天系统-------------------")fmt.Println("\t\t\t 1、登录聊天室")fmt.Println("\t\t\t 2、注册用户")fmt.Println("\t\t\t 3、退出系统")fmt.Println("\t\t\t 请选择(1-3):")fmt.Scanf("%d\n", &key)switch key {case 1:fmt.Println("登录聊天室")fmt.Println("请输入用户的id")fmt.Scanf("%d\n", &userId)fmt.Println("请输入用户密码")fmt.Scanf("%s\n", &userPwd)//完成登录//1.创建一个UserProcess的实例up := &process.UserProcess{}up.Login(userId, userPwd)//loop = falsecase 2:fmt.Println("注册用户")loop = falsecase 3:fmt.Println("退出系统")loop = falsedefault:fmt.Println("你的输入有误,请重新输入")}}//if key == 1 {//这里需要重新调用//因为使用了新的程序结构,因此我们创建//login(userId, userPwd)//err := login(userId, userPwd)// if err != nil {// 	fmt.Println("登录失败")// } else {// 	fmt.Println("登录成功")// }//} else if key == 2 {//	fmt.Println("进行用户注册的逻辑")//}
}
5.2.2 day8/chatroom/client/process/server.go
package processimport ("day8/chatroom/client/utils""fmt""net""os"
)func ShowMenu() {fmt.Println("----------恭喜xxx登录成功--------")fmt.Println("--------1、显示在线用户列表--------")fmt.Println("--------2、发送消息--------")fmt.Println("--------3、信息列表--------")fmt.Println("--------4、退出系统--------")var key intfmt.Scanf("%d\n", &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) {//创建一个transfer实例,不停的读取服务器发送的消息tf := &utils.Transfer{Conn: conn,}for {fmt.Println("客户端%s正在等待读取服务器发送的消息")mes, err := tf.ReadPkg()if err != nil {fmt.Println("tf.ReadPkg err=", err)return}//如果读取到消息,又是下一步处理逻辑fmt.Printf("mes=%v", mes)}
}
5.2.3 day8/chatroom/client/process/smsProcess.go
package process
5.2.4 day8/chatroom/client/process/userProcess.go
package processimport ("day8/chatroom/client/utils""day8/chatroom/common/message""encoding/binary""encoding/json""fmt""net"
)type UserProcess struct {
}func (this *UserProcess) Login(userId int, userPwd string) (err error) {//fmt.Printf("userId = %d userPwd = %s\n", userId, userPwd)//return nil//连接到服务器conn, err := net.Dial("tcp", "localhost:8889")if err != nil {fmt.Println("net.Dial err=", err)return}//延时关闭defer conn.Close()//准备通过 conn 发送消息给服务器var mes message.Messagemes.Type = message.LoginMesTypevar loginMes message.LoginMesloginMes.UserId = userIdloginMes.UserPwd = userPwd//将 loginMes 序列化data, err := json.Marshal(loginMes)if err != nil {fmt.Println("json.Marshal err=", err)return}//将data赋值给 message 结构体 Data 字段mes.Data = string(data)//将 mes 进行序列化data, err = json.Marshal(mes)if err != nil {fmt.Println("json.Marshal err=", err)return}//data是 我们要发送的消息,先发送 data 长度//由于 conn 接口的 Write 方法参数要求是 bytes 切片var pkgLen uint32pkgLen = uint32(len(data))var buf [4]bytebinary.BigEndian.PutUint32(buf[0:4], pkgLen)//发送长度n, err := conn.Write(buf[0:4])if n != 4 || err != nil {fmt.Println("conn.Write(bytes) fail", err)return}fmt.Printf("客户端,发送消息的长度=%d,消息内容为: %s\n", len(data), string(data))_, err = conn.Write(data)if err != nil {fmt.Printf("conn.Write(data) fail", err)return}//time.sleep(20*time.Second)//fmt.Println("休眠了20S")//这里还需要处理服务器返回的消息tf := &utils.Transfer{Conn: conn,}mes, err = tf.ReadPkg() //mes 就是if err != nil {fmt.Println("readPkg(conn) err=", err)return}//将 mes 的 data 部分反序列化成 LoginResMesvar loginResMes message.LoginResMeserr = json.Unmarshal([]byte(mes.Data), &loginResMes)if loginResMes.Code == 200 {//fmt.Println("登录成功")//这里我们还需要在客户端启动一个协程//该协程保持和服务器端的通讯,如果服务器有数据推送给客户端//则接收并显示在客户端的终端go serverProcessMes(conn)//1.显示登录成功后的菜单for {ShowMenu()}} else if loginResMes.Code == 500 {fmt.Println(loginResMes.Error)}return
}
5.2.5 day8/chatroom/client/utils/utils.go
package utils
import("fmt""net""day8/chatroom/common/message""encoding/binary""encoding/json""errors"
)
//这里将这些方法关联到结构体中
type Transfer struct{//分析它应该有哪些字段Conn net.ConnBuf [8096]byte // 这是传输时,使用缓冲
}func (this *Transfer) ReadPkg() (mes message.Message, err error) {//buf := make([]byte, 8096)// conn.Read 在 conn 没有被关闭的情况下,才会阻塞//如果客户端关闭了conn 就不会阻塞_, err = this.Conn.Read(this.Buf[:4])if err != nil {//err = errors.New("read pkg header error")return}//根据读到的  buf[:4] 转成一个 unit32 类型var pkgLen uint32pkgLen = binary.BigEndian.Uint32(this.Buf[0:4])n, err := this.Conn.Read(this.Buf[:pkgLen])if n != int(pkgLen) || err != nil {//err = errors.New("read pkg body error")return}//把 pkgLen 反序列化成  -> message.Messageerr = json.Unmarshal(this.Buf[:pkgLen], &mes)if err != nil {err = errors.New("json.Unmarshal error")return}return}func (this *Transfer) WritePkg(data []byte) (err error) {//先发送一个长度给对方//data是 我们要发送的消息,先发送 data 长度//由于 conn 接口的 Write 方法参数要求是 bytes 切片var pkgLen uint32pkgLen = uint32(len(data))//var buf [4]bytebinary.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
}

5.3 编译项目代码

go build -o server day8/chatroom/server/main
go build -o client day8/chatroom/client/main

5.4 演示代码

 ./server 
服务器[新的结构]8889 端口监听......
等待客户端连接服务器......
./client 
---------------欢迎登录多人聊天系统-------------------1、登录聊天室2、注册用户3、退出系统请选择(1-3):

client

1
登录聊天室
请输入用户的id
100
请输入用户密码
123456
客户端,发送消息的长度=86,消息内容为: {"Type":"LoginMes","Data":"{\"UserId\":100,\"UserPwd\":\"123456\",\"UserName\":\"\"}"}
----------恭喜xxx登录成功--------
--------1、显示在线用户列表--------
--------2、发送消息--------
--------3、信息列表--------
--------4、退出系统--------
客户端%s正在等待读取服务器发送的消息

server

等待客户端连接服务器......

client

1
显示在线用户列表
----------恭喜xxx登录成功--------
--------1、显示在线用户列表--------
--------2、发送消息--------
--------3、信息列表--------
--------4、退出系统--------
2
发送消息
----------恭喜xxx登录成功--------
--------1、显示在线用户列表--------
--------2、发送消息--------
--------3、信息列表--------
--------4、退出系统--------
3
信息列表
----------恭喜xxx登录成功--------
--------1、显示在线用户列表--------
--------2、发送消息--------
--------3、信息列表--------
--------4、退出系统--------
4
你选择退出了系统...

server

客户端退出,服务器端也退出..
客户端和服务器端通讯的协程错误=err EOF

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com