版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
基于TCP协议编程的网络聊天室设计内容:基于TCP协议编程的方式,编写程序模拟网络聊天室的运营过程。设计规定:1.采用C/S模式,基于TCP协议编程的方式,使得各个用户通过服务器转发实现聊天的功能。2.分为两大模块:客户端模块和服务器端模块。3.客户端模块的重要功能:1)登陆功能:用户可以注册,然后选择服务器登入聊天室。2)显示用户:将在线用户显示在列表中。3)接受信息:能接受其他用户发出的信息。4)发送信息:能发出用户要发出的信息。4.服务器端模块的重要功能:1)检查登陆信息:检查登陆信息是否对的,并向客户端返回登陆信息,如信息对的。就允许用户登陆。2)显示在线状态:将该用户的状态发给各在线用户。3)转发聊天信息:将消息转发给所有在线的用户。5.编程语言不限。需求分析此程序重要分为两部分:服务器端和客户端。服务器端用于提供一个网络端口,等待客户端发出请求,登录到此服务端,然后进行网络通讯和消息的转发;客户端可通过服务器端的IP地址发送连接请求,然后登陆聊天室。在服务器端的成员列表栏中会显示在线的所有人名单,有人退出聊天室,成员列表会自动除名。整个程序的主体使用了CSocket类的方法,实现了网络通讯聊天。整个程序设计为两个部分:服务器(SpeakerServer)和客户端(SpeakerClient)
。多人聊天的关键在于要将每个客户端发送过来的消息分发给所有其他客户端,为了解决这个问题,在服务器程序中建立一个套接口链表,用来保存所有与客户端建立了连接的服务端口。设计原理:服务器通过socket()系统调用创建一个Socket数组后(设定了接受连接客户的最大数目),与指定的本地端口绑定bind(),就可以在端口进行侦听listen()。假如有客户端连接请求,则在数组中选择一个空socket,将客户端地址赋给这个socket,然后登陆成功的客户就可以在服务器上聊天了。客户端程序相对简朴,只要建立一个socket与服务器端连接,成功后通过这个socket来发送和接受就可以了。服务器端功能:初始化socket,创建服务器端。维护一个链表,保存所有用户的IP地址,端口信息。接受用户传送来的聊天信息,然后向链表中的所用用户转发。接受用户传送来的连接判断命令,并向用户发出响应命令。客户端功能:客户端界面上的两个文本框,一个用于显示接受的聊天信息,一个用来接受用户输入的聊天信息。当按下“发送”按钮时将信息发送给服务器。概要设计:服务器客户端(设计流程图)具体设计:服务器端:1、启动服务器代码://服务器启动时,先创建套接字并绑定端口,再监听此端口。voidCSpeakerServerDlg::OnBnClickedStart(){ UINTuPort=GetDlgItemInt(IDC_PORT); //创建套接字 if(!m_TCPSocketListen.Create(uPort)) { m_TraceRichEdit.TraceString(TEXT("绑定监听端口失败,请确认该端口没有被其它程序占用"),TraceLevel_Warning); return; } //监听套接字 if(!m_TCPSocketListen.Listen()) { m_TraceRichEdit.TraceString(TEXT("监听失败"),TraceLevel_Warning); return; } UINTuMaxConnect=GetDlgItemInt(IDC_MAX); //设立接口 m_TCPSocketListen.SetTCPSocketService(this); //更新界面 m_TraceRichEdit.TraceString(TEXT("服务器启动成功"),TraceLevel_Normal); GetDlgItem(IDC_START)->EnableWindow(FALSE); GetDlgItem(IDC_STOP)->EnableWindow(TRUE);}2、监听端口,收到连接请求,接受的代码://先检查是否在服务器的最大连接限制内,若在,则获取当前客户的IP地址和端口等信息,插入链表中。//为什么要限制连接人数?由于TCP连接是相称占资源的,若不限制连接人数,服务器的资源不够分派。voidCSpeakerServerDlg::OnAccept(){ //承载能力 if(m_TCPSocketItemMap.size()>GetDlgItemInt(IDC_MAX)) { m_TraceRichEdit.TraceString(TEXT("服务器承载人数已满,已过滤其他连接"),TraceLevel_Warning); return; } //绑定套接字 CTCPSocketService*pTCPSocketConnect=newCTCPSocketService; try { SOCKADDR_IN SocketAddr; intnBufferSize=sizeof(SocketAddr); //连接 m_TCPSocketListen.Accept(*pTCPSocketConnect,(SOCKADDR*)&SocketAddr,&nBufferSize); if(pTCPSocketConnect->m_hSocket==INVALID_SOCKET)throwTEXT("无效的连接套接字"); //获取客户端IP pTCPSocketConnect->m_dwClientAddr=SocketAddr.sin_addr.S_un.S_addr; pTCPSocketConnect->SetTCPSocketService(this); //绑定数据 boolbActive=true; CTCPSocketItemMap::iteratoriter=m_TCPSocketItemMap.begin(); for(;iter!=m_TCPSocketItemMap.end();iter++) { if(pTCPSocketConnect->m_hSocket==iter->first) { bActive=false; break; } } //插入客户数据 if(bActive) { tagBindParameter*pBindParameter=newtagBindParameter; pBindParameter->pTCPSocketService=pTCPSocketConnect; pBindParameter->dwUserID=0; m_TCPSocketItemMap.insert(pair<SOCKET,tagBindParameter*>(pTCPSocketConnect->m_hSocket,pBindParameter)); } } catch(...) { if(pTCPSocketConnect->m_hSocket!=INVALID_SOCKET) pTCPSocketConnect->Close(); }}3、接受并检查数据的代码:voidCSpeakerServerDlg::OnReceive(SOCKEThSocket){ BYTEcbDataBuffer[SOCKET_TCP_BUFFER]; CTCPSocketItemMap::iteratoriter=m_TCPSocketItemMap.find(hSocket); if(iter==m_TCPSocketItemMap.end())return; //接受数据 iter->second->pTCPSocketService->Receive(cbDataBuffer,CountArray(cbDataBuffer)); //解析数据 TCP_Command*pCommand=(TCP_Command*)cbDataBuffer; //解释数据 WORDwPacketSize=pCommand->wPacketSize; WORDwDataSize=wPacketSize-sizeof(TCP_Command); //数据包效验 if(wPacketSize>SOCKET_TCP_BUFFER+sizeofTCP_Command) { m_TraceRichEdit.TraceString(TEXT("数据包太大,已拒绝"),TraceLevel_Warning); return; } //子消息解决事件 if(!OnEventTCPSocketRead(hSocket,pCommand->wMainCmdID,pCommand->wSubCmdID,pCommand+1,wDataSize)) { BYTE*pClientIP=(BYTE*)&iter->second->pTCPSocketService->m_dwClientAddr; m_TraceRichEdit.TraceString(TraceLevel_Warning,TEXT("收到伪数据包或未解决的数据包,wMainCmdID:%d,wSubCmdID:%d,来源IP:%d.%d.%d.%d"),pCommand->wMainCmdID,pCommand->wSubCmdID,pClientIP[0],pClientIP[1],pClientIP[2],pClientIP[3]); return; }}4、群发登录消息和用户发送的消息代码://服务器收到客户的消息之后会将收到的消息发送给链表之中除了发送客户之外的所有客户。boolCSpeakerServerDlg::OnEventTCPSocketRead(SOCKEThSocket,WORDwMainCmdID,WORDwSubCmdID,VOID*pData,WORDwDataSize){ //获取绑定套接字 CTCPSocketItemMap::iteratoriter=m_TCPSocketItemMap.find(hSocket); if(iter==m_TCPSocketItemMap.end())returnfalse; CTCPSocketService*pTCPSocketService=iter->second->pTCPSocketService; switch(wMainCmdID) { caseMDM_GP_LOGON: { if(wSubCmdID==SUB_CS_LOGON) { //效验数据 ASSERT(wDataSize==sizeofCMD_CS_LOGON); if(wDataSize!=sizeofCMD_CS_LOGON)returnfalse; //获取数据 CMD_CS_LOGON*pUserLogon=(CMD_CS_LOGON*)pData; m_TraceRichEdit.TraceString(TraceLevel_Normal,TEXT("%s登陆服务器"),pUserLogon->szUserName); tagUserData*pUserData=newtagUserData; //随机给用户分派一个UserID,UserID一般存储于数据库中,是一个独一无二的数字, //一般在数据库表中设为主键,是整个游戏或者软件辨认用户的唯一依据,这里我们没有涉及到数据库,暂时随机取一个数值代替 //另一方面,我们应当通过数据库SQL语句查询或者存储过程等方法,或在数据库中做密码的效验也好, //或在查询到用户的密码在服务器中进行判断也好,不管什么方法,此处一般需要进行用户密码的效验,这样才可以鉴定用户是否可以登陆了 pUserData->dwUserID=GetTickCount(); _sntprintf_s(pUserData->szUserName,CountArray(pUserData->szUserName),pUserLogon->szUserName); _sntprintf_s(pUserData->szPassWord,CountArray(pUserData->szPassWord),pUserLogon->szPassWord); //更新绑定数据 CTCPSocketItemMap::iteratoriter=m_TCPSocketItemMap.find(hSocket); if(iter!=m_TCPSocketItemMap.end()) iter->second->dwUserID=pUserData->dwUserID; //群发登陆消息 SendUserItem(NULL,pUserData); //发送在线用户 CUserItemArray::iteratorpUserItemSend=m_pUserManager->GetUserItemArray()->begin(); for(;pUserItemSend!=m_pUserManager->GetUserItemArray()->end();pUserItemSend++) SendUserItem(pTCPSocketService,pUserItemSend->second); //插入数据 m_pUserManager->InsertUserItem(pUserData); returntrue; } } break; caseMDM_GP_USER: { if(wSubCmdID==SUB_CS_USERT_CHAT) { //获取数据 CMD_CS_CHATMSG*pCHATMSG=(CMD_CS_CHATMSG*)pData; //这里其实需要做很多的效验,如dwSendUserID的有效性,字符串是否为空等,这里就不做这些效验了 CMD_SC_CHATMSG_SC_CHATMSG; ZeroMemory(&_SC_CHATMSG,sizeof_SC_CHATMSG); //获取时间 GetLocalTime(&_SC_CHATMSG.SystemTime); _sntprintf_s(_SC_CHATMSG.szSendUserName,CountArray(_SC_CHATMSG.szSendUserName),m_pUserManager->GetUserName(iter->second->dwUserID)); _sntprintf_s(_SC_CHATMSG.szDescribe,CountArray(_SC_CHATMSG.szDescribe),pCHATMSG->szDescribe); SendDataBatch(MDM_GP_USER,SUB_CS_USERT_CHAT,&_SC_CHATMSG,sizeof_SC_CHATMSG); returntrue; } } break; } returnfalse;}5、当服务器端有人退出登录时的代码://客户端退出时,服务器端获取用户名并群发退出消息,再在链表中删除该用户的数据,清理他的SocketvoidCSpeakerServerDlg::OnClose(SOCKEThSocket){ CTCPSocketItemMap::iteratoriter=m_TCPSocketItemMap.find(hSocket); if(iter==m_TCPSocketItemMap.end())return; //获取用户 m_TraceRichEdit.TraceString(TraceLevel_Normal,TEXT("%s退出了服务器"),m_pUserManager->GetUserName(iter->second->dwUserID)); //删除用户 CMD_DC_DELETE_DC_DELETE; ZeroMemory(&_DC_DELETE,sizeof_DC_DELETE); _sntprintf_s(_DC_DELETE.szUserName,CountArray(_DC_DELETE.szUserName),m_pUserManager->GetUserName(iter->second->dwUserID)); //群发消息 SendDataBatch(MDM_GP_USER,SUB_SC_DELETE,&_DC_DELETE,sizeof_DC_DELETE); //销毁数据 m_pUserManager->RemoveUserItem(iter->second->dwUserID); iter->second->pTCPSocketService->Close(); SafeDelete(iter->second->pTCPSocketService); SafeDelete(iter->second); m_TCPSocketItemMap.erase(iter);}关闭服务器连接代码:voidCSpeakerServerDlg::OnBnClickedStop(){ //关闭监听套接字 m_TCPSocketListen.Close(); //关闭连接套接字 CTCPSocketItemMap::iteratoriter=m_TCPSocketItemMap.begin(); for(;iter!=m_TCPSocketItemMap.end();++iter) { iter->second->pTCPSocketService->Close(); SafeDelete(iter->second->pTCPSocketService); SafeDelete(iter->second); } //更新界面 m_TraceRichEdit.TraceString(TEXT("服务器关闭成功"),TraceLevel_Normal); GetDlgItem(IDC_START)->EnableWindow(TRUE); GetDlgItem(IDC_STOP)->EnableWindow(FALSE);}7、退出服务器代码:voidCSpeakerServerDlg::OnCancel(){ if(m_TCPSocketListen.m_hSocket!=INVALID_SOCKET) { if(AfxMessageBox(TEXT("拟定退出服务器吗?其它所有用户将失去连接"),MB_YESNO|MB_ICONQUESTION)==IDYES) { CTCPSocketItemMap::iteratoriter=m_TCPSocketItemMap.begin(); for(;iter!=m_TCPSocketItemMap.end();++iter) { iter->second->pTCPSocketService->Close(); SafeDelete(iter->second->pTCPSocketService); SafeDelete(iter->second); } } } __super::OnCancel();}客户端:1、客户端登录://登陆消息LRESULTCSpeakerClientDlg::OnLogonMessage(WPARAMwParam,LPARAMlParam){ tagLogonInfo*pLogonInfo=(tagLogonInfo*)wParam; //关闭之前socket m_TCPScoketClient.Close(); //初始化套接字 if(!m_TCPScoketClient.Create()) { SetTraceString(TEXT("套接字创建失败")); SafeDelete(pLogonInfo); returnFALSE; } //创建连接 if(m_TCPScoketClient.Connect(pLogonInfo->szServerAddr,pLogonInfo->nPort)==FALSE) { intnErrorCode=m_TCPScoketClient.GetLastError(); if(nErrorCode!=WSAEWOULDBLOCK) { SetTraceString(TEXT("连接服务器失败,错误码:%d"),nErrorCode); SafeDelete(pLogonInfo); returnFALSE; } } //设立接口 m_TCPScoketClient.SetTCPSocketService(this); //构建数据 CMD_CS_LOGONUserLogon; ZeroMemory(&UserLogon,sizeofUserLogon); _sntprintf_s(UserLogon.szUserName,CountArray(UserLogon.szUserName),pLogonInfo->szUserName); _sntprintf_s(UserLogon.szPassWord,CountArray(UserLogon.szPassWord),pLogonInfo->szPassWord); //发送登陆请求 m_TCPScoketClient.SendData(MDM_GP_LOGON,SUB_CS_LOGON,&UserLogon,sizeofUserLogon); //设立界面 SetTraceString(TEXT("连接服务器成功")); m_LogonDlg.PostMessage(WM_CLOSE); //清理数据 SafeDelete(pLogonInfo); returnTRUE;}2、客户端发送数据代码:voidCSpeakerClientDlg::OnBnClickedSend(){ //设立数据 CMD_CS_CHATMSG_UserChat_Msg; ZeroMemory(&_UserChat_Msg,sizeof_UserChat_Msg); GetDlgItemText(IDC_EDITCHAT,_UserChat_Msg.szDescribe,CountArray(_UserChat_Msg.szDescribe)); //效验数据 if(_UserChat_Msg.szDescribe[0]==TEXT('\0')) { SetTraceString(TEXT("聊天内容为空,请先输入您想说的话")); return; } //发送数据 m_TCPScoketClient.SendData(MDM_GP_USER,SUB_CS_USERT_CHAT,&_UserChat_Msg,sizeof_UserChat_Msg);}3、客户端接受数据代码://客户端接受数据和服务器段类似,也需解析、检查voidCSpeakerClientDlg::OnReceive(intnErrorCode){ //接受消息 BYTEcbDataBuffer[SOCKET_TCP_BUFFER]; m_TCPScoketClient.Receive(cbDataBuffer,CountArray(cbDataBuffer)); //解析数据 TCP_Command*pCommand=(TCP_Command*)cbDataBuffer; //解释数据 WORDwPacketSize=pCommand->wPacketSize; WORDwDataSize=wPacketSize-sizeof(TCP_Command); //数据包效验 if(wPacketSize>SOCKET_TCP_BUFFER+sizeofTCP_Command) { SetTraceString(TEXT("数据包太大,已拒绝")); return; } //子消息解决事件 if(!OnEventTCPSocketRead(pCommand->wMainCmdID,pCommand->wSubCmdID,pCommand+1,wDataSize)) { SetTraceString(TEXT("收到未解决的数据包,wMainCmdID:%d,wSubCmdID:%d"),pCommand->wMainCmdID,pCommand->wSubCmdID); return; }}4、客户端消息的显示代码://显示的消息类型:当用户登录时,将用户数据插入用户列表中。服务器端会有xx登录的显示。当用户发消息时,服务器端就可以转发该消息给用户链表的所有其他用户。用户退出时,同理,客户端也会接受到XX退出了的消息。boolCSpeakerClientDlg::OnEventTCPSocketRead(WORDwMainCmdID,WORDwSubCmdID,VOID*pData,WORDwDataSize){ switch(wMainCmdID) { caseMDM_GP_LOGON: //登陆消息 { if(wSubCmdID==SUB_SC_USERCOME) //用户进入 { CMD_SC_USERCOME*pUserCome=(CMD_SC_USERCOME*)pData; //插入数据 if(m_ListUser.FindString(-1,pUserCome->szUserName)==LB_ERR) { //设立自己信息 if(m_UserData.dwUserID==0) { _sntprintf_s(m_UserData.szUserName,CountArray(m_UserData.szUserName),pUserCome->szUserName); m_UserData.dwUserID=m_UserData.dwUserID; SetWindowText(m_UserData.szUserName); } //添加用户列表 m_ListUser.AddString(pUserCome->szUserName); m_ListUser.SetItemData(m_ListUser.GetCount()-1,pUserCome->dwUserID); } returntrue; } break; } caseMDM_GP_USER: //用户消息 { if(wSubCmdID==SUB_CS_USERT_CHAT) //聊天消息 { CMD_SC_CHATMSG*pCHATMSG=(CMD_SC_CHATMSG*)pData; //设立聊天数据 staticCStringstr; CStringStrDescribe; StrDescribe.Format(TEXT("%s%04d-%02d-%02d%02d:%02d:%02d\r\n"),pCHATMSG->szSendUserName,pCHATMSG->SystemTime.wYear,pCHATMSG->SystemTime.wMonth, pCHATMSG->SystemTime.wDay,pCHATMSG->SystemTime.wHour,pCHATMSG->SystemTime.wMinute,pCHATMSG->SystemTime.wSecond); str+=StrDescribe; str+=pCHATMSG->szDescribe; str+=TEXT("\r\n"); SetDlgItemText(IDC_CHATRECV,str); returntrue; } elseif(wSubCmdID==SUB_SC_DELETE
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 二零二五年度幼儿园儿童床垫定制采购合同3篇
- 2025年度人工智能教育培训合作合同7篇
- 2025年厂房钢结构工程环保验收与监测合同4篇
- 2024铁路消防安全管理与应急预案合同3篇
- 2025年度健康生活APP定制化功能开发合同3篇
- 「可靠」2024年度广告位租赁合同3篇
- 2025年度科技园区场地租赁与合作开发合同范本4篇
- 2024版建筑渣土清运协议样本版
- 2025年度新能源车辆充电设施安装与维护合同3篇
- 2025年度叉车司机安全操作与事故责任认定合同4篇
- 银行信息安全保密培训
- 市政道路工程交通疏解施工方案
- 2024年部编版初中七年级上册历史:部分练习题含答案
- 拆迁评估机构选定方案
- 床旁超声监测胃残余量
- 上海市松江区市级名校2025届数学高一上期末达标检测试题含解析
- 综合实践活动教案三上
- 《新能源汽车电气设备构造与维修》项目三 新能源汽车照明与信号系统检修
- 2024年新课标《义务教育数学课程标准》测试题(附含答案)
- 医院培训课件:《静脉中等长度导管临床应用专家共识》
- 中国国际大学生创新大赛与“挑战杯”大学生创业计划竞赛(第十一章)大学生创新创业教程
评论
0/150
提交评论