




版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、精選優(yōu)質(zhì)文檔-傾情為你奉上基于TCP協(xié)議編程的網(wǎng)絡(luò)聊天室設(shè)計內(nèi)容:基于TCP協(xié)議編程的方式,編寫程序模擬網(wǎng)絡(luò)聊天室的運(yùn)行過程。設(shè)計要求:1. 采用C/S模式,基于TCP協(xié)議編程的方式,使得各個用戶通過服務(wù)器轉(zhuǎn)發(fā)實現(xiàn)聊天的功能。2. 分為兩大模塊:客戶端模塊和服務(wù)器端模塊。3. 客戶端模塊的主要功能:1)登陸功能:用戶可以注冊,然后選擇服務(wù)器登入聊天室。2)顯示用戶:將在線用戶顯示在列表中。3)接收信息:能接收其他用戶發(fā)出的信息。4)發(fā)送信息:能發(fā)出用戶要發(fā)出的信息。4.服務(wù)器端模塊的主要功能:1)檢驗登陸信息:檢查登陸信息是否正確,并向客戶端返回登陸信息,如信息正確。就允許用戶登陸。2)顯示在
2、線狀態(tài):將該用戶的狀態(tài)發(fā)給各在線用戶。3)轉(zhuǎn)發(fā)聊天信息:將消息轉(zhuǎn)發(fā)給所有在線的用戶。5. 編程語言不限。一、 需求分析此程序主要分為兩部分:服務(wù)器端和客戶端。服務(wù)器端用于提供一個網(wǎng)絡(luò)端口,等待客戶端發(fā)出請求,登錄到此服務(wù)端,然后進(jìn)行網(wǎng)絡(luò)通訊和消息的轉(zhuǎn)發(fā);客戶端可通過服務(wù)器端的IP地址發(fā)送連接請求,然后登陸聊天室。在服務(wù)器端的成員列表欄中會顯示在線的所有人名單,有人退出聊天室,成員列表會自動除名。整個程序的主體使用了CSocket類的方法,實現(xiàn)了網(wǎng)絡(luò)通訊聊天。整個程序設(shè)計為兩個部分:服務(wù)器(SpeakerServer)和客戶端 (SpeakerClient) 。 多人聊天的關(guān)鍵在于要將
3、每個客戶端發(fā)送過來的消息分發(fā)給所有其他客戶端,為了解決這個問題,在服務(wù)器程序中建立一個套接口鏈表,用來保存所有與客戶端建立了連接的服務(wù)端口。設(shè)計原理:服務(wù)器通過socket()系統(tǒng)調(diào)用創(chuàng)建一個Socket數(shù)組后(設(shè)定了接受連接客戶的最大數(shù)目),與指定的本地端口綁定bind(),就可以在端口進(jìn)行偵聽listen()。如果有客戶端連接請求,則在數(shù)組中選擇一個空socket,將客戶端地址賦給這個socket,然后登陸成功的客戶就可以在服務(wù)器上聊天了??蛻舳顺绦蛳鄬唵?,只要建立一個socket與服務(wù)器端連接,成功后通過這個socket來發(fā)送和接收就可以了。服務(wù)器端功能:1、 初始化socket,創(chuàng)建
4、服務(wù)器端。2、 維護(hù)一個鏈表,保存所有用戶的IP地址,端口信息。3、 接受用戶傳送來的聊天信息,然后向鏈表中的所用用戶轉(zhuǎn)發(fā)。4、 接受用戶傳送來的連接判斷命令,并向用戶發(fā)出響應(yīng)命令。客戶端功能:客戶端界面上的兩個文本框,一個用于顯示接受的聊天信息,一個用來接受用戶輸入的聊天信息。當(dāng)按下“發(fā)送”按鈕時將信息發(fā)送給服務(wù)器。一、 概要設(shè)計: 服務(wù)器 客戶端 (設(shè)計流程圖)二、 詳細(xì)設(shè)計:服務(wù)器端:1、啟動服務(wù)器代碼:/服務(wù)器啟動時,先創(chuàng)建套接字并綁定端口,再監(jiān)聽此端口。void CSpeakerServerDlg:OnBnClickedStart()UINT uPort = GetDlgItemIn
5、t(IDC_PORT);/創(chuàng)建套接字if ( !m_TCPSocketListen.Create(uPort) )m_TraceRichEdit.TraceString(TEXT("綁定監(jiān)聽端口失敗,請確認(rèn)該端口沒有被其它程序占用"),TraceLevel_Warning);return;/監(jiān)聽套接字if( !m_TCPSocketListen.Listen() )m_TraceRichEdit.TraceString(TEXT("監(jiān)聽失敗"),TraceLevel_Warning);return;UINT uMaxConnect = GetDlgIte
6、mInt(IDC_MAX);/設(shè)置接口m_TCPSocketListen.SetTCPSocketService(this);/更新界面m_TraceRichEdit.TraceString(TEXT("服務(wù)器啟動成功"),TraceLevel_Normal);GetDlgItem(IDC_START)->EnableWindow(FALSE);GetDlgItem(IDC_STOP)->EnableWindow(TRUE);2、監(jiān)聽端口,收到連接請求,接受的代碼:/先檢驗是否在服務(wù)器的最大連接限制內(nèi),若在,則獲取當(dāng)前客戶的IP地址和端口等信息,插入鏈表中。/為
7、什么要限制連接人數(shù)?因為TCP連接是相當(dāng)占資源的,若不限制連接人數(shù),服務(wù)器的資源不夠分配。void CSpeakerServerDlg:OnAccept()/承載能力if ( m_TCPSocketItemMap.size() > GetDlgItemInt(IDC_MAX) )m_TraceRichEdit.TraceString(TEXT("服務(wù)器承載人數(shù)已滿,已過濾其他連接"),TraceLevel_Warning);return;/綁定套接字CTCPSocketService *pTCPSocketConnect = new CTCPSocketService
8、; try SOCKADDR_INSocketAddr; int nBufferSize = sizeof(SocketAddr); /連接 m_TCPSocketListen.Accept(*pTCPSocketConnect,(SOCKADDR *) &SocketAddr, &nBufferSize); if (pTCPSocketConnect->m_hSocket = INVALID_SOCKET) throw TEXT("無效的連接套接字"); /獲取客戶端IP pTCPSocketConnect->m_dwClientAddr =
9、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ù)if (
10、bActive )tagBindParameter *pBindParameter = new tagBindParameter;pBindParameter->pTCPSocketService = pTCPSocketConnect;pBindParameter->dwUserID = 0;m_TCPSocketItemMap.insert(pair<SOCKET,tagBindParameter*>(pTCPSocketConnect->m_hSocket,pBindParameter); catch (.) if (pTCPSocketConnect-&g
11、t;m_hSocket != INVALID_SOCKET)pTCPSocketConnect->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->pTCPS
12、ocketService->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 )m_TraceRichEdit
13、.TraceString(TEXT("數(shù)據(jù)包太大,已拒絕"),TraceLevel_Warning);return;/子消息處理事件if( !OnEventTCPSocketRead(hSocket,pCommand->wMainCmdID,pCommand->wSubCmdID,pCommand+1,wDataSize) )BYTE * pClientIP=(BYTE *)&iter->second->pTCPSocketService->m_dwClientAddr;m_TraceRichEdit.TraceString(Trace
14、Level_Warning,TEXT("收到偽數(shù)據(jù)包或未處理的數(shù)據(jù)包,wMainCmdID:%d,wSubCmdID:%d,來源IP:%d.%d.%d.%d"),pCommand->wMainCmdID,pCommand->wSubCmdID,pClientIP0,pClientIP1,pClientIP2,pClientIP3);return;4、群發(fā)登錄消息和用戶發(fā)送的消息代碼:/服務(wù)器收到客戶的消息之后會將收到的消息發(fā)送給鏈表之中除了發(fā)送客戶之外的所有客戶。bool CSpeakerServerDlg:OnEventTCPSocketRead( SOCKE
15、T hSocket,WORD wMainCmdID, WORD wSubCmdID, VOID * pData, WORD wDataSize )/獲取綁定套接字CTCPSocketItemMap:iterator iter = m_TCPSocketItemMap.find(hSocket);if ( iter = m_TCPSocketItemMap.end() ) return false;CTCPSocketService *pTCPSocketService = iter->second->pTCPSocketService;switch ( wMainCmdID )ca
16、se MDM_GP_LOGON:if ( wSubCmdID = SUB_CS_LOGON )/效驗數(shù)據(jù)ASSERT( wDataSize = sizeof CMD_CS_LOGON );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登陸服務(wù)器"),pUserLogon->szUserName);t
17、agUserData *pUserData = new tagUserData;/隨機(jī)給用戶分配一個UserID,UserID一般存儲于數(shù)據(jù)庫中,是一個獨一無二的數(shù)字,/一般在數(shù)據(jù)庫表中設(shè)為主鍵,是整個游戲或者軟件識別用戶的唯一依據(jù),這里我們沒有涉及到數(shù)據(jù)庫,暫時隨機(jī)取一個數(shù)值代替/其次,我們應(yīng)該通過數(shù)據(jù)庫SQL語句查詢或者存儲過程等方法,或在數(shù)據(jù)庫中做密碼的效驗也好,/或在查詢到用戶的密碼在服務(wù)器中進(jìn)行判斷也好,不管什么方法,此處一般需要進(jìn)行用戶密碼的效驗,這樣才可以判定用戶是否可以登陸了pUserData->dwUserID = GetTickCount();_sntprintf_s
18、(pUserData->szUserName,CountArray(pUserData->szUserName),pUserLogon->szUserName);_sntprintf_s(pUserData->szPassWord,CountArray(pUserData->szPassWord),pUserLogon->szPassWord);/更新綁定數(shù)據(jù)CTCPSocketItemMap:iterator iter = m_TCPSocketItemMap.find(hSocket);if ( iter != m_TCPSocketItemMap.en
19、d() )iter->second->dwUserID = pUserData->dwUserID;/群發(fā)登陸消息SendUserItem(NULL,pUserData);/發(fā)送在線用戶CUserItemArray:iterator pUserItemSend = m_pUserManager->GetUserItemArray()->begin();for (;pUserItemSend!=m_pUserManager->GetUserItemArray()->end();pUserItemSend+ )SendUserItem(pTCPSocket
20、Service,pUserItemSend->second);/插入數(shù)據(jù)m_pUserManager->InsertUserItem(pUserData);return true;break;case MDM_GP_USER:if ( wSubCmdID = SUB_CS_USERT_CHAT )/獲取數(shù)據(jù)CMD_CS_CHATMSG *pCHATMSG = (CMD_CS_CHATMSG*)pData;/這里其實需要做很多的效驗,如dwSendUserID的有效性,字符串是否為空等,這里就不做這些效驗了CMD_SC_CHATMSG _SC_CHATMSG;ZeroMemory(&
21、amp;_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),pCHATM
22、SG->szDescribe);SendDataBatch(MDM_GP_USER,SUB_CS_USERT_CHAT,&_SC_CHATMSG,sizeof _SC_CHATMSG);return true;break;return false;5、當(dāng)服務(wù)器端有人退出登錄時的代碼:/客戶端退出時,服務(wù)器端獲取用戶名并群發(fā)退出消息,再在鏈表中刪除該用戶的數(shù)據(jù),清理他的Socketvoid CSpeakerServerDlg:OnClose(SOCKET hSocket)CTCPSocketItemMap:iterator iter = m_TCPSocketItemMap.fin
23、d(hSocket);if ( iter = m_TCPSocketItemMap.end() ) return;/獲取用戶m_TraceRichEdit.TraceString(TraceLevel_Normal,TEXT("%s退出了服務(wù)器"),m_pUserManager->GetUserName(iter->second->dwUserID);/刪除用戶CMD_DC_DELETE _DC_DELETE;ZeroMemory(&_DC_DELETE,sizeof _DC_DELETE);_sntprintf_s(_DC_DELETE.szUs
24、erName,CountArray(_DC_DELETE.szUserName),m_pUserManager->GetUserName(iter->second->dwUserID);/群發(fā)消息SendDataBatch(MDM_GP_USER,SUB_SC_DELETE,&_DC_DELETE,sizeof _DC_DELETE);/銷毀數(shù)據(jù)m_pUserManager->RemoveUserItem(iter->second->dwUserID);iter->second->pTCPSocketService->Close()
25、;SafeDelete(iter->second->pTCPSocketService);SafeDelete(iter->second);m_TCPSocketItemMap.erase(iter);6、 關(guān)閉服務(wù)器連接代碼:void CSpeakerServerDlg:OnBnClickedStop()/關(guān)閉監(jiān)聽套接字m_TCPSocketListen.Close();/關(guān)閉連接套接字CTCPSocketItemMap:iterator iter = m_TCPSocketItemMap.begin();for (;iter != m_TCPSocketItemMap.e
26、nd(); +iter)iter->second->pTCPSocketService->Close();SafeDelete(iter->second->pTCPSocketService);SafeDelete(iter->second);/更新界面m_TraceRichEdit.TraceString(TEXT("服務(wù)器關(guān)閉成功"),TraceLevel_Normal);GetDlgItem(IDC_START)->EnableWindow(TRUE);GetDlgItem(IDC_STOP)->EnableWindow
27、(FALSE);7、退出服務(wù)器代碼:void CSpeakerServerDlg:OnCancel()if ( m_TCPSocketListen.m_hSocket != INVALID_SOCKET )if ( AfxMessageBox(TEXT("確定退出服務(wù)器嗎?其它所有用戶將失去連接"),MB_YESNO|MB_ICONQUESTION) = IDYES )CTCPSocketItemMap:iterator iter = m_TCPSocketItemMap.begin();for (;iter != m_TCPSocketItemMap.end(); +it
28、er)iter->second->pTCPSocketService->Close();SafeDelete(iter->second->pTCPSocketService);SafeDelete(iter->second);_super:OnCancel();客戶端:1、客戶端登錄:/登陸消息LRESULT CSpeakerClientDlg:OnLogonMessage( WPARAM wParam,LPARAM lParam )tagLogonInfo *pLogonInfo = (tagLogonInfo*)wParam;/關(guān)閉之前socketm_T
29、CPScoketClient.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_TCPScoketClient.GetLastError();if ( nErrorCode
30、 !=WSAEWOULDBLOCK )SetTraceString(TEXT("連接服務(wù)器失敗,錯誤碼:%d"),nErrorCode);SafeDelete(pLogonInfo);return FALSE;/設(shè)置接口m_TCPScoketClient.SetTCPSocketService(this);/構(gòu)建數(shù)據(jù)CMD_CS_LOGON UserLogon;ZeroMemory(&UserLogon,sizeof UserLogon);_sntprintf_s(UserLogon.szUserName,CountArray(UserLogon.szUserNam
31、e),pLogonInfo->szUserName);_sntprintf_s(UserLogon.szPassWord,CountArray(UserLogon.szPassWord),pLogonInfo->szPassWord);/發(fā)送登陸請求m_TCPScoketClient.SendData(MDM_GP_LOGON,SUB_CS_LOGON,&UserLogon,sizeof UserLogon);/設(shè)置界面SetTraceString(TEXT("連接服務(wù)器成功");m_LogonDlg.PostMessage(WM_CLOSE);/清理數(shù)
32、據(jù)SafeDelete(pLogonInfo);return TRUE;2、客戶端發(fā)送數(shù)據(jù)代碼:void CSpeakerClientDlg:OnBnClickedSend()/設(shè)置數(shù)據(jù)CMD_CS_CHATMSG _UserChat_Msg;ZeroMemory(&_UserChat_Msg,sizeof _UserChat_Msg);GetDlgItemText(IDC_EDITCHAT,_UserChat_Msg.szDescribe,CountArray(_UserChat_Msg.szDescribe);/效驗數(shù)據(jù)if ( _UserChat_Msg.szDescribe0
33、= TEXT('0') )SetTraceString(TEXT("聊天內(nèi)容為空,請先輸入您想說的話");return;/發(fā)送數(shù)據(jù)m_TCPScoketClient.SendData(MDM_GP_USER,SUB_CS_USERT_CHAT,&_UserChat_Msg,sizeof _UserChat_Msg);3、客戶端接收數(shù)據(jù)代碼:/客戶端接收數(shù)據(jù)和服務(wù)器段類似,也需解析、檢驗void CSpeakerClientDlg:OnReceive( int nErrorCode )/接收消息BYTE cbDataBufferSOCKET_TCP_B
34、UFFER;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 )SetTrace
35、String(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、客戶端消息的顯示代碼:/顯示的消息類型:當(dāng)用戶登錄時,
36、將用戶數(shù)據(jù)插入用戶列表中。服務(wù)器端會有xx登錄的顯示。當(dāng)用戶發(fā)消息時,服務(wù)器端就可以轉(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)/用戶進(jìn)入CMD_SC_USERCOME *pUserCo
37、me = (CMD_SC_USERCOME*)pData;/插入數(shù)據(jù)if( m_ListUser.FindString(-1,pUserCome->szUserName) = LB_ERR )/設(shè)置自己信息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.sz
38、UserName);/添加用戶列表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è)置聊天數(shù)據(jù)static CString str;CString
39、StrDescribe;StrDescribe.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");SetDlgItemText(IDC_CHATRECV,str);return true;el
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 租賃房屋協(xié)議合同書
- 租房押金租房合同范本
- 杭州裝修合同范本附件
- 單位員工旅游合同范本
- 改造小工程合同范本
- 集團(tuán)轎車出租合同范本
- 課程介紹第五講
- 12.項目其他類合同范本-(空氣檢測)
- 批發(fā)合同管理與法律風(fēng)險考核試卷
- 2025年鄉(xiāng)村全科執(zhí)業(yè)醫(yī)師考試備考反饋試題及答案
- 農(nóng)家肥撒施翻耕施工方案
- GB∕T 11071-2018 區(qū)熔鍺錠-行業(yè)標(biāo)準(zhǔn)
- 產(chǎn)品和服務(wù)戰(zhàn)略
- 2019最新中小學(xué)校安全管理制度匯編
- 心理治療師考試復(fù)習(xí)重點筆記匯總
- 《中西醫(yī)結(jié)合概論》期末考試題
- 高速鐵路知識.ppt課件
- 吹灰器檢修三措兩案
- ROHS等有害物質(zhì)削減計劃
- 產(chǎn)品推介會策劃方案
- sch壁厚等級對照表要點
評論
0/150
提交評論