網絡編程-基于TCP的簡易聊天室-實驗報告(共40頁)_第1頁
網絡編程-基于TCP的簡易聊天室-實驗報告(共40頁)_第2頁
網絡編程-基于TCP的簡易聊天室-實驗報告(共40頁)_第3頁
網絡編程-基于TCP的簡易聊天室-實驗報告(共40頁)_第4頁
網絡編程-基于TCP的簡易聊天室-實驗報告(共40頁)_第5頁
已閱讀5頁,還剩35頁未讀 繼續免費閱讀

下載本文檔

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

文檔簡介

1、精選優質文檔-傾情為你奉上網絡編程課程設計 -基于TCP的簡易聊天室一、 實驗基本信息概要1. 題目要求熟悉異步網絡編程的基本方法,掌握異步網絡編程和網絡協議設計的方法。要求采用select模型、WSAAsyncSelect模型、WSAEventSelect模型、重疊模型或完成端口等模型完成編程任務。2. 上機要求要求采用select模型、WSAAsyncSelect模型、WSAEventSelect模型、重疊模型或完成端口等模型完成下面的任務。3. 題目內容內容概要:實現一個聊天室,支持多人聊天。也可以增加私聊等功能。 4. 開發環境操作系統:Windows 7 開發語言:C+集成開發環境:

2、Microsoft Visual Studio 2010 二、 系統簡介1. 界面本軟件使用DOS控制臺界面,界面風格較為樸素,沒用使用復雜的顏色。但是對聊天時界面進行了一定的控制和修正使得界面較為美觀,易讀。服務器:客戶端:2. 軟件功能本軟件實現了聊天室基本的功能,包括公開聊天,私聊,獲取在線用戶,更改昵稱,獲得幫助等。1) 公開聊天在光標處直接輸入消息后按回車即為發送公開聊天,如下圖所示。2) 私聊使用命令【 /m 對方UID 消息 】即可發送私聊,私聊只有對方可以看到,如下圖所示:客戶端1,密聊UID為132的用戶。發送后客戶端2,UID為132的用戶收到私聊消息。3) 獲取在線用戶列

3、表使用命令【/list】即可獲得在線用戶列表,用戶列表會議系統消息的方式返回,如下圖所示。命令發送后4) 更改昵稱使用命令【/name 你的新昵稱】即可立即更改昵稱,成功修改后服務器會以系統消息的方式返回成功修改的提示。命令命令發送后5) 幫助信息使用命令【/help】即可查看服務器的歡迎信息,里面包含了該聊天室的使用幫助,如下圖所示。命令命令發送后3. 系統設計開發本軟件時,我使用了面向對象的思想,把服務器和客戶端封裝成對應的類,類設計將會在下一節做詳細介紹。通行方面我在服務器接受客戶端消息,和客戶端接受服務器消息時使用了select模型,發送信息我使用的是普通的socket原語。基本原理為

4、服務器與客戶端建立TCP連接,然后服務器負責路由消息到各個客戶端。4. 優點與缺點本軟件對流程復雜的SELECT模型進行了細致的拆分與抽象,做到了邏輯流程清晰,每個函數簡潔易懂,層次分明。例如服務器啟動函數: 它其實就完成了一個簡單的流程,初始化socket,綁定,監聽,初始化fd_socket集合,死循環調用select。通過合理的封裝底層原語和加入異常處理(異常交給頂層處理),使得代碼專注于業務流程而不是繁雜的異常判斷語句,在看下面這個函數DoSelect()。它也只完成一個簡單的流程,調用select,然后循環處理有讀事件的socket。接下來的DoFDRead()函數完成的事情也非常直

5、接,如果有事件的socket是監聽socket的話,那么就是接收到了一個新的連接,否則是接收到了新的小。從上面這個簡單的例子中可以看到,本軟件最大的優點就是精心設計的類和函數。避免了使用select模型常見的反復嵌套的循環和判斷,每個函數清晰明了。本系統還存在以下不足,首先是沒有對界面做更深入的優化,只是做了最基本的調整,讓輸入輸出更加雅觀,其次是底層原語的封裝并沒有考慮到泛用性。三、 系統詳細設計這部分的文檔在編碼之前已經基本完成,由于時間較為倉促,部分內容可能和實際有所出入。1. ChatServer類該類負責完成服務器所有操作。1) 類圖2) 成員變量Map<SOCKET, str

6、ing> m_clients 聊天者的SOCKET與昵稱的映射fd_set m_fdSocket 可用套接字集合fd_set m_fdRead 有事件發生的套接字集合SOCKET m_sListen 監聽SocketSOCKET m_sNowClient 當前處理的客戶套接字int m_nPort 監聽端口3) 方法設計void Bind()void Listen() void Select()int Recv()SOCKET Accept()封裝底層原語,并加入異常機制,使得外部調用簡約明了。構造函數傳入監聽端口,初始化m_nPortStart()1)初始化監聽套接字:void Ini

7、tListenSocket() 2)綁定套接字至本地機器:void Bind()3)進入監聽模式(設置為非阻塞):void Listen() 4)初始化可用套接字集合 void InitFDSocket()5)死循環,調用select方法 DoSelect()6)結束DoSelect()1)令m_fdRead = m_fdSocket2)調用Select()3)循環處理Select的結果 DoFdRead(Socket sRead)4)結束DoFdRead(int iReadIndex)1)判斷是否為m_sListen2)是m_sListen RecvNewConnect()3)否則 令m_s

8、NowClient = m_fdReadiReadIndex,調用RecvNewMessage()RecvNewConnect()1)判斷是否達到套接字上線2)調用Accept(),接收連接sClient3)添加sCilent 至 m_fdSocket4)添加套接字至m_clients AddClientToInfoMap(string name)AddClientToInfoMap(string name)1)以SOKCET為鍵,name為值加入MAPRecvNewMessage()1)調用Recv函數2)是否為命令 IsCommand(string str)3)是,則DoCommand(s

9、tring cmd)4)否,則DoMessage(string msg)5)結束IsCommand(string str)1)判斷是否以 "/" 開頭DoCommand(string cmd)1)判斷指令,并解析命令與參數(argc, argv)2)調用指令處理函數3)假設只有SetName命令,那么則將對應的套接字的名稱設置DoMessage(string msg)1)拼接消息與名字 BuildMsg(string msg)2)在服務器上輸出3)消息路由DispatchMessage(string msg)BuildMsg(string msg)1)從m_clients

10、中取出用戶昵稱2)拼接字符串,形成格式如下超人君(127.0.0.1) 23:49:48 說:大家好! 即為:昵稱(IP地址)時間 說:消息正文3)返回DispatchMessage(string msg)1)構造迭代器2) 遍歷m_clients,若不是自身,則派送消息Send()2. ChatClient 類該類負責處理客戶端的所有操作。1) 類圖2) 字段設計SOCKET m_sClient客戶端自身的socketSOCKET m_sServer服務器socketstring m_name昵稱sockaddr_in m_ServerAddr;服務器地址3) 方法設計構造函數根據端口號和服

11、務器IP初始化m_serverConnect()void Select()int Recv()void Send()int Select()封裝底層原語,加入異常處理,使得外部調用節約優雅。void Start()1)初始化套接字 InitClientSocket()2)連接服務器 Connect() 設置為非阻塞模式3)獲取名字并發送至服務器 InitName()4)創建新線程并顯示替他用戶發言 線程函數RecvMsgThread()5)循環 SendMsg()6)關閉客戶端 CloseClient()InitName()1)提示輸入昵稱2)獲取昵稱3)合法性判斷 判斷重復4)添加命令格式5

12、)發送至服務器SendMsg()1)讀取一行消息2)判斷是否為命令 IsCommand(string str)3)命令:處理命令 DoCommand(string cmd)4)消息:處理消息 DoMessage(string msg)DoMessage(string msg)1)發送消息 Send()2)本地回顯RecvMsgThread()1) 初始化fdSocket,將m_sClient加入2)創建fdRead3)死循環,將m_sClient拷貝至fdRead4)調用Select5)循環,并輸出收到的消息 Recv()3. SocketException類該類負責記錄SOKCET錯誤的代碼

13、以及錯誤信息。1) 類圖4. 命令協議命令格式為 /命令 參數1 參數21. 退出: /exit2. 獲取在線用戶列表:/getuser 3. 私聊: /m UID 信息4. 清屏:/clear5. 幫助:/help處理方式IsCommand(string str) 負責解析是否為命令判斷首字母是否為斜杠"/" str.at(0) = '/'ResoveCommand(string cmd, int& argc, string argv) 若是命令將命令解析為argc,argvDoCommand(string cmd) 處理命令,調用具體的XXX命令

14、處理函數DoCmdXXXX()。5. 消息格式1) 公共消息超人君(127.0.0.1) UID:100 說:大家好!李四(127.0.0.1) UID:101 說:你好!2) 私聊你悄悄地對 ABC UID:100 說: 你好CDF UID:101 悄悄地對你說: 你好3) 服務器消息【系統消息】XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX。4) 程序內部提示System Infoxxxxxxxxxxxxxxxxxxxxxxxxx四、 系統測試1. 服務器使用錯誤2. 客戶端使用錯誤3. 啟動服務器5. 啟動客戶端客戶端出現歡迎信息以及昵稱輸入提示。服務器出現連接提示6. 關

15、閉客戶端服務器出現斷開連接提示7. 啟動服務器錯誤提示給出錯誤提示信息和提示代碼8. 公開聊天所有客戶端以及服務器都會顯示。9. 私聊只有私聊的二人才能看到聊天信息,其他用戶和服務器無法看到。10. 錯誤的私聊私聊自己會得到一個錯誤提示私聊不存在的用戶也會得到一個錯誤提示11. 更名12. 獲取在線用戶13. 幫助14. 非法指令非法指令會給出錯誤提示。15. 非法的指令參數16. 連接服務器失敗五、心得體會這次實現我深入研究了select模型的使用,完成了一個簡易的聊天室。這次試驗也使我在編程技巧方面也有了很大的提高。六、完整代碼Charserverd.cpp 服務器main函數文件#inc

16、lude "ChatServer.h"#include "SockException.h"#include "InitSock.h"#include <iostream>using namespace std;InitSock initSock;int main(int argc, char* argv)if (argc < 2)cout << "Usage:" << argv0 << " Port " << endl;retur

17、n 1;ChatServer charServer(atoi(argv1);trycharServer.Start();catch (SockException& e)cout << e.GetErrorInfo() << endl;cout << "System ErrorError Code:" << e.GetErrorCode() << endl;ChatServer.h 服務器類頭文件#ifndef CHAT_SERVER_H#define CHAT_SERVER_H#include <Wi

18、nSock2.h>#include <string>#include <map>#include "ClientInfo.h"using namespace std;class ChatServerpublic:void Start();void End();ChatServer(int nPort);ChatServer(void);private:void InitFDSocket();void DoSelect();void DoFDRead(SOCKET sRead);void RecvNewConnect();string IPAdd

19、rToString(sockaddr_in sin);void AddClientToInfoMap(ClientInfo info);void RecvNewMessage();bool IsCommand(string str);void DoCommand(string cmd);void ResoveCommand(string cmd, int& argc, string argv);void DoCmdName(int argc, string argv);void DoCmdGetUsers(int argc, string argv);void DoMessage(st

20、ring msg);void DoCmdPrivateMsg(int argc, string argv);string BuildMessage(string str, bool bIsPublic);string BuildSystemMsg(string str);void DispatchMessage(string msg); void CloseConnect();string IntToString(int nNum);/=簡單封裝底層原語=void InitListenSocket();void Bind();void Listen() ;int Select();int Re

21、cv(char msgBuff);void Send(string msg, SOCKET client);SOCKET Accept(sockaddr_in& sin);/=private:map<SOCKET, ClientInfo> m_clients; fd_set m_fdSocket; fd_set m_fdRead;SOCKET m_sListen; SOCKET m_sNowClient; int m_nPort; ;#endif CHAT_SERVER_HChatServer.cpp 服務器類#include <WinSock2.h>#incl

22、ude <iostream>#include "ChatServer.h"#include "SockException.h"#pragma comment(lib, "ws2_32.lib")using namespace std;#define MAX_BUFF_SIZE 500typedef map<SOCKET, ClientInfo>:iterator map_it;ChatServer:ChatServer(int nPort)this->m_nPort = nPort;void ChatSe

23、rver:Start()InitListenSocket();Bind();Listen();InitFDSocket();while (true)DoSelect();void ChatServer:DoSelect()m_fdRead = m_fdSocket;int nRet = Select();if (nRet > 0)for (int i = 0; i < m_fdRead.fd_count; i+)DoFDRead(m_fdRead.fd_arrayi);void ChatServer:DoFDRead(SOCKET sRead)if (sRead = m_sList

24、en)RecvNewConnect();elsem_sNowClient = sRead;RecvNewMessage();void ChatServer:RecvNewConnect()if (m_fdSocket.fd_count >= FD_SETSIZE)cout << "System Info接受連接達到上限,拒絕連接"<< endl;return;sockaddr_in clientAddr;m_sNowClient = Accept(clientAddr);ClientInfo clientInfo(clientAddr);cou

25、t << "System Info接受來自"<< clientInfo.GetIp() << "的連接" << endl;FD_SET(m_sNowClient, &m_fdSocket);AddClientToInfoMap(clientInfo);string ChatServer:IPAddrToString(sockaddr_in sin)string str = inet_ntoa(sin.sin_addr);str.append(":");char szFormat2

26、0; str.append(ltoa( ntohs(sin.sin_port),szFormat,10);return str;void ChatServer:AddClientToInfoMap(ClientInfo info)m_clientsm_sNowClient = info;void ChatServer:RecvNewMessage()char msgBuffMAX_BUFF_SIZE;int nRet = Recv(msgBuff);string msg(msgBuff);if (nRet <= 0) return;if (IsCommand(msg)DoCommand(

27、msg);elseDoMessage(msg);void ChatServer:DoCommand(string cmd)int argc;string argv100;ResoveCommand(cmd, argc, argv);if (argv0 = "name")DoCmdName(argc, argv);else if (argv0 = "list")DoCmdGetUsers(argc, argv);else if(argv0 = "m")DoCmdPrivateMsg(argc, argv);elseSend("

28、【系統消息】命令不存在,請使用 /help 命令查看命令幫助", m_sNowClient);void ChatServer:ResoveCommand(string cmd, int& argc, string argv)int count = 0;for(int i = 1; i < cmd.size(); i+)char c = cmd.at(i);if (c != ' ')argvcount += c;elsecount +;argc = +count;void ChatServer:DoCmdGetUsers(int argc, string

29、argv)if (argc != 1)return;string msg;string online = IntToString(m_clients.size();msg.append("【系統消息】在線人數共" + online + "人:");msg.append( "nr");map_it begin = m_clients.begin();map_it end = m_clients.end();for (; begin != end; +begin)msg.append("t" + begin->s

30、econd.GetName() + " " + begin->second.GetIp();msg.append(" UID:" + IntToString(int)begin->first);msg.append("nr");Send(msg, m_sNowClient);void ChatServer:DoCmdName(int argc, string argv)if (argc != 2)Send("【系統消息】命令格式錯誤 USAGE: /name 你的昵稱", m_sNowClient);r

31、eturn;string name = argv1;m_clientsm_sNowClient.SetName(name);Send("【系統消息】昵稱已修改為:" + name, m_sNowClient);void ChatServer:DoMessage(string str)string msg = BuildMessage(str, true);cout << msg << endl;DispatchMessage(msg);/*string ChatServer:BuildMssage(string str)ClientInfo info

32、 = m_clientsm_sNowClient;string name = info.GetName();string ip = info.GetIp();string msg = name;if (name != ip)msg.append("(" + ip + ")");char buff10;string id(itoa(int)m_sNowClient, buff, 10);msg.append(" UID:" + id);msg.append(" 說:");msg.append("nr &qu

33、ot;);msg.append(str);return msg;*/string ChatServer:BuildSystemMsg(string str)string msg("【系統消息】");msg.append(str);return msg;string ChatServer:BuildMessage(string str, bool bIsPublic)ClientInfo info = m_clientsm_sNowClient;string name = info.GetName();string ip = info.GetIp();string uid =

34、 IntToString(int)m_sNowClient);string msg(name);if (ip != name && bIsPublic = true)msg.append("(" + ip + ")");msg.append(" ");msg.append("UID:" + uid);msg.append(" ");bIsPublic ? msg.append("說:") : msg.append("悄悄地對你說:");ms

35、g.append("nr");msg.append(" ");msg.append(str);return msg;void ChatServer:DoCmdPrivateMsg(int argc, string argv)if (argc < 3)Send("【系統消息】命令格式錯誤 USAGE: /m 目標UID 私聊內容", m_sNowClient);return;SOCKET s = (SOCKET)atoi(argv1.c_str();map_it it = m_clients.find(s);if (it = m_

36、clients.end()Send("【系統消息】該用戶不存在", m_sNowClient);return;if (it->first = m_sNowClient)Send("【系統消息】您不能和自己私聊", m_sNowClient);return;string name = it->second.GetName();string uid = IntToString(s);string toDest = BuildMessage("", false);string toSrc = string("你悄悄地對

37、 " + name + " UID:" + uid + " 說:nr ");string other;for (int i = 2; i < argc; i+)other.append(argvi);other.append(" ");Send(toDest + other, s);Send(toSrc + other, m_sNowClient);void ChatServer:DispatchMessage(string msg)map_it begin = m_clients.begin();map_it end

38、 = m_clients.end();for (; begin != end; +begin)Send(msg, begin->first);bool ChatServer:IsCommand(string str)if (str.at(0) = '/')return true;elsereturn false;void ChatServer:CloseConnect()cout << "System Info來自" << m_clientsm_sNowClient.GetIp() << "的連接已斷開&q

39、uot; << endl;closesocket(m_sNowClient);FD_CLR(m_sNowClient,&m_fdSocket);void ChatServer:InitFDSocket()FD_ZERO(&m_fdSocket);FD_SET(m_sListen, &m_fdSocket);void ChatServer:InitListenSocket()m_sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (m_sListen = INVALID_SOCKET)throw Soc

40、kException("System Error創建套接字失敗:");void ChatServer:Bind()sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_addr.S_un.S_addr = INADDR_ANY;sin.sin_port = htons(m_nPort);if (bind(m_sListen, (sockaddr*)&sin, sizeof(sin) )= SOCKET_ERROR)throw SockException("System Error無法綁定端口");els

41、ecout << "System Info成功綁定端口:" << m_nPort << endl;void ChatServer:Listen()if (listen(m_sListen, 10) = SOCKET_ERROR)throw SockException("System Error服務器監聽端口失敗");elsecout << "System Info服務器在" << m_nPort << "端口開始監聽" << end

42、l;u_long value = 1;if (ioctlsocket(m_sListen, FIONBIO, &value)throw SockException("System Error設置服務器為非阻塞模式失敗");int ChatServer:Select()int nRet = select(0, &m_fdRead, NULL, NULL, NULL);if (nRet = SOCKET_ERROR)throw SockException("System Error調用SELECT原語失敗");elsereturn nRet;

43、SOCKET ChatServer:Accept(sockaddr_in& sin)int nLen = sizeof(sin);SOCKET sNew = accept(m_sListen, (sockaddr*)&sin, &nLen);if (sNew = INVALID_SOCKET)throw SockException("System Error調用ACCEPT原語接受連接失敗");return sNew;int ChatServer:Recv(char msgBuff)int nRet = recv(m_sNowClient, msgB

44、uff, MAX_BUFF_SIZE, 0);if (nRet = SOCKET_ERROR)CloseConnect();return 0;elsemsgBuffnRet = '0'return nRet;void ChatServer:Send(string msg, SOCKET client)if (send(client, msg.c_str(), msg.size(), 0) = SOCKET_ERROR)string errorIp = m_clientsclient.GetIp();throw SockException("System Error發送

45、信息" + msg + "至" + errorIp + "失敗");string ChatServer:IntToString(int nNum)char buff10;string str(itoa(nNum, buff, 10);return str;ChatServer:ChatServer(void)ClientInfo.h 客戶信息類#include <string>using namespace std;class ClientInfopublic:ClientInfo()ClientInfo(string ip, in

46、t port, string name)this->ip = ip;this->port = port;this->name = name;ClientInfo(sockaddr_in sin)ip = inet_ntoa(sin.sin_addr);/port = ntohs(sin.sin_port);name = ip;/name.append(":");/char szFormat20; /name.append(ltoa( ntohs(sin.sin_port),szFormat,10);string GetIp()return ip;strin

47、g GetName()return name;int GetPort()return port;void SetName(string name)this->name = name;private:string name;string ip;int port;Charclientd.cpp 客戶端main函數#include "SockException.h"#include "InitSock.h"#include <iostream>#include "ChatClient.h"using namespace s

48、td;InitSock initSock;int main(int argc, char* argv)tryif (argc < 3)cout << "Usage:" << argv0 << " Ip Port " << endl;return 1;ChatClient client(argv1,atoi(argv2);client.Start();catch (SockException& e)cout << e.GetErrorInfo() << endl;cout

49、 << "System ErrorError Code:" << e.GetErrorCode() << endl;ChatClient.h 客戶端頭文件#ifndef CHAT_CLIENT_H#define CHAT_CLIENT_H#include <string>#include <WinSock2.h>using namespace std;class ChatClientpublic:ChatClient(string addr, int nPort);ChatClient(void);void Sta

50、rt();void End();SOCKET GetClientSock();private:void Connect();int Recv();void Send(string msg);int Select();void InitClientSocket();string IPAddrToString(sockaddr_in sin);void Welcome();void SendMsg();void InitName();bool IsCommand(string str);void DoMessage(string msg);void DoCommand(string cmd);vo

51、id ResoveCommand(string cmd, int& argc, string argv);string GetLine();private:string m_name;SOCKET m_sClient;SOCKET m_sServer;sockaddr_in m_serverAddr;#endif CHAT_CLIENT_HChatClient.cpp 客戶端類#include <WinSock2.h>#include <iostream>#include <process.h> #include <Windows.h>#

52、include "ChatClient.h"#include "SockException.h"#pragma comment(lib, "ws2_32.lib")#define MAX_BUFF_SIZE 500using namespace std;void RecvMsgThread(void * param);ChatClient:ChatClient(string addr, int nPort)m_serverAddr.sin_addr.S_un.S_addr = inet_addr(addr.c_str();m_serv

53、erAddr.sin_port = htons(nPort);m_serverAddr.sin_family = AF_INET;ChatClient:ChatClient(void)void ChatClient:Start()InitClientSocket();Connect();Welcome();Sleep(1000);InitName();_beginthread(RecvMsgThread,NULL,(void*)this); while (true)SendMsg();void ChatClient:Welcome()cout << "=" << endl;cout << " *歡迎使用簡易聊天室* " << endl;cout << "操作指南:1)在控制臺中直接

溫馨提示

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

評論

0/150

提交評論