《计算机网络课设_基于TCP协议书编程的网络聊天室.pdf》由会员分享,可在线阅读,更多相关《计算机网络课设_基于TCP协议书编程的网络聊天室.pdf(13页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、.基于基于 TCPTCP 协议编程的网络聊天室协议编程的网络聊天室设计内容:基于 TCP 协议编程的方式,编写程序模拟网络聊天室的运行过程。设计要求:1.采用 C/S 模式,基于 TCP 协议编程的方式,使得各个用户通过服务器转发实现聊天的功能。2.分为两大模块:客户端模块和服务器端模块。3.客户端模块的主要功能:1 登陆功能:用户可以注册,然后选择服务器登入聊天室。2 显示用户:将在线用户显示在列表中。3 接收信息:能接收其他用户发出的信息。4 发送信息:能发出用户要发出的信息。4.服务器端模块的主要功能:1检验登陆信息:检查登陆信息是否正确,并向客户端返回登陆信息,如信息正确。就允许用户登
2、陆。2显示在线状态:将该用户的状态发给各在线用户。3转发聊天信息:将消息转发给所有在线的用户。5.编程语言不限。一、一、需求分析需求分析此程序主要分为两部分:服务器端和客户端。服务器端用于提供一个网络端口,等待客户端发出请求,登录到此服务端,然后进行网络通讯和消息的转发;客户端可通过服务器端的 IP 地址发送连接请求,然后登陆聊天室。在服务器端的成员列表栏中会显示在线的所有人名单,有人退出聊天室,成员列表会自动除名。整个程序的主体使用了 CSocket 类的方法,实现了网络通讯聊天。整个程序设计为两个部分:服务器和客户端。多人聊天的关键在于要将每个客户端发送过来的消息分发给所有其他客户端,为了
3、解决这个问题,在服务器程序中建立一个套接口链表,用来保存所有与客户端建立了连接的服务端口。设计原理设计原理:服务器通过 socket系统调用创建一个 Socket 数组后设定了接受连接客户的最大数目,与指定的本地端口绑定 bind,就可以在端口进行侦听 listen。如果有客户端连接请求,则在数组中选择一个空 socket,将客户端地址赋给这个 socket,然后登陆成功的客户就可以在服务器上聊天了。1/13.客户端程序相对简单,只要建立一个 socket 与服务器端连接,成功后通过这个 socket 来发送和接收就可以了。服务器端功能:1、初始化 socket,创建服务器端。2、维护一个链表
4、,保存所有用户的 IP 地址,端口信息。3、接受用户传送来的聊天信息,然后向链表中的所用用户转发。4、接受用户传送来的连接判断命令,并向用户发出响应命令。客户端功能:客户端界面上的两个文本框,一个用于显示接受的聊天信息,一个用来接受用户输入的聊天信息。当按下发送按钮时将信息发送给服务器。一、一、概要设计:概要设计:服务器客户端设计流程图二、二、详细设计:详细设计:服务器端:1 1、启动服务器代码:、启动服务器代码:/服务器启动时,先创建套接字并绑定端口,再监听此端口。void CSpeakerServerDlg:OnBnClickedStartUINT uPort=GetDlgItemInt;
5、/创建套接字if !m_TCPSocketListen.Create m_TraceRichEdit.TraceStringTEXT,TraceLevel_Warning;return;/监听套接字if!m_TCPSocketListen.Listen m_TraceRichEdit.TraceStringTEXT,TraceLevel_Warning;return;UINT uMaxConnect=GetDlgItemInt;/设置接口m_TCPSocketListen.SetTCPSocketService;/更新界面m_TraceRichEdit.TraceStringTEXT,Trac
6、eLevel_Normal;GetDlgItem-EnableWindow;GetDlgItem-EnableWindow;2/13.2 2、监听端口、监听端口,收到连接请求收到连接请求,接受的代码:接受的代码:/先检验是否在服务器的最XX 接限制内,若在,则获取当前客户的 IP 地址和端口等信息,插入链表中。/为什么要限制连接人数?因为TCP 连接是相当占资源的,若不限制连接人数,服务器的资源不够分配。void CSpeakerServerDlg:OnAccept/承载能力if m_TCPSocketItemMap.size GetDlgItemInt m_TraceRichEdit.Tra
7、ceStringTEXT,TraceLevel_Warning;return;/绑定套接字CTCPSocketService*pTCPSocketConnect=new CTCPSocketService;trySOCKADDR_IN SocketAddr;int nBufferSize=sizeof;/连接m_TCPSocketListen.Accept*pTCPSocketConnect,&SocketAddr,&nBufferSize;if m_hSocket=INVALID_SOCKET throw TEXT;/获取客户端 IPpTCPSocketConnect-m_dwClientA
8、ddr=SocketAddr.sin_addr.S_un.S_addr;pTCPSocketConnect-SetTCPSocketService;/绑定数据bool bActive=true;CTCPSocketItemMap:iterator iter=m_TCPSocketItemMap.begin;for;iter!=m_TCPSocketItemMap.end;iter+if m_hSocket=iter-first bActive=false;break;/插入客户数据if tagBindParameter*pBindParameter=new tagBindParameter;3
9、/13.pBindParameter-pTCPSocketService=pTCPSocketConnect;pBindParameter-dwUserID=0;m_TCPSocketItemMap.insertpairm_hSocket,pBindParameter;catch if m_hSocket!=INVALID_SOCKETpTCPSocketConnect-Close;3 3、接收并检验数据的代码:、接收并检验数据的代码:void CSpeakerServerDlg:OnReceiveBYTE cbDataBufferSOCKET_TCP_BUFFER;CTCPSocketIte
10、mMap:iterator iter=m_TCPSocketItemMap.find;if iter=m_TCPSocketItemMap.end return;/接收数据iter-second-pTCPSocketService-ReceivecbDataBuffer,CountArray;/解析数据TCP_Command*pCommand=cbDataBuffer;/解释数据WORD wPacketSize=pCommand-wPacketSize;WORD wDataSize=wPacketSize-sizeof;/数据包效验if SOCKET_TCP_BUFFER+sizeof TCP
11、_Command m_TraceRichEdit.TraceStringTEXT,TraceLevel_Warning;return;/子消息处理事件if!OnEventTCPSocketReadwMainCmdID,pCommand-wSubCmdID,pCommand+1,wDataSize BYTE*pClientIP=&iter-second-pTCPSocketService-m_dwClientAddr;m_TraceRichEdit.TraceStringTraceLevel_Warning,TEXT,pCommand-wMainCmdID,pCommand-wSubCmdID,
12、pClientIP0,pClientIP1,pClientIP2,pClientIP3;4/13.return;4 4、群发登录消息和用户发送的消息代码:、群发登录消息和用户发送的消息代码:/服务器收到客户的消息之后会将收到的消息发送给链表之中除了发送客户之外的所有客户。bool CSpeakerServerDlg:OnEventTCPSocketRead/获取绑定套接字CTCPSocketItemMap:iterator iter=m_TCPSocketItemMap.find;if iter=m_TCPSocketItemMap.end return false;CTCPSocketSer
13、vice*pTCPSocketService=iter-second-pTCPSocketService;switch case MDM_GP_LOGON:if/效验数据ASSERT;if return false;/获取数据CMD_CS_LOGON*pUserLogon=pData;m_TraceRichEdit.TraceStringTraceLevel_Normal,TEXT,pUserLogon-szUserName;tagUserData*pUserData=new tagUserData;/随机给用户分配一个 UserID,UserID 一般存储于数据库中,是一个独一无二的数字,/
14、一般在数据库表中设为主键,是整个游戏或者软件识别用户的唯一依据,这里我们没有涉及到数据库,暂时随机取一个数值代替/其次,我们应该通过数据库SQL 语句查询或者存储过程等方法,或在数据库中做密码的效验也好,/或在查询到用户的密码在服务器中进行判断也好,不管什么方法,此处一般需要进行用户密码的效验,这样才可以判定用户是否可以登陆了pUserData-dwUserID=GetTickCount;_sntprintf_sszUserName,CountArrayszUserName,pUserLogon-szUserName;_sntprintf_sszPassWord,CountArrayszPas
15、sWord,pUserLogon-szPassWord;/更新绑定数据5/13.CTCPSocketItemMap:iterator iter=m_TCPSocketItemMap.find;if iter!=m_TCPSocketItemMap.end iter-second-dwUserID=pUserData-dwUserID;/群发登陆消息SendUserItem;/发送在线用户CUserItemArray:iterator pUserItemSend=m_pUserManager-GetUserItemArray-begin;forGetUserItemArray-end;pUser
16、ItemSend+SendUserItemsecond;/插入数据m_pUserManager-InsertUserItem;return true;break;case MDM_GP_USER:if/获取数据CMD_CS_CHATMSG*pCHATMSG=pData;/这里其实需要做很多的效验,如 dwSendUserID 的有效性,字符串是否为空等,这里就不做这些效验了CMD_SC_CHATMSG _SC_CHATMSG;ZeroMemory;/获取时间GetLocalTime;_sntprintf_s_SC_CHATMSG.szSendUserName,CountArray,m_pUse
17、rManager-GetUserNamesecond-dwUserID;_sntprintf_s_SC_CHATMSG.szDescribe,CountArray,pCHATMSG-szDescribe;SendDataBatch;return true;break;return false;6/13.5 5、当服务器端有人退出登录时的代码:、当服务器端有人退出登录时的代码:/客户端退出时,服务器端获取用户名并群发退出消息,再在链表中删除该用户的数据,清理他的 Socketvoid CSpeakerServerDlg:OnCloseCTCPSocketItemMap:iterator iter
18、=m_TCPSocketItemMap.find;if iter=m_TCPSocketItemMap.end return;/获取用户m_TraceRichEdit.TraceStringTraceLevel_Normal,TEXT,m_pUserManager-GetUserNamesecond-dwUserID;/删除用户CMD_DC_DELETE _DC_DELETE;ZeroMemory;_sntprintf_s_DC_DELETE.szUserName,CountArray,m_pUserManager-GetUserNamesecond-dwUserID;/群发消息SendDat
19、aBatch;/销毁数据m_pUserManager-RemoveUserItemsecond-dwUserID;iter-second-pTCPSocketService-Close;SafeDeletesecond-pTCPSocketService;SafeDeletesecond;m_TCPSocketItemMap.erase;6 6、关闭服务器连接代码:关闭服务器连接代码:void CSpeakerServerDlg:OnBnClickedStop/关闭监听套接字m_TCPSocketListen.Close;/关闭连接套接字CTCPSocketItemMap:iterator i
20、ter=m_TCPSocketItemMap.begin;for;iter!=m_TCPSocketItemMap.end;+iteriter-second-pTCPSocketService-Close;SafeDeletesecond-pTCPSocketService;SafeDeletesecond;/更新界面m_TraceRichEdit.TraceStringTEXT,TraceLevel_Normal;GetDlgItem-EnableWindow;GetDlgItem-EnableWindow;7 7、退出服务器代码:、退出服务器代码:7/13.void CSpeakerSer
21、verDlg:OnCancelif if AfxMessageBoxTEXT,MB_YESNO|MB_ICONQUESTION=IDYES CTCPSocketItemMap:iterator iter=m_TCPSocketItemMap.begin;for;iter!=m_TCPSocketItemMap.end;+iteriter-second-pTCPSocketService-Close;SafeDeletesecond-pTCPSocketService;SafeDeletesecond;_super:OnCancel;客户端:1 1、客户端登录:、客户端登录:/登陆消息LRESU
22、LT CSpeakerClientDlg:OnLogonMessagetagLogonInfo*pLogonInfo=wParam;/关闭之前 socketm_TCPScoketClient.Close;/初始化套接字if !m_TCPScoketClient.Create SetTraceStringTEXT;SafeDelete;return FALSE;/创建连接if m_TCPScoketClient.ConnectszServerAddr,pLogonInfo-nPort=FALSE int nErrorCode=m_TCPScoketClient.GetLastError;if S
23、etTraceStringTEXT,nErrorCode;SafeDelete;return FALSE;8/13./设置接口m_TCPScoketClient.SetTCPSocketService;/构建数据CMD_CS_LOGON UserLogon;ZeroMemory;_sntprintf_sUserLogon.szUserName,CountArray,pLogonInfo-szUserName;_sntprintf_sUserLogon.szPassWord,CountArray,pLogonInfo-szPassWord;/发送登陆请求m_TCPScoketClient.Sen
24、dData;/设置界面SetTraceStringTEXT;m_LogonDlg.PostMessage;/清理数据SafeDelete;return TRUE;2 2、客户端发送数据代码:、客户端发送数据代码:void CSpeakerClientDlg:OnBnClickedSend/设置数据CMD_CS_CHATMSG _UserChat_Msg;ZeroMemory;GetDlgItemTextIDC_EDITCHAT,_UserChat_Msg.szDescribe,CountArray;/效验数据if _UserChat_Msg.szDescribe0=TEXT SetTraceS
25、tringTEXT;return;/发送数据m_TCPScoketClient.SendData;3 3、客户端接收数据代码:、客户端接收数据代码:/客户端接收数据和服务器段类似,也需解析、检验void CSpeakerClientDlg:OnReceive/接收消息9/13.BYTE cbDataBufferSOCKET_TCP_BUFFER;m_TCPScoketClient.ReceivecbDataBuffer,CountArray;/解析数据TCP_Command*pCommand=cbDataBuffer;/解释数据WORD wPacketSize=pCommand-wPacket
26、Size;WORD wDataSize=wPacketSize-sizeof;/数据包效验if SOCKET_TCP_BUFFER+sizeof TCP_Command SetTraceStringTEXT;return;/子消息处理事件if!OnEventTCPSocketReadwMainCmdID,pCommand-wSubCmdID,pCommand+1,wDataSize SetTraceStringTEXT,pCommand-wMainCmdID,pCommand-wSubCmdID;return;4 4、客户端消息的显示代码:、客户端消息的显示代码:/显示的消息类型:当用户登录时
27、,将用户数据插入用户列表中。服务器端会有 xx 登录的显示。当用户发消息时,服务器端就可以转发该消息给用户链表的所有其他用户。用户退出时,同理,客户端也会接收到 XX 退出了的消息。bool CSpeakerClientDlg:OnEventTCPSocketReadswitch case MDM_GP_LOGON:/登陆消息if/用户进入CMD_SC_USERCOME*pUserCome=pData;/插入数据if m_ListUser.FindStringszUserName=LB_ERR/设置自己信息if 10/13._sntprintf_sm_UserData.szUserName,C
28、ountArray,pUserCome-szUserName;m_UserData.dwUserID=m_UserData.dwUserID;SetWindowText;/添加用户列表m_ListUser.AddStringszUserName;m_ListUser.SetItemDatam_ListUser.GetCount-1,pUserCome-dwUserID;return true;break;case MDM_GP_USER:/用户消息if/聊天消息CMD_SC_CHATMSG*pCHATMSG=pData;/设置聊天数据static CString str;CString Str
29、Describe;StrDescribe.FormatTEXT,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;SetDlgItemText;return true;else
30、if/用户退出消息CMD_DC_DELETE*pDeleteUser=pData;int nIndex=m_ListUser.FindStringszUserName;if m_ListUser.DeleteString;static CString str;CString StrDescribe;11/13.StrDescribe.FormatTEXT,pDeleteUser-szUserName;str+=StrDescribe;str+=TEXT;SetDlgItemText;return true;break;return false;5 5、退出客户端:、退出客户端:void CSp
31、eakerClientDlg:OnCancelif if AfxMessageBoxTEXT,MB_YESNO|MB_ICONQUESTION=IDYES/关闭套接字m_TCPScoketClient.Close;CDialog:OnCancel;三、三、调试分析:调试分析:客户端用户登录:服务器端:客户 1 发 hello:客户 2 发你好:12/13.用户 1 和用户 2 退出时服务器端的显示:四、四、课设总结:课设总结:这次课程设计的制作我主要是参照了网上的一些网络编程实例和图书馆的相关书籍,找到类似的程序,跟着书上一步一步的做出来的,虽然这个程序算不上是我自己写出来的,但是通过这个过程
32、,还是让我学会了好多东西,也算能比较熟练地掌握 MFC 这一个软件了,这算是一个不小的收获。而且在做这个程序的过程中,遇到许许多多的问题,有的通过网上搜索可以找到答案。有的却不行,最后还是大家一起讨论出来的。总之,最后的这个程序的功能达到了之前预想的可能,通过服务器端的消息转发,实现了多用户之间的群聊,完整了一个简单的网络聊天软件的功能。但是最后验收的时候,老师还是希望我能做出私聊的效果来,但是由于时间限制,并未能实现它。私聊实现的思路:用户 1 若想与用户 2 私聊,则可以在用户列表选中用户 2,服务器接收到用户 1 的请求之后,将用户 2 的 IP 地址和端口号发送给用户 1,这样用户 1 就可以跟用户 2T 做 TCP 连接,并聊天了。通过此次的实验,也同时让我对于 CSocket 网络聊天类的使用有了更深入的了解,在关于网络编程的方面也有了新的认识。虽然此程序的功能还比较的简单,而且某些方面还没能完善。现在就软件自身还存在的问题罗列如下:1、成员列表在非正常退出时,服务器端得不到及时而有效地更新2、用户登录前必须先知道服务器端的 IP,这样操作显然比较麻烦3、程序界面做的比较单一,缺少层次性的美感。13/13