您好,欢迎访问三七文档
当前位置:首页 > 办公文档 > 其它办公文档 > 计算机网络课设-基于TCP协议编程的网络聊天室
基于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来发送和接收就可以了。服务器端功能:1、初始化socket,创建服务器端。2、维护一个链表,保存所有用户的IP地址,端口信息。3、接受用户传送来的聊天信息,然后向链表中的所用用户转发。4、接受用户传送来的连接判断命令,并向用户发出响应命令。客户端功能:客户端界面上的两个文本框,一个用于显示接受的聊天信息,一个用来接受用户输入的聊天信息。当按下“发送”按钮时将信息发送给服务器。一、概要设计:服务器客户端(设计流程图)二、详细设计:服务器端: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_INSocketAddr;intnBufferSize=sizeof(SocketAddr);//连接m_TCPSocketListen.Accept(*pTCPSocketConnect,(SOCKADDR*)&SocketAddr,&nBufferSize);if(pTCPSocketConnect-m_hSocket==INVALID_SOCKET)throwTEXT(无效的连接套接字);//获取客户端IPpTCPSocketConnect-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(pairSOCKET,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(wPacketSizeSOCKET_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_TCP
本文标题:计算机网络课设-基于TCP协议编程的网络聊天室
链接地址:https://www.777doc.com/doc-4308442 .html