基于Golang的Ltalk聊天服务器设计与实现

基于Golang的服务器与基于Qt的跨平台客户端进行通信。

1.安装Golang

下载链接
根据你的PC情况安装对应版本,这里我选择的是window版本

2.配置Go代理

go env -w GOPROXY=https://goproxy.cn,direct

3.设计聊天服务器协议

syntax = "proto3";

package httpapi;

/*
*@brief:业务逻辑http服务器:

@release:
    *用户的uuid:用于查找该用户的信息
    *用户的token:用户所有上传操作的验证
    *资源的token:用于直接下载

@1.负责用户头像下载,上传 
    -下载时只需要资源token
    -上传时需要 用户校验token + 原资源token

@2.负责用户信息下载,上传
    -下载时需要用户对应token-
    -上传时需要
*/


enum SystemNotificationType//系统通知类型
{
    NotifAddFriend = 0;//有用户添加你为好友
    OtherClientLogin = 1;
}

message SystemNotificationEvent {
    SystemNotificationType type = 1;
    string content = 2;
}

message None {
}

/*****@上传/下载文件@*****/

enum LogicFileType{
    UserAvatar = 0;//头像
}



message SendFileInfoRequest {
    int32 filesize = 1;//文件大小
    string fileName = 2;//文件姓名
    string fileUuid = 3;//唯一标识符
    int32 fileBlockNum = 4;//如果不分片就为1
}

// 大文件要先发送SendFileInfoRequest,然后服务器响应SendFileInfoResponse
// 然后客户端发送UpLoadFileRequest,服务器收到后返回UpLoadFileResponse
message SendFileInfoResponse {
    int32 filesize = 1;//文件大小
    string fileName = 2;//文件姓名
    string fileUuid = 3;//唯一标识符
    int32 fileBlockNum = 4;//如果不分片就为1
}


message SendFileRequest {
    string fileUuid = 1;//唯一标识符
    string to = 2;
    int32 fileIndex = 3;
    bytes data = 4;
}

//上传文件需要指明用户 ->用户的uuid

message SendFileResponse {//上传文件
    string fileUuid = 1;//唯一标识符
    string res = 2;//成功上传返回token 失败返回0
}

//下载文件部分需指明用户 

message DownLoadFileRequest {
    string fileUuid = 1;
}


message DownLoadFileResponse {
    string fileUuid = 1;
    int64 totalSize = 2;
    uint64 seq = 3;
    bytes content = 4;
}

message UpLoadFileRequest {
    string fileName = 1;
    int64 totalSize = 2;
    uint64 seq = 3;
    bytes content = 4;
}

/********************/

/*****@登陆/注册/登出@*****/
/*
    there is only list sub two parts and more
    *kPassWordError | kNeedVerifCode

one way:1 index 1
the onther way: 0x1 | xx or 0x4 | xx
*/
enum LoginResult {
    LoginInvild = 0;
    PassWordError = 1;//passwd error
    LoginSuccess = 2;//login success
    AlreadLogin = 3;//client alread login in anther place
    AccountFreezing = 4;// account freezing now
    PassWordErrorManyTimes = 5;//passwd error too many times
    AccountNotExist = 6;//client not exist
    NeedVerifCode = 7;//verifCode client need verifcode and post data again
    //unkown error,call the client try it again
}

enum RegisterResult {
    RegisterInvild = 0;
    AccountExist = 1;
    RegisterSuccess =  2;
    RegusterFreezing = 3;
}
message BasicUserInfo{
    string name = 1;
    string account = 2;
}
message UserInfo {
    string uuid = 1;
    string name = 2;
    string phone = 3;
    uint32 sex = 4;
    string email = 5;
    string description = 6;
    string UserAvatar = 7;
    bytes UserAvatarContent = 8;//头像缩略图
}

message UserLoginRequest {
    string userAccount = 1;
    string userPassword = 2;
    string device = 3;//当前设备
    string version = 4;//客户端版本
}

message UserLoginResponse {
    string token = 1;//token
    LoginResult result = 2;//登陆结果
    UserInfo ownInfo = 3;//个人资料
}

enum RegisterType {//注册类型
    Phone = 0;
    Email = 1;
}

message UserRegisterRequest {
    RegisterType type = 1;//注册类型
    BasicUserInfo userInfo = 2; //注册信息
    string userPassword = 3;//用户密码
}

message UserRegisterResponse {
    RegisterResult result = 1;
    string uuid = 2;//注册结果 成功为uuid 失败为0
}

message UserLoginOutRequest {
    string userUuid = 1;//用户uuid
    string userToken = 2;//用户token
    string device = 3;//当前设备
}

message UserLoginOutResponse {
    string result = 1;//登出结果
}

/********************/

/*****@个人信息@*****/

//拉取个人用户信息
message PullUserInfoRequest {
    string userAccount = 1;
    uint64 updateTime = 2;
}

message PullUserInfoResponse {
    bool needUpdate = 1;
    UserInfo userInfo = 2;
}

message PushUserInfoRequest {
    string userAccount = 1;
    string token = 2;
    uint64 updateTime = 3;
    UserInfo userInfo = 4;
}

/********************/
enum DeviceType {
    Unknown = 0;
    Android = 1;
    IOS = 2;
    MacOS = 3;
    WatchOs = 4;
    Windows = 5;
}

enum NetType {
    _2G = 0;
    _3G = 1;
    _4G = 2;
    _5G = 3;
    Wifi = 4;
}


/*****@friend信息@*****/
message FriendInfo{
    UserInfo userInfo = 1;
    DeviceType decviceType = 2;
    bool online = 3;
}

message FriendInfoItem{
    string friendUuid = 1;
    string alias = 2;
    string group = 3;
    uint64 updateTime = 4;
}

message UserListResponse {
    //repeated c++ FriendInfo[]
    repeated FriendInfo friendInfos = 1;
}

message PullFriendsInfo{
    repeated FriendInfoItem friends = 1;
}

/********************/

//operation must be take token and uuid

/*****@find friend/add friend/delete friend信息@*****/
enum AddUserResponseType {
    AddUserInvild = 0;
    AddUserNotExist = 1;
    AddUserRefuse = 2;
    AddUserSuccess = 3;
    AlareadAddUser = 4;
}


message AddFriendMsg {
    enum AddFriendStatus {
        NotAddUser = 0;
        AddUserSuccess = 1;
        RefuseAddUser = 2;
        AlareadAddUser = 3;
    }
    UserInfo userInfo = 1;
    AddFriendStatus status = 2;
}

message UserEvent {
    enum UserEventType {
        AddUserRequest = 0;
        AddUserResponse = 1;
        DelFriendRequest = 3;
        FindUserRequest = 4;
        FindUserResponse = 5;
        ChangeUserRequest = 6;
        ChangeUserResponse = 7;
        CheckUserOnlineRequest = 8;
        CheckUserOnlineResponse = 9;
    }
    UserEventType type = 1;
    string from = 2;
    string eventContent = 3; // json
    UserInfo userInfo = 4;//如果有修改就返回该字段
}

/********************/

// 聊天
message ChatMessage {
  enum ContentType {
        Unknown = 0;
        Text = 1;
        Image = 2;
        Video = 3;
        Audio = 4;
        File = 5;
    };
    uint64 timestamp = 3; 
    bytes content = 4;
    ContentType type = 5;
    string from = 6;
    string to = 7;
    string filelocalPath = 8;
    string fileUuid = 9;
}

message ChatMessageRequest {
    string seq = 1;
    uint64 totalBlock = 2;// == 1 没有分片
    uint64 splitSeq = 3;// == 0 没有分片
    ChatMessage message = 4;
}

message ChatMessageResponse {
    string seq = 1;
}
//golang server
//c++ client
service HttpApi {
    rpc DownLoadFile(DownLoadFileRequest) returns (stream DownLoadFileResponse) {}
    rpc UpLoadFile(UpLoadFileRequest) returns (None) {}
    rpc Login(UserLoginRequest) returns (UserLoginResponse) {}
    rpc Register(UserRegisterRequest) returns (UserRegisterResponse) {}
    rpc UserList(None) returns (UserListResponse) {}
    rpc SendChatMessage(stream ChatMessageRequest) returns (stream ChatMessageResponse) {}
    rpc ChatMessageLoop(stream None) returns (stream ChatMessageRequest) {}
    rpc SendFile(stream SendFileRequest) returns (SendFileResponse) {}
    rpc sendFileInfo(SendFileInfoRequest) returns (SendFileInfoResponse) {}
    rpc UserEventLoop(stream UserEvent) returns (stream UserEvent) {}
    rpc PullUserEvent(UserEvent) returns (None) {}
    rpc SystemNotificationEventLoop(None) returns (stream SystemNotificationEvent) {}
}

4.使用proto工具生成api

protoc --go_out=plugins=grpc:. httpapi.proto

5.使用脚本创建自定义证书

  • 适用于localhost的服务器
#!/bin/sh

echo Generate CA key:
openssl genrsa -passout pass:1111 -des3 -out ca.key 2048

echo Generate CA certificate:
openssl req -passin pass:1111 -new -x509 -days 3650 -key ca.key -out ca.crt -subj "/C=CN/ST=FuJian/L=XiaMen/O=YaXon/OU=gRPC/CN=localhost"
 
echo Generate server key:
openssl genrsa -passout pass:1111 -des3 -out server_privatekey.pem 2048
 
echo Generate server signing request:
openssl req -passin pass:1111 -new -key server_privatekey.pem -out server_csr.pem -subj "/C=CN/ST=FuJian/L=XiaMen/O=YaXon/OU=gRPC/CN=localhost"
 
echo Self-sign server certificate:
openssl x509 -req -passin pass:1111 -days 3650 -in server_csr.pem -CA ca.crt -CAkey ca.key -CAcreateserial -out server_self_signed_crt.pem
 
echo Remove passphrase from server key:
openssl rsa -passin pass:1111 -in server_privatekey.pem -out server_privatekey.pem
 
echo Generate client key
openssl genrsa -passout pass:1111 -des3 -out client_privatekey.pem 2048
 
echo Generate client signing request:
openssl req -passin pass:1111 -new -key client_privatekey.pem -out client_csr.pem -subj "/C=CN/ST=FuJian/L=XiaMen/O=YaXon/OU=gRPC/CN=localhost"
 
echo Self-sign client certificate:
openssl x509 -passin pass:1111 -req -days 3650 -in client_csr.pem -CA ca.crt -CAkey ca.key -CAcreateserial -out client_self_signed_crt.pem
 
echo Remove passphrase from client key:
openssl rsa -passin pass:1111 -in client_privatekey.pem -out client_privatekey.pem