計算機網(wǎng)絡課設基于TCP協(xié)議編程的網(wǎng)絡聊天室_第1頁
計算機網(wǎng)絡課設基于TCP協(xié)議編程的網(wǎng)絡聊天室_第2頁
計算機網(wǎng)絡課設基于TCP協(xié)議編程的網(wǎng)絡聊天室_第3頁
計算機網(wǎng)絡課設基于TCP協(xié)議編程的網(wǎng)絡聊天室_第4頁
計算機網(wǎng)絡課設基于TCP協(xié)議編程的網(wǎng)絡聊天室_第5頁
已閱讀5頁,還剩15頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領

文檔簡介

1、基于 TCP協(xié)議編程的網(wǎng)絡聊天室設計內(nèi)容:基于 TCP協(xié)議編程的方式,編寫程序模擬網(wǎng)絡聊天室的運行過程。 設計要求:1. 采用 C/S 模式,基于 TCP 協(xié)議編程的方式,使得各個用戶通過服務器轉(zhuǎn) 發(fā)實現(xiàn)聊天的功能。2. 分為兩大模塊:客戶端模塊和服務器端模塊。3. 客戶端模塊的主要功能:1) 登陸功能:用戶可以注冊,然后選擇服務器登入聊天室。2) 顯示用戶:將在線用戶顯示在列表中。3) 接收信息:能接收其他用戶發(fā)出的信息。4) 發(fā)送信息:能發(fā)出用戶要發(fā)出的信息。4. 服務器端模塊的主要功能:1) 檢驗登陸信息: 檢查登陸信息是否正確, 并向客戶端返回登陸信息, 如 信息正確。就允許用戶登陸。

2、2) 顯示在線狀態(tài):將該用戶的狀態(tài)發(fā)給各在線用戶。3) 轉(zhuǎn)發(fā)聊天信息:將消息轉(zhuǎn)發(fā)給所有在線的用戶。5. 編程語言不限。一、 需求分析此程序主要分為兩部分:服務器端和客戶端。 服務器端用于提供一個網(wǎng)絡端口, 等待客戶端發(fā)出請求, 登錄到此服務端, 然后進行網(wǎng)絡通訊和消息的轉(zhuǎn)發(fā);客戶端可通過服務器端的 IP 地址發(fā)送連接請 求,然后登陸聊天室。 在服務器端的成員列表欄中會顯示在線的所有人名單, 有 人退出聊天室,成員列表會自動除名。 整個程序的主體使用了 CSocket 類的方法, 實現(xiàn)了網(wǎng)絡通訊聊天。整個程序設計為兩個部分:服務器 (SpeakerServer) 和客 戶端 (SpeakerCl

3、ient) 。多人聊天的關鍵在于要將每個客戶端發(fā)送過來的消息分發(fā)給所有其他客 戶端,為了解決這個問題, 在服務器程序中建立一個套接口鏈表, 用來保存所有 與客戶端建立了連接的服務端口。設計原理 :服務器通過 socket() 系統(tǒng)調(diào)用創(chuàng)建一個 Socket 數(shù)組后(設定 了接受連接客戶的最大數(shù)目),與指定的本地端口綁定 bind() ,就可以在端口進word 可編輯 . 行偵聽 listen() 。如果有客戶端連接請求,則在數(shù)組中選擇一個空 socket ,將客 戶端地址賦給這個 socket ,然后登陸成功的客戶就可以在服務器上聊天了??蛻舳顺绦蛳鄬唵危灰⒁粋€ socket 與服務器

4、端連接,成功后通 過這個 socket 來發(fā)送和接收就可以了。服務器端功能:1、初始化 socket, 創(chuàng)建服務器端。2、維護一個鏈表,保存所有用戶的 IP 地址,端口信息。3、接受用戶傳送來的聊天信息,然后向鏈表中的所用用戶轉(zhuǎn)發(fā)。4、接受用戶傳送來的連接判斷命令,并向用戶發(fā)出響應命令??蛻舳斯δ埽?客戶端界面上的兩個文本框, 一個用于顯示接受的聊天信息, 一個用 來接受用戶輸入的聊天信息。當按下“發(fā)送”按鈕時將信息發(fā)送給服務器。 一、 概要設計:服務器 客戶端設計流程圖)word 可編輯 .、 詳細設計:服務器端:1、啟動服務器代碼:/ 服務器啟動時,先創(chuàng)建套接字并綁定端口,再監(jiān)聽此端口。v

5、oid CSpeakerServerDlg:OnBnClickedStart()UINT uPort = GetDlgItemInt(IDC_PORT);/ 創(chuàng)建套接字if ( !m_TCPSocketListen.Create(uPort) )m_TraceRichEdit.TraceString(TEXT( 綁定監(jiān)聽端口失敗 , 請確認該端口沒有被其它程序占用 ),TraceLevel_Warning);return;/ 監(jiān)聽套接字if( !m_TCPSocketListen.Listen() )m_TraceRichEdit.TraceString(TEXT( 監(jiān)聽失敗 ),TraceL

6、evel_Warning);return;UINT uMaxConnect = GetDlgItemInt(IDC_MAX);/ 設置接口m_TCPSocketListen.SetTCPSocketService(this);/ 更新界面m_TraceRichEdit.TraceString(TEXT( 服務器啟動成功 ),TraceLevel_Normal);GetDlgItem(IDC_START)-EnableWindow(FALSE);GetDlgItem(IDC_STOP)-EnableWindow(TRUE);2、監(jiān)聽端口,收到連接請求,接受的代碼:/ 先檢驗是否在服務器的最大連接

7、限制內(nèi), 若在,則獲取當前客戶的 IP 地址和端口等信息, 插入鏈表中。/ 為什么要限制連接人數(shù)?因為 TCP連接是相當占資源的, 若不限制連接人數(shù), 服務器的資 源不夠分配。void CSpeakerServerDlg:OnAccept()/ 承載能力if ( m_TCPSocketItemMap.size() GetDlgItemInt(IDC_MAX) )word 可編輯 .m_TraceRichEdit.TraceString(TEXT( 服務器承載人數(shù)已滿,已過濾其他連接 ),TraceLevel_Warning);return;/ 綁定套接字CTCPSocketService *p

8、TCPSocketConnect = new CTCPSocketService;trySOCKADDR_IN SocketAddr; int nBufferSize = sizeof(SocketAddr);/ 連接 m_TCPSocketListen.Accept(*pTCPSocketConnect,(SOCKADDR *) &SocketAddr, &nBufferSize);if (pTCPSocketConnect-m_hSocket = INVALID_SOCKET) throw TEXT(無效的連接套接字 );/ 獲取客戶端 IPpTCPSocketConnect-m_dwCl

9、ientAddr = SocketAddr.sin_addr.S_un.S_addr; pTCPSocketConnect-SetTCPSocketService(this);/ 綁定數(shù)據(jù)bool bActive = true;CTCPSocketItemMap:iterator iter = m_TCPSocketItemMap.begin();for (;iter!= m_TCPSocketItemMap.end();iter+) if ( pTCPSocketConnect-m_hSocket = iter-first ) bActive = false; break;/ 插入客戶數(shù)據(jù)i

10、f ( bActive ) tagBindParameter *pBindParameter = new tagBindParameter; pBindParameter-pTCPSocketService = pTCPSocketConnect; pBindParameter-dwUserID = 0;word 可編輯 .m_TCPSocketItemMap.insert(pair(pTCPSocketConnect- m_hSocket,pBindParameter);catch (.)if (pTCPSocketConnect-m_hSocket != INVALID_SOCKET) p

11、TCPSocketConnect-Close();3、接收并檢驗數(shù)據(jù)的代碼:void CSpeakerServerDlg:OnReceive(SOCKET hSocket)BYTE cbDataBufferSOCKET_TCP_BUFFER;CTCPSocketItemMap:iterator iter = m_TCPSocketItemMap.find(hSocket);if ( iter = m_TCPSocketItemMap.end() ) return;/ 接收數(shù)據(jù) iter-second-pTCPSocketService-Receive(cbDataBuffer,CountArr

12、ay(cbDataBuffer) );/ 解析數(shù)據(jù)TCP_Command * pCommand=(TCP_Command *)cbDataBuffer;/ 解釋數(shù)據(jù)WORD wPacketSize = pCommand-wPacketSize;WORD wDataSize = wPacketSize-sizeof(TCP_Command);/ 數(shù)據(jù)包效驗if ( wPacketSize SOCKET_TCP_BUFFER+sizeof TCP_Command )m_TraceRichEdit.TraceString(TEXT( 數(shù)據(jù)包太大,已拒絕 ),TraceLevel_Warning);r

13、eturn;/ 子消息處理事件if( !OnEventTCPSocketRead(hSocket,pCommand-wMainCmdID,pCommand-wSubCmdID, pCommand+1,wDataSize) )BYTE * pClientIP=(BYTE *)&iter-second-pTCPSocketService-m_dwClientAddr;word 可編輯 .m_TraceRichEdit.TraceString(TraceLevel_Warning,TEXT( 收到偽數(shù)據(jù)包或未處 理的數(shù)據(jù)包 ,wMainCmdID:%d,wSubCmdID:%d,來源 IP:%d.%

14、d.%d.%d),pCommand-wMainCmdID,pCommand-wSubCmdID,pClientIP0,pClientI P1,pClientIP2,pClientIP3);return;4、群發(fā)登錄消息和用戶發(fā)送的消息代碼: / 服務器收到客戶的消息之后會將收到的消息發(fā)送給鏈表之中除了發(fā)送客戶之外的所有客 戶。bool CSpeakerServerDlg:OnEventTCPSocketRead( SOCKET hSocket,WORD wMainCmdID, WORD wSubCmdID, VOID * pData, WORD wDataSize )/ 獲取綁定套接字CTCP

15、SocketItemMap:iterator iter = m_TCPSocketItemMap.find(hSocket);if ( iter = m_TCPSocketItemMap.end() ) return false;CTCPSocketService *pTCPSocketService = iter-second-pTCPSocketService;switch ( wMainCmdID )case MDM_GP_LOGON:if ( wSubCmdID = SUB_CS_LOGON )/ 效驗數(shù)據(jù)ASSERT( wDataSize = sizeof CMD_CS_LOGON

16、);if ( wDataSize != sizeof CMD_CS_LOGON ) return false;/ 獲取數(shù)據(jù)CMD_CS_LOGON *pUserLogon = (CMD_CS_LOGON*)pData;m_TraceRichEdit.TraceString(TraceLevel_Normal,TEXT(%s 登陸服務 器),pUserLogon-szUserName);tagUserData *pUserData = new tagUserData;/ 隨機給用戶分配一個 UserID , UserID 一般存儲于數(shù)據(jù)庫中,是一個獨 一無二的數(shù)字,/ 一般在數(shù)據(jù)庫表中設為主鍵,

17、 是整個游戲或者軟件識別用戶的唯一依據(jù) , 這里我們沒有涉及到數(shù)據(jù)庫,暫時隨機取一個數(shù)值代替/ 其次, 我們應該通過數(shù)據(jù)庫 SQL語句查詢或者存儲過程等方法, 或在數(shù) 據(jù)庫中做密碼的效驗也好,word 可編輯 ./ 或在查詢到用戶的密碼在服務器中進行判斷也好, 不管什么方法, 此處 一般需要進行用戶密碼的效驗,這樣才可以判定用戶是否可以登陸了pUserData-dwUserID = GetTickCount();_sntprintf_s(pUserData-szUserName,CountArray(pUserData-szUserName),pUserL ogon-szUserName);_

18、sntprintf_s(pUserData-szPassWord,CountArray(pUserData-szPassWord),pUserL ogon-szPassWord);/ 更新綁定數(shù)據(jù)CTCPSocketItemMap:iterator iter = m_TCPSocketItemMap.find(hSocket);if ( iter != m_TCPSocketItemMap.end() ) iter-second-dwUserID = pUserData-dwUserID;/ 群發(fā)登陸消息 SendUserItem(NULL,pUserData);/ 發(fā)送在線用戶CUserIt

19、emArray:iterator pUserItemSend = m_pUserManager-GetUserItemArray()-begin();for (;pUserItemSend!=m_pUserManager-GetUserItemArray()-end();pUserItemSend+ )SendUserItem(pTCPSocketService,pUserItemSend-second);/ 插入數(shù)據(jù) m_pUserManager-InsertUserItem(pUserData);return true;break;case MDM_GP_USER:if ( wSubCmd

20、ID = SUB_CS_USERT_CHAT )/ 獲取數(shù)據(jù)CMD_CS_CHATMSG *pCHATMSG = (CMD_CS_CHATMSG*)pData;/ 這里其實需要做很多的效驗,如 dwSendUserID 的有效性,字符串是否 為空等,這里就不做這些效驗了CMD_SC_CHATMSG _SC_CHATMSG;word 可編輯 .ZeroMemory(&_SC_CHATMSG,sizeof _SC_CHATMSG);/ 獲取時間 GetLocalTime(&_SC_CHATMSG.SystemTime);_sntprintf_s(_SC_CHATMSG.szSendUserName

21、,CountArray(_SC_CHATMSG.szSendUserNa me),m_pUserManager-GetUserName(iter-second-dwUserID);_sntprintf_s(_SC_CHATMSG.szDescribe,CountArray(_SC_CHATMSG.szDescribe),pCHA TMSG-szDescribe);SendDataBatch(MDM_GP_USER,SUB_CS_USERT_CHAT,&_SC_CHATMSG,sizeof _SC_CHATMSG);return true;break;return false;5、當服務器端有人

22、退出登錄時的代碼:/ 客戶端退出時, 服務器端獲取用戶名并群發(fā)退出消息, 再在鏈表中刪除該用戶的數(shù)據(jù), 清 理他的 Socketvoid CSpeakerServerDlg:OnClose(SOCKET hSocket)CTCPSocketItemMap:iterator iter = m_TCPSocketItemMap.find(hSocket);if ( iter = m_TCPSocketItemMap.end() ) return;/ 獲取用戶 m_TraceRichEdit.TraceString(TraceLevel_Normal,TEXT(%s 退出了服務器),m_pUserM

23、anager-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_pUse rManager-GetUserName(iter-second-dwUserID);/ 群發(fā)消息SendDataBatch(MDM_GP_USER,SUB_SC_DELETE,&_DC_DELETE,sizeof _

24、DC_DELETE);word 可編輯 ./ 銷毀數(shù)據(jù) m_pUserManager-RemoveUserItem(iter-second-dwUserID); iter-second-pTCPSocketService-Close(); SafeDelete(iter-second-pTCPSocketService);SafeDelete(iter-second);m_TCPSocketItemMap.erase(iter);6、關閉服務器連接代碼:void CSpeakerServerDlg:OnBnClickedStop()/ 關閉監(jiān)聽套接字 m_TCPSocketListen.Clo

25、se();/ 關閉連接套接字CTCPSocketItemMap:iterator iter = 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);Get

26、DlgItem(IDC_START)-EnableWindow(TRUE); GetDlgItem(IDC_STOP)-EnableWindow(FALSE);7、退出服務器代碼:void CSpeakerServerDlg:OnCancel()if ( m_TCPSocketListen.m_hSocket != INVALID_SOCKET )if ( AfxMessageBox(TEXT( 確定退出服務器嗎 ?其它所有用戶將失去連接 ),MB_YESNO|MB_ICONQUESTION) = IDYES )CTCPSocketItemMap:iterator iter = m_TCPSo

27、cketItemMap.begin();for (;iter != m_TCPSocketItemMap.end(); +iter) iter-second-pTCPSocketService-Close(); SafeDelete(iter-second-pTCPSocketService);word 可編輯 .SafeDelete(iter-second);_super:OnCancel();客戶端:1、客戶端登錄:/ 登陸消息LRESULT CSpeakerClientDlg:OnLogonMessage( WPARAM wParam,LPARAM lParam ) tagLogonIn

28、fo *pLogonInfo = (tagLogonInfo*)wParam;/ 關閉之前 socket m_TCPScoketClient.Close();/ 初始化套接字if ( !m_TCPScoketClient.Create() )SetTraceString(TEXT( 套接字創(chuàng)建失敗 );SafeDelete(pLogonInfo);return FALSE;/ 創(chuàng)建連接if( m_TCPScoketClient.Connect(pLogonInfo-szServerAddr,pLogonInfo-nPort)FALSE )int nErrorCode = m_TCPScoket

29、Client.GetLastError();if ( nErrorCode !=WSAEWOULDBLOCK )SetTraceString(TEXT( 連接服務器失敗 , 錯誤碼: %d),nErrorCode); SafeDelete(pLogonInfo);return FALSE;/ 設置接口 m_TCPScoketClient.SetTCPSocketService(this);word 可編輯 ./ 構(gòu)建數(shù)據(jù)CMD_CS_LOGON UserLogon;ZeroMemory(&UserLogon,sizeof UserLogon);_sntprintf_s(UserLogon.sz

30、UserName,CountArray(UserLogon.szUserName),pLogonIn fo-szUserName);_sntprintf_s(UserLogon.szPassWord,CountArray(UserLogon.szPassWord),pLogonIn fo-szPassWord);/ 發(fā)送登陸請求 m_TCPScoketClient.SendData(MDM_GP_LOGON,SUB_CS_LOGON,&UserLogon,sizeof UserLogon);/ 設置界面SetTraceString(TEXT( 連接服務器成功 ); m_LogonDlg.Pos

31、tMessage(WM_CLOSE);/ 清理數(shù)據(jù)SafeDelete(pLogonInfo);return TRUE;2、客戶端發(fā)送數(shù)據(jù)代碼: void CSpeakerClientDlg:OnBnClickedSend()/ 設置數(shù)據(jù)CMD_CS_CHATMSG _UserChat_Msg;ZeroMemory(&_UserChat_Msg,sizeof _UserChat_Msg);GetDlgItemText(IDC_EDITCHAT,_UserChat_Msg.szDescribe,CountArray(_UserChat_M sg.szDescribe);/ 效驗數(shù)據(jù)if ( _U

32、serChat_Msg.szDescribe0 = TEXT(0) )SetTraceString(TEXT( 聊天內(nèi)容為空,請先輸入您想說的話 ); return;/ 發(fā)送數(shù)據(jù)m_TCPScoketClient.SendData(MDM_GP_USER,SUB_CS_USERT_CHAT,&_UserChat_Msg,siz eof _UserChat_Msg);3、客戶端接收數(shù)據(jù)代碼:word 可編輯 ./ 客戶端接收數(shù)據(jù)和服務器段類似,也需解析、檢驗void CSpeakerClientDlg:OnReceive( int nErrorCode )/ 接收消息BYTE cbDataBuf

33、ferSOCKET_TCP_BUFFER; m_TCPScoketClient.Receive(cbDataBuffer,CountArray(cbDataBuffer) );/ 解析數(shù)據(jù)TCP_Command * pCommand=(TCP_Command *)cbDataBuffer;/ 解釋數(shù)據(jù)WORD wPacketSize = pCommand-wPacketSize;WORD wDataSize = wPacketSize-sizeof(TCP_Command);/ 數(shù)據(jù)包效驗if ( wPacketSize SOCKET_TCP_BUFFER+sizeof TCP_Command

34、 )SetTraceString(TEXT( 數(shù)據(jù)包太大,已拒絕 );return;/ 子消息處理事件if( !OnEventTCPSocketRead(pCommand-wMainCmdID,pCommand-wSubCmdID,pCommand +1,wDataSize) )SetTraceString(TEXT( 收到未處理的數(shù)據(jù)包,wMainCmdID:%d,wSubCmdID:%d),pCommand-wMainCmdID,pCommand-wSubCmdID); return;4、客戶端消息的顯示代碼:/ 顯示的消息類型:當用戶登錄時,將用戶數(shù)據(jù)插入用戶列表中。服務器端會有xx 登

35、錄的顯示。 當用戶發(fā)消息時, 服務器端就可以轉(zhuǎn)發(fā)該消息給用戶鏈表的所有其他用戶。用戶退出時,同理,客戶端也會接收到XX退出了的消息。bool CSpeakerClientDlg:OnEventTCPSocketRead( WORD wMainCmdID, WORD wSubCmdID, VOID * pData, WORD wDataSize )switch ( wMainCmdID )case MDM_GP_LOGON: / 登陸消息if ( wSubCmdID = SUB_SC_USERCOME)/ 用戶進入CMD_SC_USERCOME *pUserCome = (CMD_SC_USER

36、COME*)pData;word 可編輯 ./ 插入數(shù)據(jù)if( m_ListUser.FindString(-1,pUserCome-szUserName) = LB_ERR )/ 設置自己信息if ( m_UserData.dwUserID = 0 )_sntprintf_s(m_UserData.szUserName,CountArray(m_UserData.szUserName),pUserC ome-szUserName);m_UserData.dwUserID = m_UserData.dwUserID;SetWindowText(m_UserData.szUserName);/

37、添加用戶列表 m_ListUser.AddString(pUserCome-szUserName);m_ListUser.SetItemData(m_ListUser.GetCount()-1,pUserCome-dwUserID);return true;break;case MDM_GP_USER: / 用戶消息if ( wSubCmdID = SUB_CS_USERT_CHAT )/ 聊天消息CMD_SC_CHATMSG *pCHATMSG = (CMD_SC_CHATMSG*)pData;/ 設置聊天數(shù)據(jù)static CString str;CString StrDescribe;St

38、rDescribe.Format(TEXT(%s %04d-%02d-%02d %02d:%02d:%02drn),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(rn);word 可編輯 .SetDlgItemText(IDC_CHATRECV,str);return true;else if ( wSubCmdID = SUB_SC_DELETE )/ 用戶退出消息CMD_DC_DELETE *pDeleteUser = (CMD_DC_DEL

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
  • 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論