欢迎来到淘文阁 - 分享文档赚钱的网站! | 帮助中心 好文档才是您的得力助手!
淘文阁 - 分享文档赚钱的网站
全部分类
  • 研究报告>
  • 管理文献>
  • 标准材料>
  • 技术资料>
  • 教育专区>
  • 应用文书>
  • 生活休闲>
  • 考试试题>
  • pptx模板>
  • 工商注册>
  • 期刊短文>
  • 图片设计>
  • ImageVerifierCode 换一换

    网络编程-基于TCP的简易聊天室-实验报告(共40页).doc

    • 资源ID:13659326       资源大小:488KB        全文页数:40页
    • 资源格式: DOC        下载积分:20金币
    快捷下载 游客一键下载
    会员登录下载
    微信登录下载
    三方登录下载: 微信开放平台登录   QQ登录  
    二维码
    微信扫一扫登录
    下载资源需要20金币
    邮箱/手机:
    温馨提示:
    快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。
    如填写123,账号就是123,密码也是123。
    支付方式: 支付宝    微信支付   
    验证码:   换一换

     
    账号:
    密码:
    验证码:   换一换
      忘记密码?
        
    友情提示
    2、PDF文件下载后,可能会被浏览器默认打开,此种情况可以点击浏览器菜单,保存网页到桌面,就可以正常下载了。
    3、本站不支持迅雷下载,请使用电脑自带的IE浏览器,或者360浏览器、谷歌浏览器下载即可。
    4、本站资源下载后的文档和图纸-无水印,预览文档经过压缩,下载后原文更清晰。
    5、试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。

    网络编程-基于TCP的简易聊天室-实验报告(共40页).doc

    精选优质文档-倾情为你奉上网络编程课程设计 -基于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_fdReadiReadIndex,调用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. 获取在线用户列表:/getuser 3. 私聊: /m UID 信息4. 清屏:/clear5. 帮助:/help处理方式IsCommand(string str) 负责解析是否为命令判断首字母是否为斜杠"/" str.at(0) = '/'ResoveCommand(string cmd, int& argc, string argv) 若是命令将命令解析为argc,argvDoCommand(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 Infoxxxxxxxxxxxxxxxxxxxxxxxxx四、 系统测试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:" << argv0 << " Port " << endl;return 1;ChatServer charServer(atoi(argv1);trycharServer.Start();catch (SockException& e)cout << e.GetErrorInfo() << endl;cout << "System ErrorError 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 ChatServerpublic: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_arrayi);void ChatServer:DoFDRead(SOCKET sRead)if (sRead = m_sListen)RecvNewConnect();elsem_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 szFormat20; str.append(ltoa( ntohs(sin.sin_port),szFormat,10);return str;void ChatServer:AddClientToInfoMap(ClientInfo info)m_clientsm_sNowClient = info;void ChatServer:RecvNewMessage()char msgBuffMAX_BUFF_SIZE;int nRet = Recv(msgBuff);string msg(msgBuff);if (nRet <= 0) return;if (IsCommand(msg)DoCommand(msg);elseDoMessage(msg);void ChatServer:DoCommand(string cmd)int argc;string argv100;ResoveCommand(cmd, argc, argv);if (argv0 = "name")DoCmdName(argc, argv);else if (argv0 = "list")DoCmdGetUsers(argc, argv);else if(argv0 = "m")DoCmdPrivateMsg(argc, argv);elseSend("【系统消息】命令不存在,请使用 /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 != ' ')argvcount += c;elsecount +;argc = +count;void ChatServer:DoCmdGetUsers(int argc, string argv)if (argc != 1)return;string msg;string online = IntToString(m_clients.size();msg.append("【系统消息】在线人数共" + online + "人:");msg.append( "nr");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("nr");Send(msg, m_sNowClient);void ChatServer:DoCmdName(int argc, string argv)if (argc != 2)Send("【系统消息】命令格式错误 USAGE: /name 你的昵称", m_sNowClient);return;string name = argv1;m_clientsm_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_clientsm_sNowClient;string name = info.GetName();string ip = info.GetIp();string msg = name;if (name != ip)msg.append("(" + ip + ")");char buff10;string id(itoa(int)m_sNowClient, buff, 10);msg.append(" UID:" + id);msg.append(" 说:");msg.append("nr ");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_clientsm_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("nr");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(argv1.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 + " 说:nr ");string other;for (int i = 2; i < argc; i+)other.append(argvi);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;elsereturn false;void ChatServer:CloseConnect()cout << "System Info来自" << m_clientsm_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无法绑定端口");elsecout << "System Info成功绑定端口:" << m_nPort << endl;void ChatServer:Listen()if (listen(m_sListen, 10) = SOCKET_ERROR)throw SockException("System Error服务器监听端口失败");elsecout << "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原语失败");elsereturn 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;elsemsgBuffnRet = '0'return nRet;void ChatServer:Send(string msg, SOCKET client)if (send(client, msg.c_str(), msg.size(), 0) = SOCKET_ERROR)string errorIp = m_clientsclient.GetIp();throw SockException("System Error发送信息" + msg + "至" + errorIp + "失败");string ChatServer:IntToString(int nNum)char buff10;string str(itoa(nNum, buff, 10);return str;ChatServer:ChatServer(void)ClientInfo.h 客户信息类#include <string>using namespace std;class ClientInfopublic:ClientInfo()Clien

    注意事项

    本文(网络编程-基于TCP的简易聊天室-实验报告(共40页).doc)为本站会员(飞****2)主动上传,淘文阁 - 分享文档赚钱的网站仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知淘文阁 - 分享文档赚钱的网站(点击联系客服),我们立即给予删除!

    温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载不扣分。




    关于淘文阁 - 版权申诉 - 用户使用规则 - 积分规则 - 联系我们

    本站为文档C TO C交易模式,本站只提供存储空间、用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。本站仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知淘文阁网,我们立即给予删除!客服QQ:136780468 微信:18945177775 电话:18904686070

    工信部备案号:黑ICP备15003705号 © 2020-2023 www.taowenge.com 淘文阁 

    收起
    展开