当前位置:文档之家› 网络编程 基于TCP的简易聊天室 实验报告

网络编程 基于TCP的简易聊天室 实验报告

网络编程课程设计-基于TCP的简易聊天室一、实验基本信息概要1.题目要求熟悉异步网络编程的基本方法,掌握异步网络编程和网络协议设计的方法。

要求采用select 模型、WSAAsyncSelect模型、WSAEventSelect模型、重叠模型或完成端口等模型完成编程任务。

2.上机要求要求采用select模型、WSAAsyncSelect模型、WSAEventSelect模型、重叠模型或完成端口等模型完成下面的任务。

3.题目内容内容概要:实现一个聊天室,支持多人聊天。

也可以增加私聊等功能。

4.开发环境操作系统:Windows 7开发语言:C++集成开发环境:Microsoft Visual Studio 2010二、系统简介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模型进行了细致的拆分与抽象,做到了逻辑流程清晰,每个函数简洁易懂,层次分明。

例如服务器启动函数:它其实就完成了一个简单的流程,初始化socket,绑定,监听,初始化fd_socket集合,死循环调用select。

通过合理的封装底层原语和加入异常处理(异常交给顶层处理),使得代码专注于业务流程而不是繁杂的异常判断语句,在看下面这个函数DoSelect()。

它也只完成一个简单的流程,调用select,然后循环处理有读事件的socket。

接下来的DoFDRead()函数完成的事情也非常直接,如果有事件的socket是监听socket的话,那么就是接收到了一个新的连接,否则是接收到了新的小。

从上面这个简单的例子中可以看到,本软件最大的优点就是精心设计的类和函数。

避免了使用select模型常见的反复嵌套的循环和判断,每个函数清晰明了。

本系统还存在以下不足,首先是没有对界面做更深入的优化,只是做了最基本的调整,让输入输出更加雅观,其次是底层原语的封装并没有考虑到泛用性。

三、系统详细设计这部分的文档在编码之前已经基本完成,由于时间较为仓促,部分内容可能和实际有所出入。

1.ChatServer类该类负责完成服务器所有操作。

1)类图2) 成员变量Map<SOCKET, string> m_clients 聊天者的SOCKET与昵称的映射fd_set m_fdSocket 可用套接字集合fd_set m_fdRead 有事件发生的套接字集合SOCKET m_sListen 监听SocketSOCKET m_sNowClient 当前处理的客户套接字int m_nPort 监听端口3) 方法设计void Bind()void Listen()void Select()int Recv()SOCKET Accept()封装底层原语,并加入异常机制,使得外部调用简约明了。

构造函数传入监听端口,初始化m_nPortStart()1)初始化监听套接字:void InitListenSocket()2)绑定套接字至本地机器:void Bind()3)进入监听模式(设置为非阻塞):void Listen()4)初始化可用套接字集合void InitFDSocket()5)死循环,调用select方法DoSelect()6)结束DoSelect()1)令m_fdRead = m_fdSocket2)调用Select()3)循环处理Select的结果DoFdRead(Socket sRead)4)结束DoFdRead(int iReadIndex)1)判断是否为m_sListen2)是m_sListen RecvNewConnect()3)否则令m_sNowClient = m_fdRead[iReadIndex],调用RecvNewMessage() RecvNewConnect()1)判断是否达到套接字上线2)调用Accept(),接收连接sClient3)添加sCilent 至m_fdSocket4)添加套接字至m_clients AddClientToInfoMap(string name) AddClientToInfoMap(string name)1)以SOKCET为键,name为值加入MAPRecvNewMessage()1)调用Recv函数2)是否为命令IsCommand(string str)3)是,则DoCommand(string cmd)4)否,则DoMessage(string msg)5)结束IsCommand(string str)1)判断是否以"/" 开头DoCommand(string cmd)1)判断指令,并解析命令与参数(argc, argv)2)调用指令处理函数3)假设只有SetName命令,那么则将对应的套接字的名称设置DoMessage(string msg)1)拼接消息与名字BuildMsg(string msg)2)在服务器上输出3)消息路由DispatchMessage(string msg)BuildMsg(string msg)1)从m_clients 中取出用户昵称2)拼接字符串,形成格式如下超人君(127.0.0.1) 23:49:48 说:大家好!即为:昵称(IP地址)时间说:消息正文3)返回DispatchMessage(string msg)1)构造迭代器2)遍历m_clients,若不是自身,则派送消息Send() 2.ChatClient 类该类负责处理客户端的所有操作。

1)类图2)字段设计SOCKET m_sClient 客户端自身的socketSOCKET m_sServer 服务器socketstring m_name 昵称sockaddr_in m_ServerAddr; 服务器地址3)方法设计构造函数根据端口号和服务器IP初始化m_serverConnect()void Select()int Recv()void Send()int Select()封装底层原语,加入异常处理,使得外部调用节约优雅。

void Start()1)初始化套接字InitClientSocket()2)连接服务器Connect() 设置为非阻塞模式3)获取名字并发送至服务器InitName()4)创建新线程并显示替他用户发言线程函数RecvMsgThread() 5)循环SendMsg()6)关闭客户端CloseClient()InitName()1)提示输入昵称2)获取昵称3)合法性判断判断重复4)添加命令格式5)发送至服务器SendMsg()1)读取一行消息2)判断是否为命令IsCommand(string str)3)命令:处理命令DoCommand(string cmd)4)消息:处理消息DoMessage(string msg)DoMessage(string msg)1)发送消息Send()2)本地回显RecvMsgThread()1)初始化fdSocket,将m_sClient加入2)创建fdRead3)死循环,将m_sClient拷贝至fdRead4)调用Select5)循环,并输出收到的消息Recv()3.SocketException类该类负责记录SOKCET错误的代码以及错误信息。

1)类图4.命令协议命令格式为/命令参数1 参数21. 退出: /exit2. 获取在线用户列表:/getuser3. 私聊:/m UID 信息4. 清屏:/clear5. 帮助:/help处理方式IsCommand(string str) 负责解析是否为命令判断首字母是否为斜杠"/" str.at(0) == '/'ResoveCommand(string cmd, int& argc, string argv[]) 若是命令将命令解析为argc,argv DoCommand(string cmd) 处理命令,调用具体的XXX命令处理函数DoCmdXXXX()。

5.消息格式1)公共消息超人君(127.0.0.1) UID:100 说:大家好!李四(127.0.0.1) UID:101 说:你好!!2)私聊你悄悄地对ABC UID:100 说:你好CDF UID:101 悄悄地对你说:你好3)服务器消息【系统消息】XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。

4)程序内部提示[System Info]xxxxxxxxxxxxxxxxxxxxxxxxx四、系统测试1. 服务器使用错误2. 客户端使用错误3. 启动服务器5.启动客户端客户端出现欢迎信息以及昵称输入提示。

服务器出现连接提示6.关闭客户端服务器出现断开连接提示7.启动服务器错误提示给出错误提示信息和提示代码8.公开聊天所有客户端以及服务器都会显示。

9.私聊只有私聊的二人才能看到聊天信息,其他用户和服务器无法看到。

10.错误的私聊私聊自己会得到一个错误提示私聊不存在的用户也会得到一个错误提示11.更名12.获取在线用户13.帮助14.非法指令非法指令会给出错误提示。

15.非法的指令参数16.连接服务器失败五、心得体会这次实现我深入研究了select模型的使用,完成了一个简易的聊天室。

这次试验也使我在编程技巧方面也有了很大的提高。

六、完整代码Charserverd.cpp 服务器main函数文件#include "ChatServer.h"#include "SockException.h"#include "InitSock.h"#include <iostream>using namespace std;InitSock initSock;int main(int argc, char* argv[]){if (argc < 2){cout << "Usage:" << argv[0] << " Port " << endl;return 1;}ChatServer charServer(atoi(argv[1]));try{charServer.Start();}catch (SockException& e){cout << e.GetErrorInfo() << endl;cout << "[System Error]Error Code:" << e.GetErrorCode() << endl;}}ChatServer.h 服务器类头文件#ifndef CHAT_SERVER_H#define CHAT_SERVER_H#include <WinSock2.h>#include <string>#include <map>#include "ClientInfo.h"using namespace std;class ChatServer{public:void Start();void End();ChatServer(int nPort);~ChatServer(void);private:void InitFDSocket();void DoSelect();void DoFDRead(SOCKET sRead);void RecvNewConnect();string IPAddrToString(sockaddr_in sin);void AddClientToInfoMap(ClientInfo info);void RecvNewMessage();bool IsCommand(string str);void DoCommand(string cmd);void ResoveCommand(string cmd, int& argc, string argv[]);void DoCmdName(int argc, string argv[]);void DoCmdGetUsers(int argc, string argv[]);void DoMessage(string msg);void DoCmdPrivateMsg(int argc, string argv[]);string BuildMessage(string str, bool bIsPublic);string BuildSystemMsg(string str);void DispatchMessage(string msg);void CloseConnect();string IntToString(int nNum);//============简单封装底层原语=============void InitListenSocket();void Bind();void Listen() ;int Select();int Recv(char msgBuff[]);void Send(string msg, SOCKET client);SOCKET Accept(sockaddr_in& sin);//==========================================private:map<SOCKET, ClientInfo> m_clients;fd_set m_fdSocket;fd_set m_fdRead;SOCKET m_sListen;SOCKET m_sNowClient;int m_nPort;};#endif CHAT_SERVER_HChatServer.cpp 服务器类#include <WinSock2.h>#include <iostream>#include "ChatServer.h"#include "SockException.h"#pragma comment(lib, "ws2_32.lib")using namespace std;#define MAX_BUFF_SIZE 500typedef map<SOCKET, ClientInfo>::iterator map_it;ChatServer::ChatServer(int nPort){this->m_nPort = nPort;}void ChatServer::Start(){InitListenSocket();Bind();Listen();InitFDSocket();while (true){DoSelect();}}void ChatServer::DoSelect(){m_fdRead = m_fdSocket;int nRet = Select();if (nRet > 0){for (int i = 0; i < m_fdRead.fd_count; i++){DoFDRead(m_fdRead.fd_array[i]);}}}void ChatServer::DoFDRead(SOCKET sRead){if (sRead == m_sListen){RecvNewConnect();}else{m_sNowClient = sRead;RecvNewMessage();}}void ChatServer::RecvNewConnect(){if (m_fdSocket.fd_count >= FD_SETSIZE){cout << "[System Info]接受连接达到上限,拒绝连接"<< endl;return;}sockaddr_in clientAddr;m_sNowClient = Accept(clientAddr);ClientInfo clientInfo(clientAddr);cout << "[System Info]接受来自"<< clientInfo.GetIp() << "的连接" << endl;FD_SET(m_sNowClient, &m_fdSocket);AddClientToInfoMap(clientInfo);}string ChatServer::IPAddrToString(sockaddr_in sin){string str = inet_ntoa(sin.sin_addr);str.append(":");char szFormat[20];str.append(ltoa( ntohs(sin.sin_port),szFormat,10));return str;}void ChatServer::AddClientToInfoMap(ClientInfo info){m_clients[m_sNowClient] = info;}void ChatServer::RecvNewMessage(){char msgBuff[MAX_BUFF_SIZE];int nRet = Recv(msgBuff);string msg(msgBuff);if (nRet <= 0) return;if (IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}void ChatServer::DoCommand(string cmd){string argv[100];ResoveCommand(cmd, argc, argv);if (argv[0] == "name"){DoCmdName(argc, argv);}else if (argv[0] == "list"){DoCmdGetUsers(argc, argv);}else if(argv[0] == "m"){DoCmdPrivateMsg(argc, argv);}else{Send("【系统消息】命令不存在,请使用/help 命令查看命令帮助", m_sNowClient);}}void ChatServer::ResoveCommand(string cmd, int& argc, string argv[]){int count = 0;for(int i = 1; i < cmd.size(); i++){char c = cmd.at(i);if (c != ' '){argv[count] += c;}else{count ++;}}argc = ++count;}void ChatServer::DoCmdGetUsers(int argc, string argv[]){if (argc != 1){return;}string online = IntToString(m_clients.size());msg.append("【系统消息】在线人数共" + online + "人:");msg.append( "\n\r");map_it begin = m_clients.begin();map_it end = m_clients.end();for (; begin != end; ++begin){msg.append("\t" + begin->second.GetName() + " " + begin->second.GetIp());msg.append(" UID:" + IntToString((int)begin->first));msg.append("\n\r");}Send(msg, m_sNowClient);}void ChatServer::DoCmdName(int argc, string argv[]){if (argc != 2){Send("【系统消息】命令格式错误USAGE: /name 你的昵称", m_sNowClient);return;}string name = argv[1];m_clients[m_sNowClient].SetName(name);Send("【系统消息】昵称已修改为:" + name, m_sNowClient);}void ChatServer::DoMessage(string str){string msg = BuildMessage(str, true);cout << msg << endl;DispatchMessage(msg);}/*string ChatServer::BuildMssage(string str){ClientInfo info = m_clients[m_sNowClient];string name = info.GetName();string ip = info.GetIp();string msg = name;if (name != ip){msg.append("(" + ip + ")");char buff[10];string id(itoa((int)m_sNowClient, buff, 10));msg.append(" UID:" + id);msg.append(" 说:");msg.append("\n\r ");msg.append(str);return msg;}*/string ChatServer::BuildSystemMsg(string str){string msg("【系统消息】");msg.append(str);return msg;}string ChatServer::BuildMessage(string str, bool bIsPublic){ClientInfo info = m_clients[m_sNowClient];string name = info.GetName();string ip = info.GetIp();string uid = IntToString((int)m_sNowClient);string msg(name);if (ip != name && bIsPublic == true){msg.append("(" + ip + ")");}msg.append(" ");msg.append("UID:" + uid);msg.append(" ");bIsPublic ? msg.append("说:") : msg.append("悄悄地对你说:");msg.append("\n\r");msg.append(" ");msg.append(str);return msg;}void ChatServer::DoCmdPrivateMsg(int argc, string argv[]){if (argc < 3){Send("【系统消息】命令格式错误USAGE: /m 目标UID 私聊内容", m_sNowClient);return;}SOCKET s = (SOCKET)atoi(argv[1].c_str());map_it it = m_clients.find(s);if (it == m_clients.end()){Send("【系统消息】该用户不存在", m_sNowClient);return;}if (it->first == m_sNowClient){Send("【系统消息】您不能和自己私聊", m_sNowClient);return;}string name = it->second.GetName();string uid = IntToString(s);string toDest = BuildMessage("", false);string toSrc = string("你悄悄地对" + name + " UID:" + uid + " 说:\n\r ");string other;for (int i = 2; i < argc; i++){other.append(argv[i]);other.append(" ");}Send(toDest + other, s);Send(toSrc + other, m_sNowClient);}void ChatServer::DispatchMessage(string msg){map_it begin = m_clients.begin();map_it end = m_clients.end();for (; begin != end; ++begin){Send(msg, begin->first);}}bool ChatServer::IsCommand(string str){if (str.at(0) == '/'){return true;}else{return false;}}void ChatServer::CloseConnect(){cout << "[System Info]来自" << m_clients[m_sNowClient].GetIp() << "的连接已断开" << endl;closesocket(m_sNowClient);FD_CLR(m_sNowClient,&m_fdSocket);}void ChatServer::InitFDSocket(){FD_ZERO(&m_fdSocket);FD_SET(m_sListen, &m_fdSocket);}void ChatServer::InitListenSocket(){m_sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (m_sListen == INVALID_SOCKET){throw SockException("[System Error]创建套接字失败:");}}void ChatServer::Bind(){sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_addr.S_un.S_addr = INADDR_ANY;sin.sin_port = htons(m_nPort);if (bind(m_sListen, (sockaddr*)&sin, sizeof(sin) )== SOCKET_ERROR){throw SockException("[System Error]无法绑定端口");}else{cout << "[System Info]成功绑定端口:" << m_nPort << endl;}}void ChatServer::Listen(){if (listen(m_sListen, 10) == SOCKET_ERROR){throw SockException("[System Error]服务器监听端口失败");}else{cout << "[System Info]服务器在" << m_nPort << "端口开始监听" << endl;}u_long value = 1;if (ioctlsocket(m_sListen, FIONBIO, &value)){throw SockException("[System Error]设置服务器为非阻塞模式失败");}}int ChatServer::Select(){int nRet = select(0, &m_fdRead, NULL, NULL, NULL);if (nRet == SOCKET_ERROR){throw SockException("[System Error]调用SELECT原语失败");}else{return nRet;}}SOCKET ChatServer::Accept(sockaddr_in& sin){int nLen = sizeof(sin);SOCKET sNew = accept(m_sListen, (sockaddr*)&sin, &nLen);if (sNew == INVALID_SOCKET){throw SockException("[System Error]调用ACCEPT原语接受连接失败");}return sNew;}int ChatServer::Recv(char msgBuff[]){int nRet = recv(m_sNowClient, msgBuff, MAX_BUFF_SIZE, 0);if (nRet == SOCKET_ERROR){CloseConnect();return 0;}else{msgBuff[nRet] = '\0';return nRet;}}void ChatServer::Send(string msg, SOCKET client){if (send(client, msg.c_str(), msg.size(), 0) == SOCKET_ERROR){string errorIp = m_clients[client].GetIp();throw SockException("[System Error]发送信息" + msg + "至" + errorIp + "失败");}}string ChatServer::IntToString(int nNum){char buff[10];string str(itoa(nNum, buff, 10));return str;}ChatServer::~ChatServer(void){}ClientInfo.h 客户信息类#include <string>using namespace std;class ClientInfo{public:ClientInfo(){}ClientInfo(string ip, int port, string name){this->ip = ip;this->port = port;this->name = name;}ClientInfo(sockaddr_in sin){ip = inet_ntoa(sin.sin_addr);//port = ntohs(sin.sin_port);name = ip;//name.append(":");//char szFormat[20];//name.append(ltoa( ntohs(sin.sin_port),szFormat,10));}string GetIp(){return ip;}string GetName(){return name;}int GetPort(){return port;}void SetName(string name){this->name = name;}private:string name;string ip;int port;};Charclientd.cpp 客户端main函数#include "SockException.h"#include "InitSock.h"#include <iostream>#include "ChatClient.h"using namespace std;InitSock initSock;int main(int argc, char* argv[]){try{if (argc < 3){cout << "Usage:" << argv[0] << " Ip Port " << endl;return 1;}ChatClient client(argv[1],atoi(argv[2]));client.Start();}catch (SockException& e){cout << e.GetErrorInfo() << endl;cout << "[System Error]Error Code:" << e.GetErrorCode() << endl;}}ChatClient.h 客户端头文件#ifndef CHAT_CLIENT_H#define CHAT_CLIENT_H#include <string>#include <WinSock2.h>using namespace std;class ChatClient{public:ChatClient(string addr, int nPort);~ChatClient(void);void Start();void End();SOCKET GetClientSock();private:void Connect();int Recv();void Send(string msg);int Select();void InitClientSocket();string IPAddrToString(sockaddr_in sin);void Welcome();void SendMsg();void InitName();bool IsCommand(string str);void DoMessage(string msg);void DoCommand(string cmd);void ResoveCommand(string cmd, int& argc, string argv[]);string GetLine();private:string m_name;SOCKET m_sClient;SOCKET m_sServer;sockaddr_in m_serverAddr;};#endif CHAT_CLIENT_HChatClient.cpp 客户端类#include <WinSock2.h>#include <iostream>#include <process.h>#include <Windows.h>#include "ChatClient.h"#include "SockException.h"#pragma comment(lib, "ws2_32.lib")#define MAX_BUFF_SIZE 500using namespace std;void RecvMsgThread(void * param);ChatClient::ChatClient(string addr, int nPort)m_serverAddr.sin_addr.S_un.S_addr = inet_addr(addr.c_str());m_serverAddr.sin_port = htons(nPort);m_serverAddr.sin_family = AF_INET;}ChatClient::~ChatClient(void){}void ChatClient::Start(){InitClientSocket();Connect();Welcome();Sleep(1000);InitName();_beginthread(RecvMsgThread,NULL,(void*)this);while (true){SendMsg();}}void ChatClient::Welcome(){cout << "=============================================================" << endl;cout << " ******欢迎使用简易聊天室****** " << endl;cout << "操作指南:1)在控制台中直接输入后,按回车即可发送公共聊天消息" << endl;cout << " 2)以斜杠(/)开头的消息将会当做命令处理" << endl;cout << " " << endl;cout << "命令指南:1)退出USAGE: /exit " << endl;cout << " 2)获取在线用户USAGE: /list " << endl;cout << " 3)更改昵称USAGE: /name 你的昵称" << endl;cout << " 4)私聊USAGE: /m 对方UID 消息" << endl;cout << " 5)帮助信息USAGE: /help " << endl;cout << "=============================================================" << endl;}void ChatClient::InitName()string name;cout << "请输入昵称:";name = GetLine();Send("/name " + name);}void ChatClient::SendMsg(){string msg;msg = GetLine();if (msg == ""){return;}if (IsCommand(msg)){DoCommand(msg);}else{DoMessage(msg);}}string ChatClient::GetLine(){string input;getline(cin, input);HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);CONSOLE_SCREEN_BUFFER_INFO bInfo;GetConsoleScreenBufferInfo(hOut, &bInfo);COORD pos;pos.X = bInfo.dwCursorPosition.X;pos.Y = bInfo.dwCursorPosition.Y - 1;FillConsoleOutputCharacter(hOut, ' ', input.size(), pos, NULL);SetConsoleCursorPosition(hOut, pos);return input;}void ChatClient::DoCommand(string cmd){int argc;string argv[100];ResoveCommand(cmd, argc, argv);if (argv[0] == "exit"){//system("exit");}else if (argv[0] == "help"){Welcome();}else{DoMessage(cmd);}}void ChatClient::DoMessage(string msg){//TODOSend(msg);}void ChatClient::ResoveCommand(string cmd, int& argc, string argv[]) {int count = 0;for(int i = 1; i < cmd.size(); i++){char c = cmd.at(i);if (c != ' '){argv[count] += c;}else{count ++;}}argc = ++count;}bool ChatClient::IsCommand(string str)if (str.at(0) == '/'){return true;}else{return false;}}void ChatClient::InitClientSocket(){m_sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (m_sClient == INVALID_SOCKET){throw SockException("[System Error]创建套接字失败");}}void ChatClient::Connect(){if (connect(m_sClient, (sockaddr*)&m_serverAddr, sizeof(m_serverAddr)) == SOCKET_ERROR){throw SockException("[System Error]连接服务器:" + IPAddrToString(m_serverAddr) + "失败");}else{cout << "[System Info]连接服务器" + IPAddrToString(m_serverAddr) + "成功" << endl;}}void ChatClient::Send(string msg){if (send(m_sClient, msg.c_str(), msg.size(), 0) == SOCKET_ERROR){throw SockException("[System Error]发送消息:" + msg + "失败");}}string ChatClient::IPAddrToString(sockaddr_in sin){string str = inet_ntoa(sin.sin_addr);str.append(":");char szFormat[20];str.append(ltoa( ntohs(sin.sin_port),szFormat,10));return str;}void RecvMsgThread(void * param){ChatClient *cp = (ChatClient*)param;fd_set fdSocket;FD_ZERO(&fdSocket);FD_SET(cp->GetClientSock(), &fdSocket);fd_set fdRead;char msgBuff[MAX_BUFF_SIZE];while (true){fdRead = fdSocket;if (select(0,&fdRead,NULL,NULL,NULL)>0){for (int i = 0; i< fdRead.fd_count; i++){int nRet = recv(fdRead.fd_array[i], msgBuff,MAX_BUFF_SIZE,0);if ( nRet > 0){msgBuff[nRet] = '\0';cout << msgBuff << endl;}}}}}SOCKET ChatClient::GetClientSock(){return m_sClient;}InitSock.cpp 初始化WINSOCK类#ifndef INITSOCK_H#define INITSOCK_H#include <winsock2.h>#pragma comment(lib, "WS2_32")class InitSock{public:InitSock(BYTE minorVer = 2, BYTE majorVer = 2){WSADATA wsaData;WORD sockVersion = MAKEWORD(minorVer, majorVer);if(WSAStartup(sockVersion, &wsaData) != 0){exit(0);}}~InitSock(){WSACleanup();}};#endif INITSOCK_HSockException.h 异常类#ifndef SOCK_EXCEPTION_H#define SOCK_EXCEPTION_H#include <string>#include <WinSock2.h>using namespace std;class SockException{public:string GetErrorInfo(){return m_strErrorInfo;}int GetErrorCode(){return m_nErrorCode;}void SetErrorCode(string strErrorCode);void SetErrorInfo(string strErrorInfo);SockException() {}SockException(int nErrorCode, string strErrorInfo){m_nErrorCode = nErrorCode;m_strErrorInfo = strErrorInfo;}SockException(string strErrorInfo){int nErrorCode = WSAGetLastError();m_nErrorCode = nErrorCode;m_strErrorInfo = strErrorInfo;}private:int m_nErrorCode;string m_strErrorInfo;};#endif SOCK_EXCEPTION_H。

相关主题