2.功能设计
(1)实现多用户同时登陆(并发)
(2)用户登陆需要进行认证,用户密码在传输过程中需要进行加密(MD5)
(3)用户可以获取聊天室列表
(4)用户可以获取聊天室里的用户的列表
(5)用户可以修改自己的密码和个人信息,用户的基本信息包括:用户账号、用户姓名、昵称、手机、邮箱、QQ、住址、地区、性别、年龄、生日
(6)用户有好友管理功能,包括添加、删除好友、获取好友列表
(7)用户有黑名单管理功能,包括添加、删除黑名单用户
(8)用户可以给指定的用户发送信息,也可以给当前聊天室所有的用户发送信息
(9)用户同一时刻只允许从一个地方进行登陆(当用户已登陆时,若该用户重复登陆,则断开前面已登陆的连接)
(10)用户可以在上线的时候接收其不在线时其他用户发送给他的消息
(11)当其他用户添加某用户为好友时,需要给该用户发送消息通知
(12)用户登陆、退出、修改用户资料、修改密码以及发送消息均需要进行记录数据库日志,方便查阅
3.系统设计
(1)服务器端在LINUX进行开发,使用开发语言C或C++
(2)服务器使用多线程来实现
(3)数据库操作接口需考虑周全,方便以后使用MYSQL或其它数据库
4.通讯协议
(1)所有的消息都由消息头+消息体组成
(2)消息头定义如下:
指令号(4字节整数) | 消息序号(4字节整数) | 会话号(4字节整数) | 消息体长度(4字节) |
消息序号:依次自动增加
会话号:发送者或者接收者的会话号
消息体长度:不包含消息头
每条消息的总长度不允许超过1024字节
(3)消息定义
序号 | 指令号 | 指令说明 | 定义 | 应答 |
1 | REQ_AUTH | 认证指令 | 0x00001 | 0x80001 |
2 | REQ_LIST_ROOM | 获取聊天室列表 | 0x00002 | |
3 | REQ_ENTER_ROOM | 进入某个聊天室 | 0x00003 | |
4 | REQ_ADD_FRIEND | 添加用户到好友或黑名单 | 0x00004 | |
5 | REQ_LIST_FRIEND | 列出好友和黑名单列表 | 0x00005 | |
6 | REQ_LIST_ROOM_USER | 列出某个聊天室的用户列表 | 0x00006 | |
7 | REQ_SEND_MESSAGE | 向某个或一组用户发送消息 | 0x00007 | |
8 | REQ_DEL_FRIEND | 从好友或黑名单中删除用户 | 0x00008 | |
9 | REQ_FRIEND_ONLINE | 好友上线通知消息 | 0x00009 | |
10 | REQ_PASSIVE_ADD | 添加好友通知 | 0x0000A | |
11 | REQ_GET_USERINFO | 获取用户信息 | 0x0000B | |
12 | REQ_UPDATE_USERINFO | 更新用户信息 | 0x0000C | |
13 | REQ_LEAVE_ROOM | 离开房间(会话组) | 0x0000D | |
13 | REQ_QUIT | 退出 | 0x00080 |
(1)认证请求消息体
Sruct AuthReq{
Char username[16]
Char code[8];
Char encryptStr[32];
};
加密步骤:
a)客户端通过gettimeofday函数获取当前电脑时间,组成8字节的一个随机串RANDON_KEY
b)对用户密码进行MD5(“密码”)加密,得到字符串MD5_STR
c)定义8字节的公共密钥PUBLIC_KEY=“0x19, 0x38, 0x11, 0x05, 0x86, 0xa3, 0xf7, 0x81”,与RANDON_KEY一起组成16字节的加密密钥FINAL_KEY
d)使用AES算法,对MD5_STR进行加密,使用密钥FINAL_KEY,ENCRYPT_STR=AES(FINAL_KEY, MD5_STR);
认证成功,服务器应答:
Struct AuthResp {
uint32_t sessionID;
}
(2)获取聊天室列表
获取聊天室列表请求无消息体
服务器应答:N个RoomInfo结构体
Struct RoomInfo {
int number; //房间编号,为数字
char roomName[32]; //房间名字
int capacity; //最大用户数
int userCount; //当前用户数
}
(3)进入房间(加入会话组)
请求的结构体
struct EnterRoom {
int roomID;
};
返回结构体:
struct EnterRoom {
int roomID;
};
(4)获取房间用户列表
请求:
struct ListRoomUser {
int roomID;
};
返回消息体:
房间号 | SessionInfo 1 | SessionInfo 2 | SessionInfo 3 | …. |
int sock; //用户套接字
char userName[32]; //用户名
char realName[32]; //用户昵称
uint32_t sessionID; //用户会话号
int status; //用户状态
int roomID; //房间号
}
(5)添加用户到好友或黑名单
(6)添加用户到好友或黑名单
(7)向某个用户或一组用户发送消息
请求消息体:
接收者类型 | 接收者 | 消息类型 | 消息时间 | 消息长度 | 消息内容 |
uint32_t receiptorType;
uint32_t receiptor;
uint32_t msgType;
uint32_t msgTime;
uint32_t msgLen;
char *msg; //最大长度不超过500字节
};
接收者类型:4字节,1-普通用户,2-群用户(如整个房间的人)
接收者:4字节整数,若为普通用户,则此处为接收者的用户ID,若为群用户,则填入群ID(如房间ID),当服务器向客户端发送消息时,此处接收者则为客户端对应的用户ID
消息类型:4字节,1表示私密信息,0表示一般消息
消息时间:4字节整数,为1900年开始到现在的秒数
消息长度:4字节整数,表示随后的消息内容的字节数,不能超过500字节
消息内容:要发送的消息的内容
(8)离开房间(离开会话组)
请求的结构体
struct EnterRoom {
int roomID;
};
返回:
struct EnterRoom {
int roomID;
};
(9)
6.错误号定义
错误号 | 说明 | |
0x0000001 | 用户名不存在 | |
0x0000002 | 用户密码不正确 | |
0x0000003 | 系统访问数据库失败 | |
0x0000004 | 用户无权限执行此操作 |
(1)Sqlite的互斥锁的问题
(2)队列访问互斥锁的问题
(3)线程允许打开的文件数问题
(4)线程堆栈空间分配问题
(5)信号处理问题
(6)
8.数据库设计
(1)用户表:user
字段名 | 字段类型 | 字段说明 | |
id | bigint | 自增长的ID,每个用户唯一 | 主键 |
name | Varchar() | 用户账号名,在表中唯一 | 索引列 |
password | Varchar() | 用户密码,使用md5加密保存 | |
nickname | Varchar() | 用户昵称 | |
realname | Varchar() | 用户真实姓名 | |
mobile | Varchar(20) | 用户手机号码 | |
province | Varchar(20) | 所在省份 | |
city | Varchar(20) | 所在城市 | |
Varchar() | 邮件地址 | ||
Varchar(20) | QQ号码 | ||
gender | Char(2) | 用户性别,’M’-男,’F’-女 | |
birth | Varchar(20) | 用户生日,格式为:yyyy-mm-dd | |
role | int | 用户级别,1-普通用户,2-VIP会员,3-钻石VIP会员,5-管理员 | |
status | int | 用户状态,0-未通过验证,1-正常,2-已注销 |
(2)房间表:room
字段名 | 字段类型 | 字段说明 | |
id | bigint | 自增长的ID,每个房间唯一 | 主键 |
name | Varchar() | 房间名称,在表中唯一 | 索引列 |
max_user | int | 房间允许的最大用户数 | |
cur_user | Int | 当前用户数 | |
字段名 | 字段类型 | 字段说明 | |
user_id | bigint | 用户的ID,与user表中的id对应 | 主键 |
sessionid | bigint | 用户会话号 | 索引列 |
sock_fd | int | 会话的套接字描述符 | |
room_id | Int | 用户所在房间的id,与room表的id对应 | |
start_time | Unsigned int | 会话开始的时间,为1900年到现在的秒数 |
字段名 | 字段类型 | 字段说明 | |
id | bigint | 自增长ID | 主键 |
user_name | Varchar(20) | 用户账号,与user表中的name对应 | 索引 |
action | int | 1-登陆,2-密码验证失败,3-退出,4-发送消息,5-添加好友,6-修改密码,7-修改资料,8-删除好友 | |
object | Varchar() | 事件操作的对象 | |
param | Varchar() | 用户事件的参数 | |
log_time | int | 记录日志的时间,为1900年到现在的秒数 |
9.