题 专
班 时
:简单的聊天室程序业:计算机网络 级:网络一班
间:2011.5.05
目
目 录
[摘要]…………………………………………………………………3 一 设计内容…………………………………………………………..3
1、设计目的……………………………………………………………………3 2、设计要求……………………………………………………………………3
二 设计原理……………………………………………………………3 三 设计过程…………………………………………………………….4
1、程序设计流程及源代码…………………………………………………….4 (1)流程图……………………………………………………………………….4 (2)源代码……………………………………………………………………….4 I服务器………………………………………………………………………4 II客户端……………………………………………………………………..9 2、调试分析过程描述……………………………………………………………14 3、结果分析………………………………………………………………………19
[结论]………………………………………………..………………….20 [参考文献]………………………………………………….…………..20
2
[摘要]
当今世界正处于信息时代,计算机和通信网络是这一时代所谓“信息基础设施”。在互联网相当普及的今天,在互联网上聊天对很多“网虫”来说已经是家常便饭了。聊天室程序可以说是网上最简单的多点通信程序。一个简单的聊天室, 从程序员的观点来看就是在多个I/O端点之间实现多对多的通信。基于SOCKET的局域网通信是一种灵活的、易于实现的、低成本的方法。它可以运行在各种使用TCP/IP协议作为通讯协议的网络上。而在SOCKET API的帮助下,开发基于SOCKET的局域网通信软件也是易于实现的。
[正文] 一、设计内容 1、设计的目的
综合运用本课程及计算机网络的相关知识设计并实现一个网络应用程序,以Visual C++作为开发平台,通过实践复习巩固课堂所学的理论知识,提高对所学知识的综合应用能力。
2、设计要求
采用客户/服务器模式,分为客户端程序和服务器端程序。服务器采用WINSOCK I/O模型中的任一种,支持多个客户同时在线聊天。客户端程序和服务器程序通过网络交换聊天字符串内容,服务器窗口的列表框中显示当前在线用户,支持客户端之间的私聊(可以通过服务器中转,或考虑UDP打洞直接建立端端连接)。
3
二、设计原理
服务器端通过socket()系统调用创建一个Socket数组后(即设定了接受连接客户的最大数目),与指定的本地端口绑定bind(),就可以在端口进行侦听listen()。如果有客户端连接请求,则在数组中选择一个空Socket,将客户端地址赋给这个Socket。然后登录成功的客户就可以在服务器上聊天了。客户端程序相对简单,只需要建立一个Socket与服务器端连接,成功后通过这个Socket来发送和接收数据就可以了。socket聊天室基本原理是,抛开CGI(公共网关接口)和www服务器,根据html规范,接收到浏览器的请求以后,模仿www服务器的响应,将聊天内容发回浏览器。在浏览器看来就像浏览一个巨大的页面一样始终处于页面联接状态, 这就是一个专门的聊天服务器,一个简化了的www服务器。
三、设计过程
1、程序设计流程及源代码 (1)流程图
(2)源代码 I服务器:
// ServerDlg.cpp : implementation file //
#include \"stdafx.h\"
4
#include \"Server.h\" #include \"ServerDlg.h\" #ifdef _DEBUG
#define new DEBUG_NEW #undef THIS_FILE
static char THIS_FILE[ ] = __FILE__; #endif
///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog {
public: CAboutDlg(); protected: DECLARE_MESSAGE_MAP() };
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { }
void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); }
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////// // CServerDlg dialog
CServerDlg::CServerDlg(CWnd* pParent /*=NULL*/) : CDialog(CServerDlg::IDD, pParent) { m_hIcon = AfxGetApp()->LoadIcon(IDI_ICONAPP); }
void CServerDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST_USER, m_lbUser); DDX_Text(pDX, IDC_EDIT_DISPLAY, m_strDisplay); }
BEGIN_MESSAGE_MAP(CServerDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BN_START, OnBnStart) ON_BN_CLICKED(IDC_BN_CLOSE, OnBnClose) ON_MESSAGE(WM_RECEIVE, OnReceive) ON_MESSAGE(WM_CLIENTCLOSE, OnClientClose) ON_MESSAGE(WM_ACCEPT, OnAccept) ON_WM_DESTROY() END_MESSAGE_MAP()
///////////////////////////////////////////////////////////////////////////// // CServerDlg message handlers BOOL CServerDlg::OnInitDialog() { CDialog::OnInitDialog(); ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) {
5
pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon m_iNetPort = 3127; m_Server.Initialize(this); m_bServer = FALSE; GetDlgItem(IDC_BN_CLOSE)->EnableWindow(FALSE); return TRUE; // return TRUE unless you set the focus to a control }
void CServerDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialog::OnSysCommand(nID, lParam); } }
void CServerDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } }
HCURSOR CServerDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; }
void CServerDlg::OnBnStart() { // 创建套接字 m_Server.Create(m_iNetPort); // 开始监听 m_Server.Listen(); // 显示 CString strInfo; strInfo.LoadString(IDS_CREATE_SERVER); ShowInDisplay(strInfo); m_bServer = TRUE; GetDlgItem(IDC_BN_START)->EnableWindow(FALSE); GetDlgItem(IDC_BN_CLOSE)->EnableWindow(TRUE); }
void CServerDlg::OnBnClose()
6
{ CloseServer(); GetDlgItem(IDC_BN_START)->EnableWindow(TRUE); GetDlgItem(IDC_BN_CLOSE)->EnableWindow(FALSE); }
void CServerDlg::OnAccept() { // 创建新客户 CMySocket *pNewClient = new CMySocket; pNewClient->Initialize(this); m_Server.Accept(*pNewClient); // 放入链表中 m_lsClient.AddTail(pNewClient); }
void CServerDlg::OnClientClose(WPARAM wParam, LPARAM lParam) { CMySocket *p_delClient = (CMySocket*)wParam; // 关闭该用户 p_delClient->ShutDown(); char buffer[BUFFERSIZE]; while(p_delClient->Receive(buffer, BUFFERSIZE)>0); p_delClient->Close(); // 在用户链表中删除该用户 POSITION psCur, psList = m_lsClient.GetHeadPosition(); CMySocket *p_curClient; while(psList!=NULL) { psCur = psList; p_curClient = (CMySocket *)m_lsClient.GetNext(psList); if(p_delClient==p_curClient) { m_lsClient.RemoveAt(psCur); break; } } // 发送信息告诉其他用户该客户退出 CString strMsg; strMsg.LoadString(IDS_CLIENT_CLOSE); NETMESSAGE netMessage(PTC_CLIENT_QUIT, p_delClient->GetName(), p_delClient->GetName() + strMsg); SendToAllClient(netMessage); // 界面上清理该用户信息 m_lbUser.DeleteString(m_lbUser.FindString(-1, p_delClient->GetName())); ShowInDisplay(p_delClient->GetName() + strMsg); // 删除该用户 delete p_delClient; }
void CServerDlg::OnReceive(WPARAM wParam, LPARAM lParam) { CMySocket *p_curClient = (CMySocket*)wParam; NETMESSAGE netMessage; p_curClient->Receive((char *)&netMessage, sizeof(netMessage)); switch(netMessage.type) { case PTC_NEW: // 新客户加入 NewClientAdd(p_curClient, &netMessage); break; case PTC_SAY: // 普通聊天 ShowMessage(netMessage); break; } // 把加工过的信息发送给其他用户
7
_T(\"\"),
SendToAllClient(netMessage); }
void CServerDlg::CloseServer() { if(!m_bServer) return; // 向所有客户发送服务端关闭的信息 CString strInfo; strInfo.LoadString(IDS_CLOSESERVER); NETMESSAGE netMessage(PTC_SYSTEM, _T(\"\"), _T(\"\"), strInfo); SendToAllClient(netMessage); // 服务端界面显示关闭信息 ShowInDisplay(strInfo); // 关闭服务端 m_Server.Close(); // 关闭客户端 char buffer[BUFFERSIZE]; CMySocket *m_pClient; POSITION psList = m_lsClient.GetHeadPosition(); while(psList!=NULL) { m_pClient = (CMySocket *)m_lsClient.GetNext(psList); m_pClient->ShutDown(); while(m_pClient->Receive(buffer, BUFFERSIZE)>0); m_pClient->Close(); delete m_pClient; } m_lsClient.RemoveAll(); m_bServer = FALSE; }
// 向所有户发出信息
void CServerDlg::SendToAllClient(const NETMESSAGE &netMessage) { CMySocket *m_pClient; POSITION psList = m_lsClient.GetHeadPosition(); while(psList!=NULL) { m_pClient = (CMySocket *)m_lsClient.GetNext(psList); m_pClient->SendMsg(netMessage); } }
void CServerDlg::ShowInDisplay(CString str) { m_strDisplay += str + \"\\r\\n\"; UpdateData(false); }
void CServerDlg::OnDestroy() { CDialog::OnDestroy(); // TODO: Add your message handler code here }
// 新用户加入
void CServerDlg::NewClientAdd(CMySocket *p_client, NETMESSAGE *netMessage) { // 存入用户名字 p_client->SetName(netMessage->form); // 在列表框中显示该用户 m_lbUser.AddString(p_client->GetName()); // 发送用户列表给该客户 NETMESSAGE netMsg(PTC_USER_LIST, _T(\"\"), _T(\"\"), _T(\"\")); int iListLen = m_lbUser.GetCount(); CString strMsg; for(int index=m_lbUser.GetTopIndex(); index m_lbUser.GetText(index, strMsg); strcpy(netMsg.data, (LPCTSTR)strMsg); p_client->SendMsg(netMsg); } // 加工信息 strMsg.LoadString(IDS_NEW_CLIENT); strMsg = p_client->GetName() + strMsg; strcpy(netMessage->data, (LPCTSTR)strMsg); // 显示该用户进入 ShowInDisplay(strMsg); } // 显示普通消息 void CServerDlg::ShowMessage(NETMESSAGE netMessage) { CString strTo = netMessage.to; if(strTo==_T(\"\")) { strTo = \"所有人\"; } CString strForm = netMessage.form; CString strMsg = netMessage.data; CString strOut = strForm + \"对\" + strTo + \"说:\" + strMsg; ShowInDisplay(strOut); } II客户端: // ClientDlg.cpp : implementation file // #include \"stdafx.h\" #include \"Client.h\" #include \"ClientDlg.h\" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[ ] = __FILE__; #endif ///////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public: CAboutDlg(); enum { IDD = IDD_ABOUTBOX }; protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CClientDlg dialog CClientDlg::CClientDlg(CWnd* pParent /*=NULL*/) : CDialog(CClientDlg::IDD, pParent) { m_strDisplay = _T(\"\"); m_strSend = _T(\"\"); 9 m_bWhispering = FALSE; m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CClientDlg::DoDataExchange(CDataExchange* pDX) { CDialog::DoDataExchange(pDX); DDX_Control(pDX, IDC_LIST_USER, m_lbUser); DDX_Text(pDX, IDC_EDIT_DIAPLAY, m_strDisplay); DDX_Text(pDX, IDC_EDIT_SEND, m_strSend); DDX_Check(pDX, IDC_CHECK_WHISPERING, m_bWhispering); } BEGIN_MESSAGE_MAP(CClientDlg, CDialog) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_BN_CONNECT, OnBnConnect) ON_BN_CLICKED(IDC_BN_CLOSE, OnBnClose) ON_BN_CLICKED(IDC_BN_SEND, OnBnSend) ON_MESSAGE(WM_RECEIVE, OnReceive) ON_MESSAGE(WM_SOCKETCLOSE, OnServerClose) ON_MESSAGE(WM_SEND, OnSend) ON_MESSAGE(WM_CONNCET, OnConnect) ON_LBN_DBLCLK(IDC_LIST_USER, OnDblclkListUser) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CClientDlg message handlers BOOL CClientDlg::OnInitDialog() { CDialog::OnInitDialog(); ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != NULL) { CString strAboutMenu; strAboutMenu.LoadString(IDS_ABOUTBOX); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } SetIcon(m_hIcon, TRUE); // Set big icon SetIcon(m_hIcon, FALSE); // Set small icon // 参数初始化 m_Socket.Initialize(this); m_strNetIP = _T(\"127.0.0.1\"); m_iNetPort = 3127; m_bConnect = FALSE; GetDlgItem(IDC_BN_CLOSE)->EnableWindow(FALSE); GetDlgItem(IDC_BN_SEND)->EnableWindow(FALSE); return TRUE; // return TRUE unless you set the focus to a control } void CClientDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { 10 CDialog::OnSysCommand(nID, lParam); } } void CClientDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // device context for painting SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0); int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; dc.DrawIcon(x, y, m_hIcon); } else { CDialog::OnPaint(); } } HCURSOR CClientDlg::OnQueryDragIcon() { return (HCURSOR) m_hIcon; } void CClientDlg::OnBnConnect() { CString strConnectError; // 获取用户名字 CInputDlg inputDlg; int iResult = inputDlg.DoModal(); if(iResult==IDCANCEL){return;} m_strName = inputDlg.m_strName; // 创建套接字 if(!m_Socket.Create()) { strConnectError.LoadString(IDS_FAIL_CREATE_SOCKET); ShowInDisplay(strConnectError); return; } CString strInfo; strInfo.LoadString(IDS_CONNECTING); ShowInDisplay(strInfo); // 连接服务器 if(!m_Socket.Connect(m_strNetIP, m_iNetPort)) { int n = m_Socket.GetLastError(); strConnectError.LoadString(IDS_FAIL_CONNECT); ShowInDisplay(strConnectError); return; } strInfo.LoadString(IDS_SUCCEED_CONNECT); ShowInDisplay(strInfo); m_bConnect = TRUE; // 发送自已的名字 NETMESSAGE netMessage(PTC_NEW, m_strName, _T(\"\"), _T(\"\")); m_Socket.SendMsg(netMessage); GetDlgItem(IDC_BN_CONNECT)->EnableWindow(FALSE); GetDlgItem(IDC_BN_CLOSE)->EnableWindow(TRUE); GetDlgItem(IDC_BN_SEND)->EnableWindow(TRUE); } void CClientDlg::OnBnClose() 11 { if(m_bConnect) { m_Socket.Close(); m_bConnect = FALSE; GetDlgItem(IDC_BN_CONNECT)->EnableWindow(TRUE); GetDlgItem(IDC_BN_CLOSE)->EnableWindow(FALSE); GetDlgItem(IDC_BN_SEND)->EnableWindow(FALSE); m_lbUser.ResetContent(); CString strMsg; strMsg.LoadString(IDS_BREAK); ShowInDisplay(strMsg); } } void CClientDlg::OnBnSend() { OnSend(); m_strSend = _T(\"\"); UpdateData(FALSE); } void CClientDlg::OnReceive() { NETMESSAGE netMessage; m_Socket.Receive((char *)&netMessage, sizeof(netMessage)); CString strForm = netMessage.form; // 过滤自已发出的信息 if(strForm==m_strName) return; CString strTo = netMessage.to; CString strMsg = netMessage.data; switch(netMessage.type) { case PTC_USER_LIST: // 用户列表 m_lbUser.AddString(strMsg); break; case PTC_NEW: // 新用户进入 m_lbUser.AddString(strForm); ShowInDisplay(strMsg); break; case PTC_SAY: ShowMessage(netMessage); case PTC_WHISPERING: // 密语 if(strTo!=m_strName) return; ShowMessage(netMessage); break; case PTC_CLIENT_QUIT: // 有用户退出 m_lbUser.DeleteString(m_lbUser.FindString(-1, strForm)); ShowInDisplay(strMsg); break; } } void CClientDlg::OnServerClose() { if(!m_bConnect) return; CString strMsg; strMsg.LoadString(IDS_SERVER_CLOSE); ShowInDisplay(strMsg); m_Socket.Close(); m_bConnect = FALSE; GetDlgItem(IDC_BN_CONNECT)->EnableWindow(TRUE); GetDlgItem(IDC_BN_CLOSE)->EnableWindow(FALSE); GetDlgItem(IDC_BN_SEND)->EnableWindow(FALSE); m_lbUser.ResetContent(); } 12 void CClientDlg::OnSend() { UpdateData(TRUE); // 主要是填充NETMESSAGE结构体 NETMESSAGE netMessage; strcpy(netMessage.form, m_strName); strcpy(netMessage.data, m_strSend); // 取得列表框中的用户 if(m_lbUser.GetText(m_lbUser.GetCurSel(), netMessage.to)==LB_ERR) { strcpy(netMessage.to, _T(\"\")); } if(m_bWhispering) { // 密语 CString strTo = netMessage.to; if(strTo==_T(\"\")) { MessageBox(\"请选择对方!\错误\"); return; } netMessage.type = PTC_WHISPERING; } else { strcpy(netMessage.to, _T(\"所有人\")); netMessage.type = PTC_SAY; } // 发送该信息 m_Socket.SendMsg(netMessage); // 显示发送信息 CString strTo = netMessage.to; CString strMsg = netMessage.data; ShowInDisplay(\"你对\" + strTo + \"说:\" + strMsg); } void CClientDlg::ShowInDisplay(CString str) { m_strDisplay += str + \"\\r\\n\"; UpdateData(FALSE); } void CClientDlg::ShowMessage(const NETMESSAGE &netMessage) { CString strTo = netMessage.to; if(strTo==_T(\"\")) { strTo = \"所有人\"; } else if(strTo==m_strName) { strTo = \"你\"; } CString strForm = netMessage.form; CString strMsg = netMessage.data; CString strOut = strForm + \"对\" + strTo + \"说:\" + strMsg; ShowInDisplay(strOut); } void CClientDlg::OnDblclkListUser() { // TODO: Add your control notification handler code here m_lbUser.SetCurSel(-1); } void CClientDlg::OnConnect() 13 { m_bConnect = TRUE; } 2、调试分析过程描述 Login服务器: 启动服务器: 14 Login客户端: 创建张三、李四、王五客户端,并与服务器连接: 15 16 服务器显示: 17 开始聊天,群聊: 张三与李四私聊: 18 王五看不到私聊内容 19 聊天室是分客户端和服务端两个应用程序的,两个应用程序要想实现交互必须编写相应的指令和识别指令的代码,我写的这是个指令依次是 向服务端请求在线人数列表 用户私聊 用户退出 的命令,但用户想要进行以上动作中的任何一个时,在用户按下按键的时候,客户端都是向服务端发送相应的指令,再由服务端实际的执行。只要弄懂客户端和服务端的交互过程后,这一切都会看上去很简单的。需要改进的地方: ①消息记录中未能提供时间的实时显示; ②客户端没有自己的ID标志; ③服务器不能与客户端聊天。 3、结果分析: 设计基本符合要求,支持多个客户同时在线聊天,并支持客户端之间的私聊;服务器窗口的列表框中能显示当前在线用户,并能监视客户端的群聊内容。然而这个聊天室还有许多不足之处需要改进,界面也比较粗糙,需要进一步美化。 20
因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- 517ttc.cn 版权所有 赣ICP备2024042791号-8
违法及侵权请联系:TEL:199 18 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务