完整word版网络编程基于TCP的简易聊天室实验报告.docx
《完整word版网络编程基于TCP的简易聊天室实验报告.docx》由会员分享,可在线阅读,更多相关《完整word版网络编程基于TCP的简易聊天室实验报告.docx(52页珍藏版)》请在冰豆网上搜索。
完整word版网络编程基于TCP的简易聊天室实验报告
网络编程
课程设计说明书
题目名称:
基于TCP/IP的网络文字聊天程序设计
专业:
网络工程
班级:
1201班
学号:
1220140105
姓名:
蒋星
指导老师:
陈利平
完成日期:
2014年12月13日
一、实验基本信息概要
1.题目要求
1、开发平台
VisualC++6.0。
2、对设计方法的要求
使用VisualC++6.0开发出在Windows控制台或图形界面下运行的程序。
3、对设计内容的要求
1实现网络文字聊天程序的服务器端。
2实现网络文字聊天程序的客户端。
3主程序的结构和流程。
4程序运行过程的截图。
5网络文字聊天程序的实现原理。
6网络文字聊天程序实现代码的分析。
4、对课程设计说明书的要求
①设计思路与开发过程。
②对主要代码段要有较详细的注释。
③对本次设计的评价、设计的收获与建议。
5、说明书为打印件
2.题目内容
内容概要:
实现网络文字聊天程序的基本功能。
3.开发环境
操作系统:
Windows7
开发语言:
C++
集成开发环境:
MicrosoftVisualStudio2010
二、流程图
三、系统简介
1.界面
本软件使用DOS控制台界面,界面风格较为朴素。
服务器:
客户端:
2.软件功能
本软件实现了聊天室基本的功能,包括公开聊天,私聊,获取在线用户,更改昵称,获得帮助等。
1)公开聊天
在光标处直接输入消息后按回车即为发送公开聊天,如下图所示。
2)私聊
使用命令【/m对方UID消息】即可发送私聊,私聊只有对方可以看到,如下图所示:
客户端1,密聊UID为132的用户。
发送后
客户端2,UID为132的用户收到私聊消息。
3)获取在线用户列表
使用命令【/list】即可获得在线用户列表,用户列表会议系统消息的方式返回,如下图所示。
命令
4)更改昵称
使用命令【/name你的新昵称】即可立即更改昵称,成功修改后服务器会以系统消息的方式返回成功修改的提示。
命令发送后
5)帮助信息
使用命令【/help】即可查看服务器的欢迎信息,里面包含了该聊天室的使用帮助,如下图所示。
3.系统设计
开发本软件时,我使用了面向对象的思想,把服务器和客户端封装成对应的类,类设计将会在下一节做详细介绍。
通行方面我在服务器接受客户端消息,和客户端接受服务器消息时使用了select模型,发送信息我使用的是普通的socket原语。
基本原理为服务器与客户端建立TCP连接,然后服务器负责路由消息到各个客户端。
4.优点与缺点
本软件对流程复杂的SELECT模型进行了细致的拆分与抽象,做到了逻辑流程清晰,每个函数简洁易懂,层次分明。
例如服务器启动函数:
voidChatServer:
:
Start()
{
InitListenSocket();
Bind();
Listen();
InitFDSocket();
while(true)
DoSelect();
}
try
charServer.Start();
catch(SockExceptione)
cout<cout<<"[SystemError]ErrorCode:"<}它其实就完成了一个简单的流程,初始化socket,绑定,监听,初始化fd_socket集合,死循环调用select。通过合理的封装底层原语和加入异常处理(异常交给顶层处理),使得代码专注于业务流程而不是繁杂的异常判断语句,在看下面这个函数DoSelect()。voidChatServer::DoSelect(){m_fdRead=m_fdSocket;intnRet=Select();if(nRet>0){for(inti=0;i{DoFDRead(m_fdRead.fd_array[i]);}}}它也只完成一个简单的流程,调用select,然后循环处理有读事件的socket。voidChatServer::DoRead(SOCKETsRead){if(sRead==m_sListen){RecvNewConnect();}else{m_sNowClient=sRead;RecvNewConnect();}}接下来的DoFDRead()函数完成的事情也非常直接,如果有事件的socket是监听socket的话,那么就是接收到了一个新的连接,否则是接收到了新的小。从上面这个简单的例子中可以看到,本软件最大的优点就是精心设计的类和函数。避免了使用select模型常见的反复嵌套的循环和判断,每个函数清晰明了。本系统还存在以下不足,首先是没有对界面做更深入的优化,只是做了最基本的调整,让输入输出更加雅观,其次是底层原语的封装并没有考虑到泛用性。四、系统详细设计这部分的文档在编码之前已经基本完成,由于时间较为仓促,部分内容可能和实际有所出入。1.ChatServer类该类负责完成服务器所有操作。1)类图2)成员变量Mapm_clients聊天者的SOCKET与昵称的映射fd_setm_fdSocket可用套接字集合fd_setm_fdRead有事件发生的套接字集合SOCKETm_sListen监听SocketSOCKETm_sNowClient当前处理的客户套接字intm_nPort监听端口3)方法设计voidBind()voidListen()voidSelect()intRecv()SOCKETAccept()封装底层原语,并加入异常机制,使得外部调用简约明了。构造函数传入监听端口,初始化m_nPortStart()1)初始化监听套接字:voidInitListenSocket()2)绑定套接字至本地机器:voidBind()3)进入监听模式(设置为非阻塞):voidListen()4)初始化可用套接字集合voidInitFDSocket()5)死循环,调用select方法DoSelect()6)结束DoSelect()1)令m_fdRead=m_fdSocket2)调用Select()3)循环处理Select的结果DoFdRead(SocketsRead)4)结束DoFdRead(intiReadIndex)1)判断是否为m_sListen2)是m_sListenRecvNewConnect()3)否则令m_sNowClient=m_fdRead[iReadIndex],调用RecvNewMessage()RecvNewConnect()1)判断是否达到套接字上线2)调用Accept(),接收连接sClient3)添加sCilent至m_fdSocket4)添加套接字至m_clientsAddClientToInfoMap(stringname)AddClientToInfoMap(stringname)1)以SOKCET为键,name为值加入MAPRecvNewMessage()1)调用Recv函数2)是否为命令IsCommand(stringstr)3)是,则DoCommand(stringcmd)4)否,则DoMessage(stringmsg)5)结束IsCommand(stringstr)1)判断是否以"/"开头DoCommand(stringcmd)1)判断指令,并解析命令与参数(argc,argv)2)调用指令处理函数3)假设只有SetName命令,那么则将对应的套接字的名称设置DoMessage(stringmsg)1)拼接消息与名字BuildMsg(stringmsg)2)在服务器上输出3)消息路由DispatchMessage(stringmsg)BuildMsg(stringmsg)1)从m_clients中取出用户昵称2)拼接字符串,形成格式如下超人君(127.0.0.1)23:49:48说:大家好!即为:昵称(IP地址)时间说:消息正文3)返回DispatchMessage(stringmsg)1)构造迭代器2)遍历m_clients,若不是自身,则派送消息Send()2.ChatClient类该类负责处理客户端的所有操作。1)类图2)字段设计SOCKETm_sClient客户端自身的socketSOCKETm_sServer服务器socketstringm_name昵称sockaddr_inm_ServerAddr;服务器地址3)方法设计构造函数根据端口号和服务器IP初始化m_serverConnect()voidSelect()intRecv()voidSend()intSelect()封装底层原语,加入异常处理,使得外部调用节约优雅voidStart()1)初始化套接字InitClientSocket()2)连接服务器Connect()设置为非阻塞模式3)获取名字并发送至服务器InitName()4)创建新线程并显示替他用户发言线程函数RecvMsgThread()5)循环SendMsg()6)关闭客户端CloseClient()InitName()1)提示输入昵称2)获取昵称3)合法性判断判断重复4)添加命令格式5)发送至服务器SendMsg()1)读取一行消息2)判断是否为命令IsCommand(stringstr)3)命令:处理命令DoCommand(stringcmd)4)消息:处理消息DoMessage(stringmsg)DoMessage(stringmsg)1)发送消息Send()2)本地回显RecvMsgThread()1)初始化fdSocket,将m_sClient加入2)创建fdRead3)死循环,将m_sClient拷贝至fdRead4)调用Select5)循环,并输出收到的消息Recv()3.SocketException类该类负责记录SOKCET错误的代码以及错误信息。5.命令协议命令格式为/命令参数1参数21.退出:/exit2.获取在线用户列表:/getuser3.私聊:/mUID信息4.清屏:/clear5.帮助:/help处理方式IsCommand(stringstr)负责解析是否为命令判断首字母是否为斜杠"/"str.at(0)=='/'ResoveCommand(stringcmd,int&argc,stringargv[])若是命令将命令解析为argc,argvDoCommand(stringcmd)处理命令,调用具体的XXX命令处理函数DoCmdXXXX()。6.消息格式1)公共消息超人君(127.0.0.1)UID:100说:大家好!李四(127.0.0.1)UID:101说:你好!!2)私聊你悄悄地对ABCUID:100说:你好CDFUID:101悄悄地对你说:你好3)服务器消息【系统消息】XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。4)程序内部提示[SystemInfo]xxxxxxxxxxxxxxxxxxxxxxxxx五、系统测试1.服务器使用错误2.客户端使用错误3.启动服务器4.启动客户端客户端出现欢迎信息以及昵称输入提示。服务器出现连接提示5.关闭客户端服务器出现断开连接提示6.启动服务器错误提示给出错误提示信息和提示代码7.公开聊天所有客户端以及服务器都会显示。8.私聊只有私聊的二人才能看到聊天信息,其他用户和服务器无法看到。9.错误的私聊私聊自己会得到一个错误提示私聊不存在的用户也会得到一个错误提示10.更名11.帮助12.非法指令非法指令会给出错误提示。13.非法的指令参数14.连接服务器失败六、心得体会这次实现我深入研究了select模型的使用,完成了一个简易的聊天室。这次试验也使我在编程技巧方面也有了很大的提高。七、完整代码Charserverd.cpp服务器main函数文件#include"ChatServer.h"#include"SockException.h"#include"InitSock.h"#includeusingnamespacestd;InitSockinitSock;intmain(intargc,char*argv[]){if(argc<2){cout<<"Usage:"<return1;}ChatServercharServer(atoi(argv[1]));try{charServer.Start();}catch(SockException&e){cout<cout<<"[SystemError]ErrorCode:"<}}ChatServer.h服务器类头文件#ifndefCHAT_SERVER_H#defineCHAT_SERVER_H#include#include#include#include"ClientInfo.h"usingnamespacestd;classChatServer{public:voidStart();voidEnd();ChatServer(intnPort);~ChatServer(void);private:voidInitFDSocket();voidDoSelect();voidDoFDRead(SOCKETsRead);voidRecvNewConnect();stringIPAddrToString(sockaddr_insin);voidAddClientToInfoMap(ClientInfoinfo);voidRecvNewMessage();boolIsCommand(stringstr);voidDoCommand(stringcmd);voidResoveCommand(stringcmd,int&argc,stringargv[]);voidDoCmdName(intargc,stringargv[]);voidDoCmdGetUsers(intargc,stringargv[]);voidDoMessage(stringmsg);voidDoCmdPrivateMsg(intargc,stringargv[]);stringBuildMessage(stringstr,boolbIsPublic);stringBuildSystemMsg(stringstr);voidDispatchMessage(stringmsg);voidCloseConnect();stringIntToString(intnNum);//============简单封装底层原语=============voidInitListenSocket();voidBind();voidListen();intSelect();intRecv(charmsgBuff[]);voidSend(stringmsg,SOCKETclient);SOCKETAccept(sockaddr_in&sin);//==========================================private:mapm_clients;fd_setm_fdSocket;fd_setm_fdRead;SOCKETm_sListen;SOCKETm_sNowClient;intm_nPort;};#endifCHAT_SERVER_HChatServer.cpp服务器类#include#include#include"ChatServer.h"#include"SockException.h"#pragmacomment(lib,"ws2_32.lib")usingnamespacestd;#defineMAX_BUFF_SIZE500typedefmap::iteratormap_it;ChatServer::ChatServer(intnPort){this->m_nPort=nPort;}voidChatServer::Start(){InitListenSocket();Bind();Listen();InitFDSocket();while(true){DoSelect();}}voidChatServer::DoSelect(){m_fdRead=m_fdSocket;intnRet=Select();if(nRet>0){for(inti=0;i{DoFDRead(m_fdRead.fd_array[i]);}}}voidChatServer::DoFDRead(SOCKETsRead){if(sRead==m_sListen){RecvNewConnect();}else{m_sNowClient=sRead;RecvNewMessage();}}voidChatServer::RecvNewConnect(){if(m_fdSocket.fd_count>=FD_SETSIZE){cout<<"[SystemInfo]接受连接达到上限,拒绝连接"<return;}sockaddr_inclientAddr;m_sNowClient=Accept(clientAddr);ClientInfoclientInfo(clientAddr);cout<<"[SystemInfo]接受来自"<FD_SET(m_sNowClient,&m_fdSocket);AddClientToInfoMap(clientInfo);}stringChatServer::IPAddrToString(sockaddr_insin){stringstr=inet_ntoa(sin.sin_addr);str.append(":");charszFormat[20];str.append(ltoa(ntohs(sin.sin_port),szFormat,10));returnstr;}voidChatServer::AddClientToInfoMap(ClientInfoinfo){m_clients[m_sNowClient]=info;}voidChatServer::RecvNewMessage(){charmsgBuff[MAX_BUFF_SIZE];intnRet=Recv(msgBuff);stringmsg(msgBuff);if(nRet<=0)return;if(IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}voidChatServer::Do
cout<<"[SystemError]ErrorCode:
"<}它其实就完成了一个简单的流程,初始化socket,绑定,监听,初始化fd_socket集合,死循环调用select。通过合理的封装底层原语和加入异常处理(异常交给顶层处理),使得代码专注于业务流程而不是繁杂的异常判断语句,在看下面这个函数DoSelect()。voidChatServer::DoSelect(){m_fdRead=m_fdSocket;intnRet=Select();if(nRet>0){for(inti=0;i{DoFDRead(m_fdRead.fd_array[i]);}}}它也只完成一个简单的流程,调用select,然后循环处理有读事件的socket。voidChatServer::DoRead(SOCKETsRead){if(sRead==m_sListen){RecvNewConnect();}else{m_sNowClient=sRead;RecvNewConnect();}}接下来的DoFDRead()函数完成的事情也非常直接,如果有事件的socket是监听socket的话,那么就是接收到了一个新的连接,否则是接收到了新的小。从上面这个简单的例子中可以看到,本软件最大的优点就是精心设计的类和函数。避免了使用select模型常见的反复嵌套的循环和判断,每个函数清晰明了。本系统还存在以下不足,首先是没有对界面做更深入的优化,只是做了最基本的调整,让输入输出更加雅观,其次是底层原语的封装并没有考虑到泛用性。四、系统详细设计这部分的文档在编码之前已经基本完成,由于时间较为仓促,部分内容可能和实际有所出入。1.ChatServer类该类负责完成服务器所有操作。1)类图2)成员变量Mapm_clients聊天者的SOCKET与昵称的映射fd_setm_fdSocket可用套接字集合fd_setm_fdRead有事件发生的套接字集合SOCKETm_sListen监听SocketSOCKETm_sNowClient当前处理的客户套接字intm_nPort监听端口3)方法设计voidBind()voidListen()voidSelect()intRecv()SOCKETAccept()封装底层原语,并加入异常机制,使得外部调用简约明了。构造函数传入监听端口,初始化m_nPortStart()1)初始化监听套接字:voidInitListenSocket()2)绑定套接字至本地机器:voidBind()3)进入监听模式(设置为非阻塞):voidListen()4)初始化可用套接字集合voidInitFDSocket()5)死循环,调用select方法DoSelect()6)结束DoSelect()1)令m_fdRead=m_fdSocket2)调用Select()3)循环处理Select的结果DoFdRead(SocketsRead)4)结束DoFdRead(intiReadIndex)1)判断是否为m_sListen2)是m_sListenRecvNewConnect()3)否则令m_sNowClient=m_fdRead[iReadIndex],调用RecvNewMessage()RecvNewConnect()1)判断是否达到套接字上线2)调用Accept(),接收连接sClient3)添加sCilent至m_fdSocket4)添加套接字至m_clientsAddClientToInfoMap(stringname)AddClientToInfoMap(stringname)1)以SOKCET为键,name为值加入MAPRecvNewMessage()1)调用Recv函数2)是否为命令IsCommand(stringstr)3)是,则DoCommand(stringcmd)4)否,则DoMessage(stringmsg)5)结束IsCommand(stringstr)1)判断是否以"/"开头DoCommand(stringcmd)1)判断指令,并解析命令与参数(argc,argv)2)调用指令处理函数3)假设只有SetName命令,那么则将对应的套接字的名称设置DoMessage(stringmsg)1)拼接消息与名字BuildMsg(stringmsg)2)在服务器上输出3)消息路由DispatchMessage(stringmsg)BuildMsg(stringmsg)1)从m_clients中取出用户昵称2)拼接字符串,形成格式如下超人君(127.0.0.1)23:49:48说:大家好!即为:昵称(IP地址)时间说:消息正文3)返回DispatchMessage(stringmsg)1)构造迭代器2)遍历m_clients,若不是自身,则派送消息Send()2.ChatClient类该类负责处理客户端的所有操作。1)类图2)字段设计SOCKETm_sClient客户端自身的socketSOCKETm_sServer服务器socketstringm_name昵称sockaddr_inm_ServerAddr;服务器地址3)方法设计构造函数根据端口号和服务器IP初始化m_serverConnect()voidSelect()intRecv()voidSend()intSelect()封装底层原语,加入异常处理,使得外部调用节约优雅voidStart()1)初始化套接字InitClientSocket()2)连接服务器Connect()设置为非阻塞模式3)获取名字并发送至服务器InitName()4)创建新线程并显示替他用户发言线程函数RecvMsgThread()5)循环SendMsg()6)关闭客户端CloseClient()InitName()1)提示输入昵称2)获取昵称3)合法性判断判断重复4)添加命令格式5)发送至服务器SendMsg()1)读取一行消息2)判断是否为命令IsCommand(stringstr)3)命令:处理命令DoCommand(stringcmd)4)消息:处理消息DoMessage(stringmsg)DoMessage(stringmsg)1)发送消息Send()2)本地回显RecvMsgThread()1)初始化fdSocket,将m_sClient加入2)创建fdRead3)死循环,将m_sClient拷贝至fdRead4)调用Select5)循环,并输出收到的消息Recv()3.SocketException类该类负责记录SOKCET错误的代码以及错误信息。5.命令协议命令格式为/命令参数1参数21.退出:/exit2.获取在线用户列表:/getuser3.私聊:/mUID信息4.清屏:/clear5.帮助:/help处理方式IsCommand(stringstr)负责解析是否为命令判断首字母是否为斜杠"/"str.at(0)=='/'ResoveCommand(stringcmd,int&argc,stringargv[])若是命令将命令解析为argc,argvDoCommand(stringcmd)处理命令,调用具体的XXX命令处理函数DoCmdXXXX()。6.消息格式1)公共消息超人君(127.0.0.1)UID:100说:大家好!李四(127.0.0.1)UID:101说:你好!!2)私聊你悄悄地对ABCUID:100说:你好CDFUID:101悄悄地对你说:你好3)服务器消息【系统消息】XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。4)程序内部提示[SystemInfo]xxxxxxxxxxxxxxxxxxxxxxxxx五、系统测试1.服务器使用错误2.客户端使用错误3.启动服务器4.启动客户端客户端出现欢迎信息以及昵称输入提示。服务器出现连接提示5.关闭客户端服务器出现断开连接提示6.启动服务器错误提示给出错误提示信息和提示代码7.公开聊天所有客户端以及服务器都会显示。8.私聊只有私聊的二人才能看到聊天信息,其他用户和服务器无法看到。9.错误的私聊私聊自己会得到一个错误提示私聊不存在的用户也会得到一个错误提示10.更名11.帮助12.非法指令非法指令会给出错误提示。13.非法的指令参数14.连接服务器失败六、心得体会这次实现我深入研究了select模型的使用,完成了一个简易的聊天室。这次试验也使我在编程技巧方面也有了很大的提高。七、完整代码Charserverd.cpp服务器main函数文件#include"ChatServer.h"#include"SockException.h"#include"InitSock.h"#includeusingnamespacestd;InitSockinitSock;intmain(intargc,char*argv[]){if(argc<2){cout<<"Usage:"<return1;}ChatServercharServer(atoi(argv[1]));try{charServer.Start();}catch(SockException&e){cout<cout<<"[SystemError]ErrorCode:"<}}ChatServer.h服务器类头文件#ifndefCHAT_SERVER_H#defineCHAT_SERVER_H#include#include#include#include"ClientInfo.h"usingnamespacestd;classChatServer{public:voidStart();voidEnd();ChatServer(intnPort);~ChatServer(void);private:voidInitFDSocket();voidDoSelect();voidDoFDRead(SOCKETsRead);voidRecvNewConnect();stringIPAddrToString(sockaddr_insin);voidAddClientToInfoMap(ClientInfoinfo);voidRecvNewMessage();boolIsCommand(stringstr);voidDoCommand(stringcmd);voidResoveCommand(stringcmd,int&argc,stringargv[]);voidDoCmdName(intargc,stringargv[]);voidDoCmdGetUsers(intargc,stringargv[]);voidDoMessage(stringmsg);voidDoCmdPrivateMsg(intargc,stringargv[]);stringBuildMessage(stringstr,boolbIsPublic);stringBuildSystemMsg(stringstr);voidDispatchMessage(stringmsg);voidCloseConnect();stringIntToString(intnNum);//============简单封装底层原语=============voidInitListenSocket();voidBind();voidListen();intSelect();intRecv(charmsgBuff[]);voidSend(stringmsg,SOCKETclient);SOCKETAccept(sockaddr_in&sin);//==========================================private:mapm_clients;fd_setm_fdSocket;fd_setm_fdRead;SOCKETm_sListen;SOCKETm_sNowClient;intm_nPort;};#endifCHAT_SERVER_HChatServer.cpp服务器类#include#include#include"ChatServer.h"#include"SockException.h"#pragmacomment(lib,"ws2_32.lib")usingnamespacestd;#defineMAX_BUFF_SIZE500typedefmap::iteratormap_it;ChatServer::ChatServer(intnPort){this->m_nPort=nPort;}voidChatServer::Start(){InitListenSocket();Bind();Listen();InitFDSocket();while(true){DoSelect();}}voidChatServer::DoSelect(){m_fdRead=m_fdSocket;intnRet=Select();if(nRet>0){for(inti=0;i{DoFDRead(m_fdRead.fd_array[i]);}}}voidChatServer::DoFDRead(SOCKETsRead){if(sRead==m_sListen){RecvNewConnect();}else{m_sNowClient=sRead;RecvNewMessage();}}voidChatServer::RecvNewConnect(){if(m_fdSocket.fd_count>=FD_SETSIZE){cout<<"[SystemInfo]接受连接达到上限,拒绝连接"<return;}sockaddr_inclientAddr;m_sNowClient=Accept(clientAddr);ClientInfoclientInfo(clientAddr);cout<<"[SystemInfo]接受来自"<FD_SET(m_sNowClient,&m_fdSocket);AddClientToInfoMap(clientInfo);}stringChatServer::IPAddrToString(sockaddr_insin){stringstr=inet_ntoa(sin.sin_addr);str.append(":");charszFormat[20];str.append(ltoa(ntohs(sin.sin_port),szFormat,10));returnstr;}voidChatServer::AddClientToInfoMap(ClientInfoinfo){m_clients[m_sNowClient]=info;}voidChatServer::RecvNewMessage(){charmsgBuff[MAX_BUFF_SIZE];intnRet=Recv(msgBuff);stringmsg(msgBuff);if(nRet<=0)return;if(IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}voidChatServer::Do
它其实就完成了一个简单的流程,初始化socket,绑定,监听,初始化fd_socket集合,死循环调用select。
通过合理的封装底层原语和加入异常处理(异常交给顶层处理),使得代码专注于业务流程而不是繁杂的异常判断语句,在看下面这个函数DoSelect()。
DoSelect()
m_fdRead=m_fdSocket;
intnRet=Select();
if(nRet>0)
for(inti=0;i{DoFDRead(m_fdRead.fd_array[i]);}}}它也只完成一个简单的流程,调用select,然后循环处理有读事件的socket。voidChatServer::DoRead(SOCKETsRead){if(sRead==m_sListen){RecvNewConnect();}else{m_sNowClient=sRead;RecvNewConnect();}}接下来的DoFDRead()函数完成的事情也非常直接,如果有事件的socket是监听socket的话,那么就是接收到了一个新的连接,否则是接收到了新的小。从上面这个简单的例子中可以看到,本软件最大的优点就是精心设计的类和函数。避免了使用select模型常见的反复嵌套的循环和判断,每个函数清晰明了。本系统还存在以下不足,首先是没有对界面做更深入的优化,只是做了最基本的调整,让输入输出更加雅观,其次是底层原语的封装并没有考虑到泛用性。四、系统详细设计这部分的文档在编码之前已经基本完成,由于时间较为仓促,部分内容可能和实际有所出入。1.ChatServer类该类负责完成服务器所有操作。1)类图2)成员变量Mapm_clients聊天者的SOCKET与昵称的映射fd_setm_fdSocket可用套接字集合fd_setm_fdRead有事件发生的套接字集合SOCKETm_sListen监听SocketSOCKETm_sNowClient当前处理的客户套接字intm_nPort监听端口3)方法设计voidBind()voidListen()voidSelect()intRecv()SOCKETAccept()封装底层原语,并加入异常机制,使得外部调用简约明了。构造函数传入监听端口,初始化m_nPortStart()1)初始化监听套接字:voidInitListenSocket()2)绑定套接字至本地机器:voidBind()3)进入监听模式(设置为非阻塞):voidListen()4)初始化可用套接字集合voidInitFDSocket()5)死循环,调用select方法DoSelect()6)结束DoSelect()1)令m_fdRead=m_fdSocket2)调用Select()3)循环处理Select的结果DoFdRead(SocketsRead)4)结束DoFdRead(intiReadIndex)1)判断是否为m_sListen2)是m_sListenRecvNewConnect()3)否则令m_sNowClient=m_fdRead[iReadIndex],调用RecvNewMessage()RecvNewConnect()1)判断是否达到套接字上线2)调用Accept(),接收连接sClient3)添加sCilent至m_fdSocket4)添加套接字至m_clientsAddClientToInfoMap(stringname)AddClientToInfoMap(stringname)1)以SOKCET为键,name为值加入MAPRecvNewMessage()1)调用Recv函数2)是否为命令IsCommand(stringstr)3)是,则DoCommand(stringcmd)4)否,则DoMessage(stringmsg)5)结束IsCommand(stringstr)1)判断是否以"/"开头DoCommand(stringcmd)1)判断指令,并解析命令与参数(argc,argv)2)调用指令处理函数3)假设只有SetName命令,那么则将对应的套接字的名称设置DoMessage(stringmsg)1)拼接消息与名字BuildMsg(stringmsg)2)在服务器上输出3)消息路由DispatchMessage(stringmsg)BuildMsg(stringmsg)1)从m_clients中取出用户昵称2)拼接字符串,形成格式如下超人君(127.0.0.1)23:49:48说:大家好!即为:昵称(IP地址)时间说:消息正文3)返回DispatchMessage(stringmsg)1)构造迭代器2)遍历m_clients,若不是自身,则派送消息Send()2.ChatClient类该类负责处理客户端的所有操作。1)类图2)字段设计SOCKETm_sClient客户端自身的socketSOCKETm_sServer服务器socketstringm_name昵称sockaddr_inm_ServerAddr;服务器地址3)方法设计构造函数根据端口号和服务器IP初始化m_serverConnect()voidSelect()intRecv()voidSend()intSelect()封装底层原语,加入异常处理,使得外部调用节约优雅voidStart()1)初始化套接字InitClientSocket()2)连接服务器Connect()设置为非阻塞模式3)获取名字并发送至服务器InitName()4)创建新线程并显示替他用户发言线程函数RecvMsgThread()5)循环SendMsg()6)关闭客户端CloseClient()InitName()1)提示输入昵称2)获取昵称3)合法性判断判断重复4)添加命令格式5)发送至服务器SendMsg()1)读取一行消息2)判断是否为命令IsCommand(stringstr)3)命令:处理命令DoCommand(stringcmd)4)消息:处理消息DoMessage(stringmsg)DoMessage(stringmsg)1)发送消息Send()2)本地回显RecvMsgThread()1)初始化fdSocket,将m_sClient加入2)创建fdRead3)死循环,将m_sClient拷贝至fdRead4)调用Select5)循环,并输出收到的消息Recv()3.SocketException类该类负责记录SOKCET错误的代码以及错误信息。5.命令协议命令格式为/命令参数1参数21.退出:/exit2.获取在线用户列表:/getuser3.私聊:/mUID信息4.清屏:/clear5.帮助:/help处理方式IsCommand(stringstr)负责解析是否为命令判断首字母是否为斜杠"/"str.at(0)=='/'ResoveCommand(stringcmd,int&argc,stringargv[])若是命令将命令解析为argc,argvDoCommand(stringcmd)处理命令,调用具体的XXX命令处理函数DoCmdXXXX()。6.消息格式1)公共消息超人君(127.0.0.1)UID:100说:大家好!李四(127.0.0.1)UID:101说:你好!!2)私聊你悄悄地对ABCUID:100说:你好CDFUID:101悄悄地对你说:你好3)服务器消息【系统消息】XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。4)程序内部提示[SystemInfo]xxxxxxxxxxxxxxxxxxxxxxxxx五、系统测试1.服务器使用错误2.客户端使用错误3.启动服务器4.启动客户端客户端出现欢迎信息以及昵称输入提示。服务器出现连接提示5.关闭客户端服务器出现断开连接提示6.启动服务器错误提示给出错误提示信息和提示代码7.公开聊天所有客户端以及服务器都会显示。8.私聊只有私聊的二人才能看到聊天信息,其他用户和服务器无法看到。9.错误的私聊私聊自己会得到一个错误提示私聊不存在的用户也会得到一个错误提示10.更名11.帮助12.非法指令非法指令会给出错误提示。13.非法的指令参数14.连接服务器失败六、心得体会这次实现我深入研究了select模型的使用,完成了一个简易的聊天室。这次试验也使我在编程技巧方面也有了很大的提高。七、完整代码Charserverd.cpp服务器main函数文件#include"ChatServer.h"#include"SockException.h"#include"InitSock.h"#includeusingnamespacestd;InitSockinitSock;intmain(intargc,char*argv[]){if(argc<2){cout<<"Usage:"<return1;}ChatServercharServer(atoi(argv[1]));try{charServer.Start();}catch(SockException&e){cout<cout<<"[SystemError]ErrorCode:"<}}ChatServer.h服务器类头文件#ifndefCHAT_SERVER_H#defineCHAT_SERVER_H#include#include#include#include"ClientInfo.h"usingnamespacestd;classChatServer{public:voidStart();voidEnd();ChatServer(intnPort);~ChatServer(void);private:voidInitFDSocket();voidDoSelect();voidDoFDRead(SOCKETsRead);voidRecvNewConnect();stringIPAddrToString(sockaddr_insin);voidAddClientToInfoMap(ClientInfoinfo);voidRecvNewMessage();boolIsCommand(stringstr);voidDoCommand(stringcmd);voidResoveCommand(stringcmd,int&argc,stringargv[]);voidDoCmdName(intargc,stringargv[]);voidDoCmdGetUsers(intargc,stringargv[]);voidDoMessage(stringmsg);voidDoCmdPrivateMsg(intargc,stringargv[]);stringBuildMessage(stringstr,boolbIsPublic);stringBuildSystemMsg(stringstr);voidDispatchMessage(stringmsg);voidCloseConnect();stringIntToString(intnNum);//============简单封装底层原语=============voidInitListenSocket();voidBind();voidListen();intSelect();intRecv(charmsgBuff[]);voidSend(stringmsg,SOCKETclient);SOCKETAccept(sockaddr_in&sin);//==========================================private:mapm_clients;fd_setm_fdSocket;fd_setm_fdRead;SOCKETm_sListen;SOCKETm_sNowClient;intm_nPort;};#endifCHAT_SERVER_HChatServer.cpp服务器类#include#include#include"ChatServer.h"#include"SockException.h"#pragmacomment(lib,"ws2_32.lib")usingnamespacestd;#defineMAX_BUFF_SIZE500typedefmap::iteratormap_it;ChatServer::ChatServer(intnPort){this->m_nPort=nPort;}voidChatServer::Start(){InitListenSocket();Bind();Listen();InitFDSocket();while(true){DoSelect();}}voidChatServer::DoSelect(){m_fdRead=m_fdSocket;intnRet=Select();if(nRet>0){for(inti=0;i{DoFDRead(m_fdRead.fd_array[i]);}}}voidChatServer::DoFDRead(SOCKETsRead){if(sRead==m_sListen){RecvNewConnect();}else{m_sNowClient=sRead;RecvNewMessage();}}voidChatServer::RecvNewConnect(){if(m_fdSocket.fd_count>=FD_SETSIZE){cout<<"[SystemInfo]接受连接达到上限,拒绝连接"<return;}sockaddr_inclientAddr;m_sNowClient=Accept(clientAddr);ClientInfoclientInfo(clientAddr);cout<<"[SystemInfo]接受来自"<FD_SET(m_sNowClient,&m_fdSocket);AddClientToInfoMap(clientInfo);}stringChatServer::IPAddrToString(sockaddr_insin){stringstr=inet_ntoa(sin.sin_addr);str.append(":");charszFormat[20];str.append(ltoa(ntohs(sin.sin_port),szFormat,10));returnstr;}voidChatServer::AddClientToInfoMap(ClientInfoinfo){m_clients[m_sNowClient]=info;}voidChatServer::RecvNewMessage(){charmsgBuff[MAX_BUFF_SIZE];intnRet=Recv(msgBuff);stringmsg(msgBuff);if(nRet<=0)return;if(IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}voidChatServer::Do
DoFDRead(m_fdRead.fd_array[i]);
它也只完成一个简单的流程,调用select,然后循环处理有读事件的socket。
DoRead(SOCKETsRead)
if(sRead==m_sListen)
RecvNewConnect();
else
m_sNowClient=sRead;
接下来的DoFDRead()函数完成的事情也非常直接,如果有事件的socket是监听socket的话,那么就是接收到了一个新的连接,否则是接收到了新的小。
从上面这个简单的例子中可以看到,本软件最大的优点就是精心设计的类和函数。
避免了使用select模型常见的反复嵌套的循环和判断,每个函数清晰明了。
本系统还存在以下不足,首先是没有对界面做更深入的优化,只是做了最基本的调整,让输入输出更加雅观,其次是底层原语的封装并没有考虑到泛用性。
四、系统详细设计
这部分的文档在编码之前已经基本完成,由于时间较为仓促,部分内容可能和实际有所出入。
1.ChatServer类
该类负责完成服务器所有操作。
1)类图
2)成员变量
Mapm_clients聊天者的SOCKET与昵称的映射
fd_setm_fdSocket可用套接字集合
fd_setm_fdRead有事件发生的套接字集合
SOCKETm_sListen监听Socket
SOCKETm_sNowClient当前处理的客户套接字
intm_nPort监听端口
3)方法设计
voidBind()
voidListen()
voidSelect()
intRecv()
SOCKETAccept()
封装底层原语,并加入异常机制,使得外部调用简约明了。
构造函数
传入监听端口,初始化m_nPort
1)初始化监听套接字:
voidInitListenSocket()
2)绑定套接字至本地机器:
3)进入监听模式(设置为非阻塞):
4)初始化可用套接字集合voidInitFDSocket()
5)死循环,调用select方法DoSelect()
6)结束
1)令m_fdRead=m_fdSocket
2)调用Select()
3)循环处理Select的结果DoFdRead(SocketsRead)
4)结束
DoFdRead(intiReadIndex)
1)判断是否为m_sListen
2)是m_sListenRecvNewConnect()
3)否则令m_sNowClient=m_fdRead[iReadIndex],调用RecvNewMessage()
RecvNewConnect()
1)判断是否达到套接字上线
2)调用Accept(),接收连接sClient
3)添加sCilent至m_fdSocket
4)添加套接字至m_clientsAddClientToInfoMap(stringname)
AddClientToInfoMap(stringname)
1)以SOKCET为键,name为值加入MAP
RecvNewMessage()
1)调用Recv函数
2)是否为命令IsCommand(stringstr)
3)是,则DoCommand(stringcmd)
4)否,则DoMessage(stringmsg)
5)结束
IsCommand(stringstr)
1)判断是否以"/"开头
DoCommand(stringcmd)
1)判断指令,并解析命令与参数(argc,argv)
2)调用指令处理函数
3)假设只有SetName命令,那么则将对应的套接字的名称设置
DoMessage(stringmsg)
1)拼接消息与名字BuildMsg(stringmsg)
2)在服务器上输出
3)消息路由DispatchMessage(stringmsg)
BuildMsg(stringmsg)
1)从m_clients中取出用户昵称
2)拼接字符串,形成格式如下
超人君(127.0.0.1)23:
49:
48说:
大家好!
即为:
昵称(IP地址)时间说:
消息正文
3)返回
DispatchMessage(stringmsg)
1)构造迭代器
2)遍历m_clients,若不是自身,则派送消息Send()
2.ChatClient类
该类负责处理客户端的所有操作。
2)字段设计
SOCKETm_sClient客户端自身的socket
SOCKETm_sServer服务器socket
stringm_name昵称
sockaddr_inm_ServerAddr;服务器地址
根据端口号和服务器IP初始化m_server
Connect()
voidSend()
intSelect()
封装底层原语,加入异常处理,使得外部调用节约优雅
voidStart()
1)初始化套接字InitClientSocket()
2)连接服务器Connect()设置为非阻塞模式
3)获取名字并发送至服务器InitName()
4)创建新线程并显示替他用户发言线程函数RecvMsgThread()
5)循环SendMsg()
6)关闭客户端CloseClient()
InitName()
1)提示输入昵称
2)获取昵称
3)合法性判断判断重复
4)添加命令格式
5)发送至服务器
SendMsg()
1)读取一行消息
2)判断是否为命令IsCommand(stringstr)
3)命令:
处理命令DoCommand(stringcmd)
4)消息:
处理消息DoMessage(stringmsg)
1)发送消息Send()
2)本地回显
RecvMsgThread()
1)初始化fdSocket,将m_sClient加入
2)创建fdRead
3)死循环,将m_sClient拷贝至fdRead
4)调用Select
5)循环,并输出收到的消息Recv()
3.SocketException类
该类负责记录SOKCET错误的代码以及错误信息。
5.命令协议
命令格式为/命令参数1参数2
1.退出:
/exit
2.获取在线用户列表:
/getuser
3.私聊:
/mUID信息
4.清屏:
/clear
5.帮助:
/help
处理方式
IsCommand(stringstr)负责解析是否为命令
判断首字母是否为斜杠"/"str.at(0)=='/'
ResoveCommand(stringcmd,int&argc,stringargv[])若是命令将命令解析为argc,argv
DoCommand(stringcmd)处理命令,调用具体的XXX命令处理函数DoCmdXXXX()。
6.消息格式
1)公共消息
超人君(127.0.0.1)UID:
100说:
李四(127.0.0.1)UID:
101说:
你好!
!
你悄悄地对ABCUID:
100说:
你好
CDFUID:
101悄悄地对你说:
3)服务器消息
【系统消息】XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。
4)程序内部提示
[SystemInfo]xxxxxxxxxxxxxxxxxxxxxxxxx
五、系统测试
1.服务器使用错误
2.客户端使用错误
3.启动服务器
4.启动客户端
客户端出现欢迎信息以及昵称输入提示。
服务器出现连接提示
5.关闭客户端
服务器出现断开连接提示
6.启动服务器错误提示
给出错误提示信息和提示代码
7.公开聊天
所有客户端以及服务器都会显示。
8.私聊
只有私聊的二人才能看到聊天信息,其他用户和服务器无法看到。
9.错误的私聊
私聊自己会得到一个错误提示
私聊不存在的用户也会得到一个错误提示
10.更名
11.帮助
12.非法指令
非法指令会给出错误提示。
13.非法的指令参数
14.连接服务器失败
六、心得体会
这次实现我深入研究了select模型的使用,完成了一个简易的聊天室。
这次试验也使我在编程技巧方面也有了很大的提高。
七、完整代码
Charserverd.cpp服务器main函数文件
#include"ChatServer.h"
#include"SockException.h"
#include"InitSock.h"
#include
usingnamespacestd;
InitSockinitSock;
intmain(intargc,char*argv[])
if(argc<2)
cout<<"Usage:
"<return1;}ChatServercharServer(atoi(argv[1]));try{charServer.Start();}catch(SockException&e){cout<cout<<"[SystemError]ErrorCode:"<}}ChatServer.h服务器类头文件#ifndefCHAT_SERVER_H#defineCHAT_SERVER_H#include#include#include#include"ClientInfo.h"usingnamespacestd;classChatServer{public:voidStart();voidEnd();ChatServer(intnPort);~ChatServer(void);private:voidInitFDSocket();voidDoSelect();voidDoFDRead(SOCKETsRead);voidRecvNewConnect();stringIPAddrToString(sockaddr_insin);voidAddClientToInfoMap(ClientInfoinfo);voidRecvNewMessage();boolIsCommand(stringstr);voidDoCommand(stringcmd);voidResoveCommand(stringcmd,int&argc,stringargv[]);voidDoCmdName(intargc,stringargv[]);voidDoCmdGetUsers(intargc,stringargv[]);voidDoMessage(stringmsg);voidDoCmdPrivateMsg(intargc,stringargv[]);stringBuildMessage(stringstr,boolbIsPublic);stringBuildSystemMsg(stringstr);voidDispatchMessage(stringmsg);voidCloseConnect();stringIntToString(intnNum);//============简单封装底层原语=============voidInitListenSocket();voidBind();voidListen();intSelect();intRecv(charmsgBuff[]);voidSend(stringmsg,SOCKETclient);SOCKETAccept(sockaddr_in&sin);//==========================================private:mapm_clients;fd_setm_fdSocket;fd_setm_fdRead;SOCKETm_sListen;SOCKETm_sNowClient;intm_nPort;};#endifCHAT_SERVER_HChatServer.cpp服务器类#include#include#include"ChatServer.h"#include"SockException.h"#pragmacomment(lib,"ws2_32.lib")usingnamespacestd;#defineMAX_BUFF_SIZE500typedefmap::iteratormap_it;ChatServer::ChatServer(intnPort){this->m_nPort=nPort;}voidChatServer::Start(){InitListenSocket();Bind();Listen();InitFDSocket();while(true){DoSelect();}}voidChatServer::DoSelect(){m_fdRead=m_fdSocket;intnRet=Select();if(nRet>0){for(inti=0;i{DoFDRead(m_fdRead.fd_array[i]);}}}voidChatServer::DoFDRead(SOCKETsRead){if(sRead==m_sListen){RecvNewConnect();}else{m_sNowClient=sRead;RecvNewMessage();}}voidChatServer::RecvNewConnect(){if(m_fdSocket.fd_count>=FD_SETSIZE){cout<<"[SystemInfo]接受连接达到上限,拒绝连接"<return;}sockaddr_inclientAddr;m_sNowClient=Accept(clientAddr);ClientInfoclientInfo(clientAddr);cout<<"[SystemInfo]接受来自"<FD_SET(m_sNowClient,&m_fdSocket);AddClientToInfoMap(clientInfo);}stringChatServer::IPAddrToString(sockaddr_insin){stringstr=inet_ntoa(sin.sin_addr);str.append(":");charszFormat[20];str.append(ltoa(ntohs(sin.sin_port),szFormat,10));returnstr;}voidChatServer::AddClientToInfoMap(ClientInfoinfo){m_clients[m_sNowClient]=info;}voidChatServer::RecvNewMessage(){charmsgBuff[MAX_BUFF_SIZE];intnRet=Recv(msgBuff);stringmsg(msgBuff);if(nRet<=0)return;if(IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}voidChatServer::Do
return1;
ChatServercharServer(atoi(argv[1]));
catch(SockException&e)
cout<cout<<"[SystemError]ErrorCode:"<}}ChatServer.h服务器类头文件#ifndefCHAT_SERVER_H#defineCHAT_SERVER_H#include#include#include#include"ClientInfo.h"usingnamespacestd;classChatServer{public:voidStart();voidEnd();ChatServer(intnPort);~ChatServer(void);private:voidInitFDSocket();voidDoSelect();voidDoFDRead(SOCKETsRead);voidRecvNewConnect();stringIPAddrToString(sockaddr_insin);voidAddClientToInfoMap(ClientInfoinfo);voidRecvNewMessage();boolIsCommand(stringstr);voidDoCommand(stringcmd);voidResoveCommand(stringcmd,int&argc,stringargv[]);voidDoCmdName(intargc,stringargv[]);voidDoCmdGetUsers(intargc,stringargv[]);voidDoMessage(stringmsg);voidDoCmdPrivateMsg(intargc,stringargv[]);stringBuildMessage(stringstr,boolbIsPublic);stringBuildSystemMsg(stringstr);voidDispatchMessage(stringmsg);voidCloseConnect();stringIntToString(intnNum);//============简单封装底层原语=============voidInitListenSocket();voidBind();voidListen();intSelect();intRecv(charmsgBuff[]);voidSend(stringmsg,SOCKETclient);SOCKETAccept(sockaddr_in&sin);//==========================================private:mapm_clients;fd_setm_fdSocket;fd_setm_fdRead;SOCKETm_sListen;SOCKETm_sNowClient;intm_nPort;};#endifCHAT_SERVER_HChatServer.cpp服务器类#include#include#include"ChatServer.h"#include"SockException.h"#pragmacomment(lib,"ws2_32.lib")usingnamespacestd;#defineMAX_BUFF_SIZE500typedefmap::iteratormap_it;ChatServer::ChatServer(intnPort){this->m_nPort=nPort;}voidChatServer::Start(){InitListenSocket();Bind();Listen();InitFDSocket();while(true){DoSelect();}}voidChatServer::DoSelect(){m_fdRead=m_fdSocket;intnRet=Select();if(nRet>0){for(inti=0;i{DoFDRead(m_fdRead.fd_array[i]);}}}voidChatServer::DoFDRead(SOCKETsRead){if(sRead==m_sListen){RecvNewConnect();}else{m_sNowClient=sRead;RecvNewMessage();}}voidChatServer::RecvNewConnect(){if(m_fdSocket.fd_count>=FD_SETSIZE){cout<<"[SystemInfo]接受连接达到上限,拒绝连接"<return;}sockaddr_inclientAddr;m_sNowClient=Accept(clientAddr);ClientInfoclientInfo(clientAddr);cout<<"[SystemInfo]接受来自"<FD_SET(m_sNowClient,&m_fdSocket);AddClientToInfoMap(clientInfo);}stringChatServer::IPAddrToString(sockaddr_insin){stringstr=inet_ntoa(sin.sin_addr);str.append(":");charszFormat[20];str.append(ltoa(ntohs(sin.sin_port),szFormat,10));returnstr;}voidChatServer::AddClientToInfoMap(ClientInfoinfo){m_clients[m_sNowClient]=info;}voidChatServer::RecvNewMessage(){charmsgBuff[MAX_BUFF_SIZE];intnRet=Recv(msgBuff);stringmsg(msgBuff);if(nRet<=0)return;if(IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}voidChatServer::Do
"<}}ChatServer.h服务器类头文件#ifndefCHAT_SERVER_H#defineCHAT_SERVER_H#include#include#include#include"ClientInfo.h"usingnamespacestd;classChatServer{public:voidStart();voidEnd();ChatServer(intnPort);~ChatServer(void);private:voidInitFDSocket();voidDoSelect();voidDoFDRead(SOCKETsRead);voidRecvNewConnect();stringIPAddrToString(sockaddr_insin);voidAddClientToInfoMap(ClientInfoinfo);voidRecvNewMessage();boolIsCommand(stringstr);voidDoCommand(stringcmd);voidResoveCommand(stringcmd,int&argc,stringargv[]);voidDoCmdName(intargc,stringargv[]);voidDoCmdGetUsers(intargc,stringargv[]);voidDoMessage(stringmsg);voidDoCmdPrivateMsg(intargc,stringargv[]);stringBuildMessage(stringstr,boolbIsPublic);stringBuildSystemMsg(stringstr);voidDispatchMessage(stringmsg);voidCloseConnect();stringIntToString(intnNum);//============简单封装底层原语=============voidInitListenSocket();voidBind();voidListen();intSelect();intRecv(charmsgBuff[]);voidSend(stringmsg,SOCKETclient);SOCKETAccept(sockaddr_in&sin);//==========================================private:mapm_clients;fd_setm_fdSocket;fd_setm_fdRead;SOCKETm_sListen;SOCKETm_sNowClient;intm_nPort;};#endifCHAT_SERVER_HChatServer.cpp服务器类#include#include#include"ChatServer.h"#include"SockException.h"#pragmacomment(lib,"ws2_32.lib")usingnamespacestd;#defineMAX_BUFF_SIZE500typedefmap::iteratormap_it;ChatServer::ChatServer(intnPort){this->m_nPort=nPort;}voidChatServer::Start(){InitListenSocket();Bind();Listen();InitFDSocket();while(true){DoSelect();}}voidChatServer::DoSelect(){m_fdRead=m_fdSocket;intnRet=Select();if(nRet>0){for(inti=0;i{DoFDRead(m_fdRead.fd_array[i]);}}}voidChatServer::DoFDRead(SOCKETsRead){if(sRead==m_sListen){RecvNewConnect();}else{m_sNowClient=sRead;RecvNewMessage();}}voidChatServer::RecvNewConnect(){if(m_fdSocket.fd_count>=FD_SETSIZE){cout<<"[SystemInfo]接受连接达到上限,拒绝连接"<return;}sockaddr_inclientAddr;m_sNowClient=Accept(clientAddr);ClientInfoclientInfo(clientAddr);cout<<"[SystemInfo]接受来自"<FD_SET(m_sNowClient,&m_fdSocket);AddClientToInfoMap(clientInfo);}stringChatServer::IPAddrToString(sockaddr_insin){stringstr=inet_ntoa(sin.sin_addr);str.append(":");charszFormat[20];str.append(ltoa(ntohs(sin.sin_port),szFormat,10));returnstr;}voidChatServer::AddClientToInfoMap(ClientInfoinfo){m_clients[m_sNowClient]=info;}voidChatServer::RecvNewMessage(){charmsgBuff[MAX_BUFF_SIZE];intnRet=Recv(msgBuff);stringmsg(msgBuff);if(nRet<=0)return;if(IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}voidChatServer::Do
ChatServer.h服务器类头文件
#ifndefCHAT_SERVER_H
#defineCHAT_SERVER_H
#include"ClientInfo.h"
classChatServer
public:
voidStart();
voidEnd();
ChatServer(intnPort);
~ChatServer(void);
private:
voidInitFDSocket();
voidDoSelect();
voidDoFDRead(SOCKETsRead);
voidRecvNewConnect();
stringIPAddrToString(sockaddr_insin);
voidAddClientToInfoMap(ClientInfoinfo);
voidRecvNewMessage();
boolIsCommand(stringstr);
voidDoCommand(stringcmd);
voidResoveCommand(stringcmd,int&argc,stringargv[]);
voidDoCmdName(intargc,stringargv[]);
voidDoCmdGetUsers(intargc,stringargv[]);
voidDoMessage(stringmsg);
voidDoCmdPrivateMsg(intargc,stringargv[]);
stringBuildMessage(stringstr,boolbIsPublic);
stringBuildSystemMsg(stringstr);
voidDispatchMessage(stringmsg);
voidCloseConnect();
stringIntToString(intnNum);
//============简单封装底层原语=============
voidInitListenSocket();
voidBind();
voidListen();
intSelect();
intRecv(charmsgBuff[]);
voidSend(stringmsg,SOCKETclient);
SOCKETAccept(sockaddr_in&sin);
//==========================================
mapm_clients;
fd_setm_fdSocket;
fd_setm_fdRead;
SOCKETm_sListen;
SOCKETm_sNowClient;
intm_nPort;
};
#endifCHAT_SERVER_H
ChatServer.cpp服务器类
#pragmacomment(lib,"ws2_32.lib")
#defineMAX_BUFF_SIZE500
typedefmap:
iteratormap_it;
ChatServer:
ChatServer(intnPort)
this->m_nPort=nPort;
for(inti=0;i{DoFDRead(m_fdRead.fd_array[i]);}}}voidChatServer::DoFDRead(SOCKETsRead){if(sRead==m_sListen){RecvNewConnect();}else{m_sNowClient=sRead;RecvNewMessage();}}voidChatServer::RecvNewConnect(){if(m_fdSocket.fd_count>=FD_SETSIZE){cout<<"[SystemInfo]接受连接达到上限,拒绝连接"<return;}sockaddr_inclientAddr;m_sNowClient=Accept(clientAddr);ClientInfoclientInfo(clientAddr);cout<<"[SystemInfo]接受来自"<FD_SET(m_sNowClient,&m_fdSocket);AddClientToInfoMap(clientInfo);}stringChatServer::IPAddrToString(sockaddr_insin){stringstr=inet_ntoa(sin.sin_addr);str.append(":");charszFormat[20];str.append(ltoa(ntohs(sin.sin_port),szFormat,10));returnstr;}voidChatServer::AddClientToInfoMap(ClientInfoinfo){m_clients[m_sNowClient]=info;}voidChatServer::RecvNewMessage(){charmsgBuff[MAX_BUFF_SIZE];intnRet=Recv(msgBuff);stringmsg(msgBuff);if(nRet<=0)return;if(IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}voidChatServer::Do
DoFDRead(SOCKETsRead)
RecvNewMessage();
if(m_fdSocket.fd_count>=FD_SETSIZE)
cout<<"[SystemInfo]接受连接达到上限,拒绝连接"<return;}sockaddr_inclientAddr;m_sNowClient=Accept(clientAddr);ClientInfoclientInfo(clientAddr);cout<<"[SystemInfo]接受来自"<FD_SET(m_sNowClient,&m_fdSocket);AddClientToInfoMap(clientInfo);}stringChatServer::IPAddrToString(sockaddr_insin){stringstr=inet_ntoa(sin.sin_addr);str.append(":");charszFormat[20];str.append(ltoa(ntohs(sin.sin_port),szFormat,10));returnstr;}voidChatServer::AddClientToInfoMap(ClientInfoinfo){m_clients[m_sNowClient]=info;}voidChatServer::RecvNewMessage(){charmsgBuff[MAX_BUFF_SIZE];intnRet=Recv(msgBuff);stringmsg(msgBuff);if(nRet<=0)return;if(IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}voidChatServer::Do
return;
sockaddr_inclientAddr;
m_sNowClient=Accept(clientAddr);
ClientInfoclientInfo(clientAddr);
cout<<"[SystemInfo]接受来自"<FD_SET(m_sNowClient,&m_fdSocket);AddClientToInfoMap(clientInfo);}stringChatServer::IPAddrToString(sockaddr_insin){stringstr=inet_ntoa(sin.sin_addr);str.append(":");charszFormat[20];str.append(ltoa(ntohs(sin.sin_port),szFormat,10));returnstr;}voidChatServer::AddClientToInfoMap(ClientInfoinfo){m_clients[m_sNowClient]=info;}voidChatServer::RecvNewMessage(){charmsgBuff[MAX_BUFF_SIZE];intnRet=Recv(msgBuff);stringmsg(msgBuff);if(nRet<=0)return;if(IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}voidChatServer::Do
FD_SET(m_sNowClient,&m_fdSocket);
AddClientToInfoMap(clientInfo);
stringChatServer:
IPAddrToString(sockaddr_insin)
stringstr=inet_ntoa(sin.sin_addr);
str.append(":
");
charszFormat[20];
str.append(ltoa(ntohs(sin.sin_port),szFormat,10));
returnstr;
AddClientToInfoMap(ClientInfoinfo)
m_clients[m_sNowClient]=info;
charmsgBuff[MAX_BUFF_SIZE];
intnRet=Recv(msgBuff);
stringmsg(msgBuff);
if(nRet<=0)return;
if(IsCommand(msg))
DoCommand(msg);
DoMessage(msg);
Do
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1