go 聊天系统项目-1

news2024/12/23 14:48:40

1、登录界面

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

1.1登录界面代码目录结构

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

1.2登录界面代码

main.go

package main

import (
	"fmt"
)

var userId int
var userPwd string

func main() {
	var key int
	var loop = true
	for 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 = false
		case 2:
			fmt.Println("注册用户")
			loop = false
		case 3:
			fmt.Println("退出系统")
			loop = false
		default:
			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 main

import (
	"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.go

6 directories, 4 files

2.2代码

2.2.1 day8/chatroom/common/message/message.go
package message

const (
	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 main

import (
	"fmt"
	"net"
)

// 处理和客户端的通信
func process(conn net.Conn) {
	//这里需要延时关闭conn
	defer 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 main

import (
	"fmt"
)

var userId int
var userPwd string

func main() {
	var key int
	var loop = true
	for 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 = false
		case 2:
			fmt.Println("注册用户")
			loop = false
		case 3:
			fmt.Println("退出系统")
			loop = false
		default:
			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 main

import (
	"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.Message
	mes.Type = message.LoginMesType
	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赋值给 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 uint32
	pkgLen = uint32(len(data))
	var buf [4]byte
	binary.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.go

6 directories, 5 files

3.2 代码

3.2.1 day8/chatroom/client/login.go
package main

import (
	"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.Message
	mes.Type = message.LoginMesType
	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赋值给 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 uint32
	pkgLen = uint32(len(data))
	var buf [4]byte
	binary.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 部分反序列化成 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
}
3.2.2 day8/chatroom/client/main.go
package main

import (
	"fmt"
)

var userId int
var userPwd string

func main() {
	var key int
	var loop = true
	for 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 = false
		case 2:
			fmt.Println("注册用户")
			loop = false
		case 3:
			fmt.Println("退出系统")
			loop = false
		default:
			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 main

import (
	"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 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[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 message

const (
	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 main

import (
	"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) {
	//这里需要延时关闭conn
	defer 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.go

10 directories, 9 files

4.2 代码

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

4.2.1 day8/chatroom/server/main/main.go
package main

import (
	// "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) {
	//这里需要延时关闭conn
	defer 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 process2

import(
	"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,并直接反序列化成 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,将其封装到函数中
	//因为使用分层模式(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.Conn
	Buf [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 uint32
	pkgLen = 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.Message
	err = 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 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
}

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.go

14 directories, 11 files

5.2 代码

5.2.1 day8/chatroom/client/main/main.go
package main

import (
	"day8/chatroom/client/process"
	"fmt"
)

var userId int
var userPwd string

func main() {
	var key int
	var loop = true
	for 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 = false
		case 2:
			fmt.Println("注册用户")
			loop = false
		case 3:
			fmt.Println("退出系统")
			loop = false
		default:
			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 process

import (
	"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 int
	fmt.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 process

import (
	"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.Message
	mes.Type = message.LoginMesType
	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赋值给 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 uint32
	pkgLen = uint32(len(data))
	var buf [4]byte
	binary.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 部分反序列化成 LoginResMes
	var loginResMes message.LoginResMes
	err = 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.Conn
	Buf [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 uint32
	pkgLen = 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.Message
	err = 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 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
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2229338.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Steam deck 倒腾日记 - 安装Windows软件,玩上黑神话悟空

Steam deck 倒腾日记 关于Steam Deck基本信息性能特点游戏兼容性 问题一: 软键盘输入问题二: 系统切换问题三: 安装运行Window 软件关于Proton如何运行 问题四: 优化网络问题黑神话.悟空PS参考 关于Steam Deck Steam Deck是一款由Valve开发的便携式游戏PC,它搭载了A…

【内网渗透】最保姆级的2022网鼎杯半决赛复盘打靶笔记

目录 flag1 flag2 flag3 flag4 flag1 fscan什么也没扫到 访问是个web dirsearch开扫 访问./wp-admin 弱口令admin:123456登录 编辑主题文件 在header.php中插入一句话木马 header.php位置:https://tw.godaddy.com/help/change-the-header-in-wordpress-264…

Python 实现斐波那契数列的方法

以下是使用 Python 实现斐波那契数列的方法&#xff1a; def fibonacci(n): if n < 1: return n else: return fibonacci(n - 1) fibonacci(n - 2) # 打印前 10 个斐波那契数 for i in range(10): print(fibonacci(i)) 在这个代码中&#xff0c;定义了一个函数 fibonacc…

IntelliJ IDEA 中上传项目到 Gitee 的完整指南

博主主页:【南鸢1.0】 本文专栏&#xff1a;git 目录 简介 1.插入intellij-gitee 2.导入下载插件 3.选择导航栏中的VCS->Share Project on Gitee 4.登录gitee 6.验证gitee仓库是否创建成功 7.上传分享项目 8.验证仓库代码是否上传成功 总结 简介 Gitee 是一个代码…

【p2p、分布式,区块链笔记 分布式容错算法】: 拜占庭将军问题+实用拜占庭容错算法PBFT

papercodehttps://pmg.csail.mit.edu/papers/osdi99.pdfhttps://github.com/luckydonald/pbft 其他相关实现&#xff1a;This is an implementation of the Pracltical Byzantine Fault Tolerance protocol using PythonAn implementation of the PBFT consensus algorithm us…

简单的kafkaredis学习之redis

简单的kafka&redis学习之redis 2. Redis 2.1 什么是Redis Redis是一种面向 “Key-Value” 数据类型的内存数据库&#xff0c;可以满足我们对海量数据的快速读写需求&#xff0c;Redis是一个 NoSQL 数据库&#xff0c;NoSQL的全称是not only sql&#xff0c;不仅仅是SQL&…

无人机之卫星通信技术篇

无人机的卫星通信技术是一种利用人造地球卫星作为中继站来转发无线电波&#xff0c;从而实现无人机与地面控制站之间通信的技术。 一、技术概述 卫星通信系统主要由通信卫星和经该卫星连通的地球站两部分组成。在无人机应用中&#xff0c;卫星通信技术能够确保无人机在全球范围…

网鼎杯 misc -好久不见4

不嘻嘻&#xff0c;没见过这种题&#xff0c;需要把这个红线还原重组成二维码&#xff0c;搜索一个是这个Peano曲线 from PIL import Image from tqdm import tqdmdef peano(n):if n 0:return [[0, 0]]else:in_lst peano(n - 1)lst in_lst.copy()px, py lst[-1]lst.extend(…

ARM base instruction -- adcs

Add with Carry, setting flags, adds two register values and the Carry flag value, and writes the result to the destination register. It updates the condition flags based on the result. 带进位加法&#xff0c;设置标志&#xff0c;将两个寄存器值和进位标志值相…

笔记本双系统win10+Ubuntu 20.04 无法调节亮度亲测解决

sudo add-apt-repository ppa:apandada1/brightness-controller sudo apt-get update sudo apt-get install brightness-controller-simple 安装好后找到一个太阳的图标&#xff0c;就是这个软件&#xff0c;打开后调整brightness&#xff0c;就可以调整亮度&#xff0c;可…

vue版本太低无法执行vue ui命令

连接 ui和create目前都只支持3.0以后得版本才能使用 https://blog.csdn.net/m0_67318913/article/details/136775252?utm_mediumdistribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-136775252-blog-121204604.235v43pc_blog_bottom_relevance…

萤石私有化设备视频平台EasyCVR视频融合平台如何构建农业综合监控监管系统?

现代农业的迅速发展中&#xff0c;集成监控管理系统已成为提高农业生产效率和优化管理的关键工具。萤石私有化设备视频平台EasyCVR&#xff0c;作为一个具有高度可扩展性、灵活的视频处理能力和便捷的部署方式的视频监控解决方案&#xff0c;为农业监控系统的建设提供了坚实的技…

Pr 视频效果:闪光灯

视频效果/风格化/闪光灯 Stylize/Strobe Light 闪光灯 Strobe Light效果可用于在视频中创建闪烁或频闪的效果&#xff0c;类似于舞台上的频闪灯或摄影中的闪光灯。 ◆ ◆ ◆ 效果选项说明 通过调整各种参数&#xff0c;可以自定义闪光的颜色、频率、持续时间和混合模式&#…

FreeRTOS确定任务栈大小

一、FreeRTOS内存分配 所有任务共用一个堆空间&#xff0c;所以当调用xPortGetFreeHeapSize这个函数时&#xff0c;返回的就是现在所有可用堆空间的消息 所有任务都有自己的栈空间&#xff0c;比如在任务中定义一个uint32_t data[100]的数组&#xff0c;此时调用uxTaskGetSt…

计算机毕业设计Hadoop+Spark+Hive抖音情感分析 抖音可视化 抖音舆情监测 预测算法 抖音爬虫 抖音大数据 情感分析 NLP 自然语言处理

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; HadoopSparkHive抖音情感分…

tcp shutdown, fin_wait1, fin_wait2, close_wait, last_ack, 谢特!

TCP 作为双向传输协议&#xff0c;如果你想只收不发&#xff0c;可以单向关掉发&#xff0c;shutdown(socket.SHUT_WR)&#xff0c;但不建议这么做。 看以下代码&#xff1a; #!/Users/zhaoya/myenv/bin/python3 # client import socketclient_socket socket.socket(socket.…

redis安装使用

1. 下载地址 :::color1 下载最新稳定版本的 redis-windows 7.x 版本(本实例以 7.2.3 为例) ::: # 下载地址 https://download.csdn.net/download/qq827245563/899238402. 解压文件 ![](https://img-blog.csdnimg.cn/img_convert/c094d561f7f8ed6e9d139d07be1271cb.png) 3. …

如果在 Ubuntu 24.04 上安装 Yarn ?

Yarn 是一种快速、可靠、安全的 JavaScript 项目依赖管理工具&#xff0c;它提供了比同类产品更好的缓存机制、网络性能和更直观的用户界面。作为现代 web 开发的基本工具&#xff0c;在系统上安装 Yarn 可以确保您可以有效地管理项目依赖关系。 他的文章将指导您通过 4 种有效…

【React 轮子】文本溢出后显示展开/收起按钮

/** hooks* 用于文本展示时判断是否展示 展开/收起按钮 &#xff08;包含监听 文本变化/页面尺寸变换&#xff09;* param { string } text 需要展示的文本* param { number } maxLength 文本最大展示行数* param { number } lineHeight 文本行高 (单位 px) */ import React, …

Python | Leetcode Python题解之第523题连续的子数组和

题目&#xff1a; 题解&#xff1a; class Solution:def checkSubarraySum(self, nums, k):d {0: -1}pre 0for index, num in enumerate(nums):pre numrem pre % ki d.get(rem, index)if i index:d[rem] indexelif i < index - 2:return Truereturn False