服务器代码
proto文件
syntax = "proto3";
package login;
option go_package = "../../gate/api/login";
message User {
string username = 1;
string password = 2;
string roles = 3;
int32 status = 4;
int64 created_time = 5;
int64 updated_time = 6;
}
message SignupRequest {
string username = 1;
string password = 2;
}
message SigninRequest {
string username = 1;
string password = 2;
}
message LoginResponse {
int64 code = 1;
int64 user_id = 2;
string msg = 3;
}
service UserService {
rpc Signup(SignupRequest) returns (LoginResponse);
rpc Signin(SigninRequest) returns (LoginResponse);
}
代码生成命令
protoc --go_out=. --go-grpc_out=. --grpc_out=. --cpp_out=. --plugin=protoc-gen-grpc=你的\.build\Release\grpc_cpp_plugin.exe .\login.proto
Go 控制层代码逻辑
package login
import (
"ask_server/services"
"context"
"github.com/sirupsen/logrus"
)
var Server = newLoginServer()
func newLoginServer() *loginServer {
return &loginServer{}
}
type loginServer struct {
*UnimplementedUserServiceServer
}
func (s *loginServer) mustEmbedUnimplementedUserServiceServer() {
}
func (s *loginServer) Signup(ctx context.Context, req *SignupRequest) (*LoginResponse, error) {
user, err := services.UserService.SignUp(req.Username, req.Password)
if err != nil {
logrus.Errorf("Signup Err : [%s]", err.Error())
return &LoginResponse{Code: -1, Msg: err.Error()}, nil
}
return &LoginResponse{
Code: 0,
UserId: user.Id,
}, nil
}
func (s *loginServer) Signin(ctx context.Context, req *SigninRequest) (*LoginResponse, error) {
user, err := services.UserService.SignIn(req.Username, req.Password)
if err != nil {
logrus.Errorf("Signin Err : [%s]", err.Error())
return &LoginResponse{Code: -1, Msg: err.Error()}, nil
}
return &LoginResponse{
Code: 0,
UserId: user.Id,
}, nil
}
Go服务层代码逻辑
// SignUp 注册
func (s *userService) SignUp(username, password string) (*model.User, error) {
username = strings.TrimSpace(username)
// 验证用户名
if len(username) > 0 {
if err := validate.IsUsername(username); err != nil {
return nil, err
}
if s.isUsernameExists(username) {
return nil, errors.New(username + " 已被占用")
}
}
user := &model.User{
Username: sqls.SqlNullString(username),
Password: passwd.EncodePassword(password),
Status: constants.StatusOk,
CreatedTime: dates.NowTimestamp(),
UpdatedTime: dates.NowTimestamp(),
}
err := repo.UserRepo.Create(sqls.DB(), user)
if err != nil {
return nil, err
}
return user, nil
}
// SignIn 登录
func (s *userService) SignIn(username, password string) (*model.User, error) {
if len(username) == 0 {
return nil, errors.New("用户名不能为空")
}
if len(password) == 0 {
return nil, errors.New("密码不能为空")
}
if err := validate.IsPassword(password); err != nil {
return nil, err
}
var user *model.User = nil
user = s.GetByUsername(username)
if user == nil || user.Status != constants.StatusOk {
return nil, errors.New("用户名不存在或被禁用")
}
if !passwd.ValidatePassword(user.Password, password) {
return nil, errors.New("用户名或密码错误")
}
return user, nil
}
// GetByUsername 根据用户名查找
func (s *userService) GetByUsername(username string) *model.User {
return repo.UserRepo.GetByUsername(sqls.DB(), username)
}
Go数据层代码逻辑(gorm)自行百度~
func (r *userRepo) GetByUsername(db *gorm.DB, username string) *model.User {
return r.Take(db, "username = ?", username)
}
func (r *userRepo) Create(db *gorm.DB, t *model.User) (err error) {
err = db.Create(t).Error
return
}
客户端代码
LoginSerice.h
#pragma once
#include <grpcpp/channel.h>
#include "Proto/Login/login.grpc.pb.h"
using namespace login;
DECLARE_DELEGATE_TwoParams(FOnUsereDelegate, const bool&, const int64&);
class FLoginReactor
{
public:
explicit FLoginReactor(UserService::Stub* InServiceStub);
void Signin(const FString Username, const FString Password);
void Signup(const FString Username, const FString Password);
FOnUsereDelegate OnReadReceive;
protected:
UserService::Stub* ServiceStub;
TArray<grpc::ClientContext*> ClientContexts;
std::shared_ptr<SigninRequest> SigninReq;
std::shared_ptr<LoginResponse> LoginResp;
std::shared_ptr<SignupRequest> SignupReq;
};
class ASK_CLIENT_API FLoginService
{
public:
void Start();
void Stop();
FOnUsereDelegate& GetCallback() const {
return LoginReactor->OnReadReceive;
};
void Signin(const FString& Username, const FString& Password) const
{
LoginReactor->Signin(Username, Password);
};
void Signup(const FString& Username, const FString& Password) const
{
LoginReactor->Signup(Username, Password);
};
protected:
std::unique_ptr<UserService::Stub> ServiceStub;
std::unique_ptr<FLoginReactor> LoginReactor;
private:
std::shared_ptr<grpc::Channel> Channel;
public:
FORCEINLINE void SetChannel(const std::shared_ptr<grpc::Channel>& Chan) { Channel = Chan;}
};
LoginService.cpp
#include "Sys/Login/LoginService.h"
FLoginReactor::FLoginReactor(UserService::Stub* InServiceStub)
{
ServiceStub = InServiceStub;
SigninReq = std::make_shared<SigninRequest>();
LoginResp = std::make_shared<LoginResponse>();
SignupReq = std::make_shared<SignupRequest>();
}
void FLoginReactor::Signin(const FString Username, const FString Password)
{
auto context = new grpc::ClientContext();
ClientContexts.Add(context);
SigninReq->set_username(TCHAR_TO_UTF8(*Username));
SigninReq->set_password(TCHAR_TO_UTF8(*Password));
ServiceStub->async()->Signin(context, SigninReq.get(), LoginResp.get(), [this, context](grpc::Status Status)
{
int32 index = ClientContexts.Find(context);
delete ClientContexts[index];
ClientContexts.RemoveAt(index);
bool isSuccess = Status.ok();
int64 user_id;
if (isSuccess)
{
user_id = LoginResp->user_id();
AsyncTask(ENamedThreads::GameThread, [=]()
{
OnReadReceive.ExecuteIfBound(isSuccess, user_id);
});
}
else
{
AsyncTask(ENamedThreads::GameThread, [=]()
{
OnReadReceive.ExecuteIfBound(false, 0);
});
}
});
}
void FLoginReactor::Signup(const FString Username, const FString Password)
{
auto context = new grpc::ClientContext();
ClientContexts.Add(context);
SignupReq->set_username(TCHAR_TO_UTF8(*Username));
SignupReq->set_password(TCHAR_TO_UTF8(*Password));
ServiceStub->async()->Signup(context, SignupReq.get(), LoginResp.get(), [this, context](grpc::Status Status)
{
int32 index = ClientContexts.Find(context);
delete ClientContexts[index];
ClientContexts.RemoveAt(index);
bool isSuccess = Status.ok();
int64 user_id;
if (isSuccess)
{
user_id = LoginResp->user_id();
}
else
{
user_id = 0;
}
AsyncTask(ENamedThreads::GameThread, [=]()
{
OnReadReceive.ExecuteIfBound(isSuccess, user_id);
});
});
}
void FLoginService::Start()
{
ServiceStub = UserService::NewStub(Channel);
LoginReactor = std::make_unique<FLoginReactor>(ServiceStub.get());
}
void FLoginService::Stop()
{
LoginReactor.release();
ServiceStub.release();
}
LoginWidget.h
// uecosmic
#pragma once
#include "CoreMinimal.h"
#include "Widget/AskWidgetBase.h"
#include "LoginWidget.generated.h"
UCLASS()
class ASK_CLIENT_API ULoginWidget : public UAskWidgetBase
{
GENERATED_BODY()
public:
virtual void NativeConstruct() override;
UFUNCTION()
void OnHandleLoginClicked();
UFUNCTION()
void OnHandleRegisterClicked();
UFUNCTION()
void OnLoginClicked();
UFUNCTION()
void OnRegistryClicked();
private:
UPROPERTY(meta = (BindWidget))
class UButton* BtnLogin;
UPROPERTY(meta = (BindWidget))
class UButton* BtnBackLogin;
UPROPERTY(meta = (BindWidget))
class UButton* BtnRegistry;
UPROPERTY(meta = (BindWidget))
class UButton* BtnGoRegistry;
UPROPERTY(meta = (BindWidget))
class UEditableTextBox* TB_Username;
UPROPERTY(meta = (BindWidget))
class UEditableTextBox* TB_Password;
};
LoginWidget.cpp
// uecosmic
#include "Widget/LoginWidget.h"
#include "Components/Button.h"
#include "Components/EditableTextBox.h"
#include "Sys/AskGrpcSubsystem.h"
void ULoginWidget::NativeConstruct()
{
Super::NativeConstruct();
if (BtnLogin != nullptr)
{
BtnLogin->OnClicked.AddDynamic(this, &ULoginWidget::OnHandleLoginClicked);
}
if (BtnRegistry != nullptr)
{
BtnRegistry->OnClicked.AddDynamic(this, &ULoginWidget::OnHandleRegisterClicked);
}
if (BtnBackLogin != nullptr)
{
BtnBackLogin->OnClicked.AddDynamic(this, &ULoginWidget::OnLoginClicked);
}
if (BtnGoRegistry != nullptr)
{
BtnGoRegistry->OnClicked.AddDynamic(this, &ULoginWidget::OnRegistryClicked);
}
}
void ULoginWidget::OnLoginClicked()
{
BtnLogin->SetVisibility(ESlateVisibility::Visible);
BtnBackLogin->SetVisibility(ESlateVisibility::Hidden);
BtnRegistry->SetVisibility(ESlateVisibility::Hidden);
BtnGoRegistry->SetVisibility(ESlateVisibility::Visible);
}
void ULoginWidget::OnRegistryClicked()
{
BtnLogin->SetVisibility(ESlateVisibility::Hidden);
BtnBackLogin->SetVisibility(ESlateVisibility::Visible);
BtnRegistry->SetVisibility(ESlateVisibility::Visible);
BtnGoRegistry->SetVisibility(ESlateVisibility::Hidden);
}
void ULoginWidget::OnHandleLoginClicked()
{
const FString Username = TB_Username->GetText().ToString();
const FString Password = TB_Password->GetText().ToString();
const UAskGrpcSubsystem* Ass = GetGameInstance()->GetSubsystem<UAskGrpcSubsystem>();
check(Ass);
Ass->GetLoginService()->Signin(Username, Password);
}
void ULoginWidget::OnHandleRegisterClicked()
{
const FString Username = TB_Username->GetText().ToString();
const FString Password = TB_Password->GetText().ToString();
const UAskGrpcSubsystem* Ass = GetGameInstance()->GetSubsystem<UAskGrpcSubsystem>();
check(Ass);
Ass->GetLoginService()->Signup(Username, Password);
}
AskGrpcSubsystem.h(GrpcWrapper 插件在商城)
// uecosmic
#pragma once
#include "CoreMinimal.h"
#include "GrpcWrapperSubsystem.h"
#if PLATFORM_WINDOWS
#include "Windows/AllowWindowsPlatformAtomics.h"
#include "Windows/PreWindowsApi.h"
// Add native Windows/gRPC headers here
#include "Login/LoginService.h"
#include "Windows/PostWindowsApi.h"
#include "Windows/HideWindowsPlatformAtomics.h"
#endif
#include "AskGrpcSubsystem.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FOnUserIdReveiveMessage, const bool, IsSuccess,const int64, UserId);
UCLASS()
class ASK_CLIENT_API UAskGrpcSubsystem : public UGrpcWrapperSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
protected:
std::shared_ptr<grpc::Channel> channel;
private:
TUniquePtr<FLoginService> LoginService;
public:
UPROPERTY(BlueprintAssignable)
FOnCharacterReveiveMessage OnCharacterReveiveMessage;
FORCEINLINE FLoginService* GetLoginService() const { return LoginService.Get();}
};
AskGrpcSubsystem.cpp
// uecosmic
#include "Sys/AskGrpcSubsystem.h"
#include <grpcpp/security/credentials.h>
#include "Game/AskGameInstance.h"
void UAskGrpcSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
const FString& ServerUrl = "127.0.0.1:8009";
const FString& Certificate = "";
const FString& SslHostName = "";
UE_LOG(LogTemp, Log, TEXT("StartService: ServerUrl=%s, Certificate=%s, SslHostName=%s"), *ServerUrl, *Certificate, *SslHostName);
std::shared_ptr<grpc::ChannelCredentials> credentials;
if (Certificate.IsEmpty())
{
credentials = grpc::InsecureChannelCredentials();
}
else
{
grpc::SslCredentialsOptions option;
option.pem_root_certs = TCHAR_TO_UTF8(*Certificate);
credentials = grpc::SslCredentials(option);
}
grpc::ChannelArguments channelArgs;
channelArgs.SetSslTargetNameOverride(TCHAR_TO_UTF8(*SslHostName));
std::shared_ptr<grpc::Channel> Chan = grpc::CreateCustomChannel(TCHAR_TO_UTF8(*ServerUrl), credentials, channelArgs);
if (!LoginService.IsValid())
{
LoginService = MakeUnique<FLoginService>();
LoginService->SetChannel(Chan);
LoginService->Start();
LoginService->GetCallback().BindLambda(
[this](const bool& IsSuccess, const int64& User_id)
{
if (IsSuccess)
{
UAskGameInstance* Agi = CastChecked<UAskGameInstance>(GetGameInstance());
Agi->UserId = User_id;
}
OnUserIdReveiveMessage.Broadcast(IsSuccess, User_id);
});
}
}
void UAskGrpcSubsystem::Deinitialize()
{
Super::Deinitialize();
if (LoginService.IsValid())
{
LoginService->Stop();
LoginService = nullptr;
}
}
蓝图
一会发