设为首页 加入收藏

TOP

我的Go gRPC之旅、03 简单控制台聊天室(一)
2023-07-23 13:28:05 】 浏览:75
Tags:gRPC 之旅 简单控 台聊天

效果

使用gRPC一元通信模式和双向流通信模式写一个简单的控制台聊天室。实现创建用户实时聊天两个功能,不考虑高性能。复习了内存同步访问Sync包的使用。用切片缓存聊天记录,新用户可以同步聊天记录。

image-20220920204845525

image-20220920204908759

PS C:\Users\小能喵喵喵\Desktop\Go\gRPC\chatroom> tree /f
├───client
│   │   go.mod
│   │   go.sum
│   │   main.go
│   │   
│   └───chatroom
│           chat_room.pb.go
│           chat_room_grpc.pb.go
│
├───proto
│   │   chat_room.pb.go
│   │   chat_room.proto
│   │   chat_room_grpc.pb.go
│   │
│   └───google
│       └───protobuf
│               wrappers.proto
│
└───server
    │   go.mod
    │   go.sum
    │   main.go
    │   service.go
    │
    └───chatroom
            chat_room.pb.go
            chat_room_grpc.pb.go
            server.code-workspace

Proto

syntax = "proto3";

import "google/protobuf/wrappers.proto";
package chatroom;
option go_package=".";

service ChatRoom{
  rpc login(User) returns(google.protobuf.StringValue);
  rpc chat(stream ChatMessage) returns(stream ChatMessage);
}

message User{
  string id = 1;
  string name = 2;
}

message ChatMessage{
  string id = 1;
  string name = 2;
  uint64 time = 3;
  string content = 4;
}
protoc --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative ./chat_room.proto

Server

service.go

package main

import (
	"context"
	"fmt"
	"sync"
	"time"

	"github.com/golang/protobuf/ptypes/wrappers"
	"github.com/google/uuid"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
	pb "wolflong.com/chatroom_server/chatroom"
)

// ^ 实现服务
type service struct {
	pb.UnimplementedChatRoomServer
	chatMessageCache []*pb.ChatMessage
	userMap          sync.Map
	L                sync.RWMutex
}

var (
	workers map[pb.ChatRoom_ChatServer]pb.ChatRoom_ChatServer = make(map[pb.ChatRoom_ChatServer]pb.ChatRoom_ChatServer)
)

// ^ 实现login用户注册方法
func (s *service) Login(ctx context.Context, in *pb.User) (*wrappers.StringValue, error) {
	in.Id = uuid.New().String()
	if _, ok := s.userMap.Load(in.Id); ok {
		return nil, status.Errorf(codes.AlreadyExists, "已有同名用户,请换个用户名")
	}
	s.userMap.Store(in.Id, in)
	go s.sendMessage(nil, &pb.ChatMessage{Id: "server", Content: fmt.Sprintf("%v 加入聊天室", in.Name), Time: uint64(time.Now().Unix())})
	// some work...
	return &wrappers.StringValue{Value: in.Id}, status.New(codes.OK, "").Err()
}

// ^ 实现聊天室
func (s *service) Chat(stream pb.ChatRoom_ChatServer) error {
	if s.chatMessageCache == nil {
		s.chatMessageCache = make([]*pb.ChatMessage, 0, 1024)
	}
	workers[stream] = stream
	for _, v := range s.chatMessageCache {
		stream.Send(v)
	}
	s.recvMessage(stream)
	return status.New(codes.OK, "").Err()
}

func (s *service) recvMessage(stream pb.ChatRoom_ChatServer) {
	md, _ := metadata.FromIncomingContext(stream.Context())
	for {
		mes, err := stream.Recv()
		if err != nil {
			s.L.Lock()
			delete(workers, stream)
			s.L.Unlock()
			s.userMap.Delete(md.Get("uuid")[0])
			fmt.Println("某个用户掉线,目前用户在线数量", len(workers))
			break
		}
		s.chatMessageCache = append(s.chatMessageCache, mes)
		v, ok := s.userMap.Load(md.Get("uuid")[0])
		if !ok {
			fmt.Println("致命错误,用户不存在")
			return
		}
		mes.Name = v.(*pb.User).Name
		mes.Time = uint64(time.Now().U
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇我的Go gRPC之旅、02 四种通信模式 下一篇go语言实现的定时任务管理系统 go..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目