1 团队介绍
团队组成:
齐爽爽(258)个人博客:http://www.cnblogs.com/shuangshuangblog/
马帅(248)个人博客:http://www.cnblogs.com/whu-mashuai/
何健(267)个人博客:http://www.cnblogs.com/hankin2017/
蔡凯峰(285)个人博客:http://www.cnblogs.com/cai2017282110285/
团队Git链接:https://github.com/WHUSE2017/C-team
2 Bate过程回顾
2.1 团队项目目标
(1)预期典型用户:18--40岁在武汉的人,前期只在武汉大学,武大学生。
(2)预期功能描述:能够发布自己的活动行程、搜索到某时间某地点的活动及参加人员信息。
(3)预期用户数量:前期500人左右。
2.2 如何满足用户需求
只要是本系统的用户,我们都提供活动查询,不过是一件想好取得地点,或者没有想好干什么,但是有时间,都能在系统里面找到想要的信息,即使当时没有找到,你也可以自己做主,发布行程消息,等待其他人的加入。至少我们提供一个集中的平台,更易于找到想找到的人。
像正常软件一样,用户登录自己的账户,可以直接查看自己的活动行程,也可以点击取消自己的活动,当然,用户最好给系统一个反馈,好让系统的信息更准确。
2.3 分工与经验教训
我们分工依然比较粗暴的,
前台界面:何健;
逻辑层:蔡凯峰;
数据库设计与连接以及Alpha版展示与讲解:马帅;
文档编辑与组员协作:齐爽爽。
经验教训:
(1)面对面沟通:依然是加强沟通。因为这个周末,大家都有事情,所以小组会议移交在网上,还是没有面对面来的实在。
(2)还想用《构建之法》里面的那段:>scrum计划阶段的估计不是一个"合同",领导们不要把它当成一个合同。估计总是不准的。坚持短期的sprint,这样即使不准的估计也不会又打的损害。真的觉得敏捷的sprint还是很有效的。
2.4 项目管理
项目管理就是按照老师上课讲的和书上讲的大概中和一下,分工明确,各自领取任务,然后每天汇报。然后具体修改文档发到讨论组。
2.5 如何做到如期交付
有计划。主要在前期老师给了两周时间来做分析,把思路计划都已经做好了,所以在冲刺阶段,基本按照前期计划走,有一个计划在那里,所以每天看着燃尽图都有种“革命尚未成功,同志仍需努力”的紧迫感。
还有小组成员也都很负责。虽然老师任务也很重,但是能熬夜做项目,即使最后交付的东西还不够完善,但是至少能做到,既然咱们干这个事情,就要负责,在规定时间完成自己的任务。
3 燃尽图
在上次,是木真正的会使用leangoo,后期修改了时间,所以会有偏差,这次一开始就设置好了所有参数,所以还很很准确的显示了项目完成度,因为全部都没有在计划内完成..........于是在最后一天疯狂的解决问题。
4 留给后面小组的说明
项目文件:
文件说明:
1.SQLStruct.h
定义了与数据库操作相关的一些结构体:
struct userStruct; //用户信息 struct EventStruct; //事件信息 struct StationMessageStruct; //站内信 struct ParticipantsStrcut; //事件参与 struct SecretSecurityStruct; //密保
2.Operate.h/Operate.cpp
与数据库进行交互的类,类的定义如下:
class Operate { public: Operate(); string IntToString(int variable); int StringToInt(string variable); bool InsertIntoUserTable(userStruct User); bool InsertIntoEventTable(EventStruct Event); bool InsertIntoStationMessageable(StationMessageStruct StationMessage); bool UpdateUserTable(userStruct User);//修改用户信息 bool UpdatePassword(string UserName, string Password); //修改密码 bool InsertIntoSecretSecurity(SecretSecurityStruct Security); //添加密保 bool joinEvent(int EventID, string username); //加入活动 bool setEventState(int Eventid, int State);//设置活动状态 SecretSecurityStruct GetSecretSecurity(string UserName); //获取密保 bool DeleteDataParticipants(int EventID, string UserName);//删除Participants中特定数据 vector<userStruct> LikeUserName(string username);//模糊查找用户名 vector<ParticipantsStrcut> GetParticipantsByUsername(string Username); vector<EventStruct> getEventByCondition(string publisher, string participant, int state); //查看已发布,或已加入的活动 string GetPasswordFromUserTable(string username); userStruct GetUserDetails(string username); //获取用户信息 vector<EventStruct> GetEvent(string StartSite, string EndSite, string StartTime, string EventType); //搜索活动信息 vector<EventStruct> GetEventByState(int State);//通过活动状态查询活动 vector<EventStruct> GetEventByLike(string StartSite, string EndSite);//开始地和结束地模糊查询 vector<string> GetParticipants(int EventId);//获取活动的参与者 vector<StationMessageStruct> GetMessageBySender(string SenderName); vector<StationMessageStruct> GetMessageByReceiver(string ReceiverName); EventStruct GetEventDetailById(int EventId); public: MYSQL mydata; };
3.socket_stream.h/socket_stream.cpp
处理socket流数据的类,定义如下:
class socket_stream{ public: enum { disconnected, connected, }; //用来判断socket状态的enum值 socket_stream(char *ip,unsigned short port); //客户端构造函数,指定要连接 //的远程主机的ip,port socket_stream(SOCKET s,sockaddr_in * lpremoteAddr = NULL,int nAddrlen=0); //服务端构造函数,将监听到的socket连接传进来,剩下的两个参数 //可以用来获取socket连接的其他信息 ~socket_stream(); //析构函数,有些东西需要在这释放掉 int recvData(char *buffer, int buflen, int flag =0); //接收流数据 int sendData(char *buffer, int buflen, int flag =0); //发送流数据 int getState(); //获取当前连接的状态 private: void init_cs(); //CRITICAL_SECTION初始化CRITICAL_SECTION结构 SOCKET sock; int state; CRITICAL_SECTION send_cs; //这两个变量用来锁住对读写缓冲区的操作,避免多个线程同时 CRITICAL_SECTION recv_cs; //操作同一个socket连接的读写缓冲区 sockaddr_in remoteAddr; int nAddrlen; };
4.socket_packet.h/socket_packet.cpp
将socket流数据封装成packet。
enum { PACKET_LOGIN, PACKET_GETXX, PACKET_SETXX, }; #pragma pack (push,1) /*指定1字节对齐,有些编译器会补齐成4字节对齐,需要指定1字节对 齐,否则底层缓冲区可能对数据的长度判断出错*/ typedef struct { unsigned int type; //packet类型 unsigned int length; //整个packet的长度 unsigned int flag; //附加的标志 char data[1]; //数据 }packet_t; #pragma pack (pop) /*还原默认字节对齐*/ class socket_packet { public: enum { err_stream_check, err_stream_state=-4099, }; //在这一层出错时返回的错误标志 socket_packet(socket_stream *stream); //构造 ~socket_packet(); //析构,释放相应内存 int send_packet(packet_t *packet,int length); //发送一个packet int recv_packet(); //接收一个完整的packet,返回接收的标志 char *get_buffer(); //接收成功后,用该函数获取packet数据 private: bool check_buffer(packet_t *p); //检查本类中申请的buffer是否足够大,不足将继续申请 //内存 socket_stream *mStream; //封装在该层的底层类 char *buffer; //可变长的缓冲区 int bufLength; //缓冲区长度 };
5.session_s.h/session_s.cpp
一个session的服务端代码,用来获取用户请求和发送处理结果
class session_s { public: enum { disconected, have_request, }; //判断session的状态 session_s(socket_packet *sp); //构造 ~session_s(); //析构 bool login(char *username,char*pass); bool _register(void *p); int wait_request(); //等待用户请求 request_t *get_request(); //获取用户请求 bool send_reply(reply_t *reply); //发送回复 void login_success(char* name); //登录成功后设置相关标志 bool IsLogin(){return this->b_login;} //判断当前用户是否已经登录 void setSqlOperate(Operate * o){op=o;} //设置数据库操作类 char* getusername(){return username;} //登录成功后可用来获取当前用户名 Operate *op; private: bool b_login; char username[128]; socket_packet *sp; request_t request; char *reply_buffer; char reply_buffer_len; };
6.session_c.h/session_c.cpp
一个session的客户端代码,用来生成、发送用户请求,接收处理结果
class session_c { public: enum { disconected=-1, have_request=1, }; //session状态 session_c(socket_packet *sp); ~session_c(); bool login(char *params); bool _register(char* params); struct userStruct getUserInfo(char *params); int wait_reply(); //等待服务端回复 reply_t *get_reply(); //获取回复结果 bool SendAndCheck(request_t *req); //发送requeset并检查当前状态,等待回复 int getState(){return this->state;}; //获取当前session状态 bool addEvent(char* params); //添加活动 vector<StationMessageStruct> getStationMessage();//获取站内信 bool addMessage(char *params); //发送消息 vector<EventStruct> getEvent(char* params); //出发地,目的地,时间,活动类型获取活动 bool joinEvent(char * params); //参加某个活动 vector<string> getParticipants(char* params); //获取某个活动的所有参与者 EventStruct getEventByID(char* params); //获取某一个活动 bool updatauserinfo(char* params); //修改用户信息 bool exitEvent(char *params); //退出某个活动 bool setEventState(char *parmas); //设置活动的状态,进行中,完成,取消 vector<EventStruct> getEventByConditions(char* params);//获取已发布或已加入的活动 bool setSecurity(char* params); //设置密保 string getSecurity(char *params); //获取密保问题 string checkSecurity(char *params); //检查密保回答是否正确,返回密码 private: int send_request(request_t *req); //发送请求 int state; //当前session状态 socket_packet *sp; //封装的packet处理类 bool b_login; reply_t reply; char *buffer; int bufferlen; };
7.request_reply.h
以上两个session类中所需的一些定义:
enum { TYPE_LOGIN, TYPE_REGISTER, TYPE_GETUSERINFO, TYPE_ADDEVENT, TYPE_GETMESSAGE, TYPE_ADDMESSAGE, TYPE_GETEVENT, TYPE_JOINEVENT, TYPE_GETPARTICIPANTS, TYPE_GETEVENTDETAILBYID, TYPE_UPDATEUSERINFO, TYPE_EXITEVENT, TYPE_SETEVENTSTATE, TYPE_GETEVENTBYCONDITIONS, TYPE_SETSECURITY, TYPE_GETSECURITY, TYPE_CHECKSECURITY }; typedef struct{ int type; int flag; int datalen; char *data; }request_t; typedef struct{ int type; int flag; int datalen; char *data; }reply_t;
8.UserClient.h/UserClient.cpp
提供给客户端界面的接口类
class UserClient { public: UserClient(char *ip, unsigned short port); //连接服务端的ip,port bool Login(char *name,char* password); //登录 bool Register(struct userStruct userInfo); //注册 int getState(){return this->state;}; //获取session状态 bool reConnect(); //重连 bool fisrtConnect(); //第一次连接 struct userStruct getUserInfo(char* username); //获取用户信息 bool addEvent(EventStruct Event); //发布活动 bool sendMessage(StationMessageStruct message); //发送消息 vector<StationMessageStruct> getStationMessage(); //获取站内信 vector<EventStruct> getEvent(string StartSite, string EndSite, string StartTime,string EventType); //获取活动列表 bool joinEvent(int EventID, string username); //加入活动 vector<string> getParticipants(int EventID); //获取参与者列表 EventStruct getEventById(int EventID); //获取活动详情 void Logout(); //注销当前用户 bool UpdateUserInfo(struct userStruct userInfo); //修改用户信息 bool ExitEvent(int EventID,string username); //退出活动 bool SetEventState(int EventID,int state); //设置活动状态 vector<EventStruct> getEventByConditions(string publisher,string participant,int state); //获取已发布或已加入的活动 bool SetSecurity(string username,string sercurity,string answer); //设置密保 string GetSecurity(string username); //获取密保问题 string CheckSecurity(string username,string security,string answer); //检查密保回答是否正确,如果正确返回密码 //服务端会返回密码 private: int state; session_c* sc; //封装的session类 char mIp[16]; unsigned short mPort; char *buffer; int bufferlen; };
9.Service.h/Service.cpp
服务端服务代码
class Service { public: enum{ failed, success, }; //当前服务状态 Service(unsigned short port); //构造要监听的端口 ~Service(); //析构 int get_state(){return state;}; //获取当前服务状态 void start_loop(); //开始循环,等待连接 void setSqlOperate(Operate *o){op=o;} //设置数据库操作类 Operate *getSqlOperate(){return op;}; //获取数据库操作类 private: Operate *op; socket_packet *sp; int state; SOCKET slisten; void new_connect_handler(SOCKET); //每一个新连接都会生成一个新线程处理 };
10.serverthread.h
处理每个连接的线程逻辑代码
DWORD WINAPI myServerThread(LPVOID lpParam) { /*每一个连接都会有一个这样的处理线程*/ ServerThreadParam *lp =(ServerThreadParam*)lpParam; session_s *s = lp->session; while(1) { int state = s->wait_request(); if (state == s->disconected) break; request_t * req = s->get_request(); //获取请求 //根据不同的请求类型,调用不同的处理逻辑(service_xxx)。 switch(req->type) { case (TYPE_LOGIN): { service_login(s,req); break; } case(TYPE_REGISTER): { service_register(s,req); break; } case(TYPE_GETUSERINFO): { service_getuserinfo(s,req); break; } case(TYPE_ADDEVENT): { service_addevent(s,req); break; } case(TYPE_GETMESSAGE): { service_getmessages(s,req); break; } case(TYPE_ADDMESSAGE): { service_addmessage(s,req); break; } case(TYPE_GETEVENT): { service_getevent(s,req); break; } case(TYPE_JOINEVENT): { service_joinevent(s,req); break; } case(TYPE_GETPARTICIPANTS): { service_getparticipants(s,req); break; } case(TYPE_GETEVENTDETAILBYID): { service_geteventdetail(s,req); break; } case(TYPE_UPDATEUSERINFO): { service_updateuserinfo(s,req); break; } case(TYPE_EXITEVENT): { service_exitevent(s,req); break; } case(TYPE_SETEVENTSTATE): { service_seteventstate(s,req); break; } case(TYPE_GETEVENTBYCONDITIONS): { service_geteventbycondition(s,req); break; } case(TYPE_SETSECURITY): { service_setsecurity(s,req); break; } default: break; } } return 0; }