第 61 章
Address, Port);
lvi.mask = LVIF_TEXT;
lvi.iSubItem = 2;
lvi.pszText = sIPPort.GetBuffer(sIPPort.GetLength());
GetListCtrl().SetItem(&lvi);
}
添加一个在列表中删除某用户的cāo作,函数名为void DeleteChatter(CString Ncom),其中
Ncom 就是用户名。代码如下:
void CChattersListView::DeleteChatter(CString Ncom)
{
LVFINDINFO lvfi;
lvfi.flags = LVFI_STRING;
lvfi.psz = Ncom.GetBuffer(Ncom.GetLength());
//在列表中查找用户名,得到其索引值
int nFoundAt = GetListCtrl().FindItem(&lvfi);
//如果找到则将其删除
if(nFoundAt != -1)
GetListCtrl().DeleteItem(nFoundAt);
}
(4)编写聊天信息界面
新建一个类,取名为CChatView,其父类为CView,该类用于显示聊天信息。为其添加
一个成员变量:CEdit m_EditBox,负责聊天信息的显示。
为WM_CREATE 消息添加响应函数,在此函数中,对m_EditBox 进行初始化,代码如
下:
int CChatView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
枫叶文学网www.fywxw.com
第11 章 网络编程
·295·
// TODO: Add your specialized creation code here
CRect rect;
GetClientRect(&rect);
m_EditBox.Create(WS_VISIBLE |
WS_BORDER |
WS_CHILD |
ES_MULTILINE |
WS_VSCROLL,
rect, this , 0);
return 0;
}
当窗口大小变化时,会激发WM_SIZE 消息。为此消息编写响应函数,代码如下:
void CChatView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// TODO: Add yourcomssage handler code here
m_EditBox.MoveWindow(0 , 0 , cx , cy , FALSE);
}
最后,为该类添加一函数void ShowMessage(LPCTSTR lpszMessage),此函数用于消息的
显示,其参数为消息内容。代码如下:
void CChatView::ShowMessage(LPCTSTR lpszMessage)
{
CString strTemp = lpszMessage;
strTemp += _T("\r\n");
int len = m_EditBox.GetWindowTextLength();
m_EditBox.SetSel(len,len);
m_EditBox.ReplaceSel(strTemp);
}
至此,负责显示客户端信息以及聊天信息的两个窗口都已编写完毕,接下来要在主框架
中进行分割,也就是将主框架分割成显示客户端信息的窗口和显示聊天信息的窗口两部分。
(5)分割框架
为类CMainFrcom 添加成员变量CSplitterWnd m_wndSplitter,用于分割主框架。重载函
数virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext),该函数
在主框架生成期间被调用,在此函数中添加分割窗口的cāo作,代码如下:
BOOL CMainFrcom::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
CCreateContext* pContext)
{
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·296·
//将窗口分为两行一列
if (!m_wndSplitter.CreateStatic(this, 2, 1))
return FALSE;
//指定每个窗口的位置及初始大小
if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CChattersListView), CSize(150, 100),
pContext) ||
!m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CChatView), CSize(300, 300), pContext))
{
m_wndSplitter.DestroyWindow();
return FALSE;
}
return TRUE;
}
在此函数中用到了类CChatView 和CChattersListView,因此在文件“MainFrm.cpp”的
开头要加入下面两个语句:
#include "ChatView.h"
#include "ChattersListView.h"
现在编译、运行程序,可以看到主框架已经被分割成两个窗口,如图11-8 所示。
图11-8 主框架被分割为两部分
至此,界面部分已经完成。
(6)创建CListeningSocket 类
该类用户jiān tīng客户端的连接,创建方式与CChattersListView 类的创建方式相同,只是基
类选择CSocket。
在类的定义中声明宏DECLARE_DYNAMIC(CListeningSocket),该宏的作用是可以在运
枫叶文学网www.fywxw.com
第11 章 网络编程
·297·
行时获得类的信息。相应的,如果在类定义中声明了此宏,则在类实现时需要声明宏
IMPLEMENT_DYNAMIC(CListeningSocket, CSocket)。
为该类增加成员变量CChatServerDoc* m_pDoc,这是因为本程序采用文档-视图结构,
所有关于数据的cāo作都可以通过文档类进行,m_pDoc 就是文档类的一个指针。这里用到了
CChatServerDoc 类, 因此在CListeningSocket 类定义的外部, 要加上语句class
CChatServerDoc,在文件“ListeningSocket.cpp”中,要加入语句#include "ChatServerDoc.h"。
为CListeningSocket 类新增加一个构造函数CListeningSocket(CChatServerDoc* pDoc),
代码如下:
CListeningSocket::CListeningSocket(CChatServerDoc* pDoc)
{
m_pDoc = pDoc;
}
最后要重载CSocket 类的OnAccept()函数,代码如下:
void CListeningSocket::OnAccept(int nErrorCode)
{
//调用CScoekt 的OnAccept 函数
CSocket::OnAccept(nErrorCode);
//其余工作jiāo给文档类处理
m_pDoc->ProcessAccept();
}
可以看出,CListeningSocket 类的工作就是接收客户端连接,然后具体的数据传输工作jiāo
给CChatServerDoc 类来处理。这里的ProcessAccept()函数是CChatServerDoc 类的一个成员函
数。在后面的CChatServerDoc 类成员函数的实现中,读者可以看到此函数的具体实现过程。
(7)创建CClientSocket 类
当服务器jiān tīng到一个连接并接收后,会生成一个新的CSocket 对象来处理服务器和客户
端的数据传输。在本程序中,将这新生成的CSocket 封装成CClientSocket 类。创建该类的过
程和创建CListeningSocket 类完全一样。
同样的,在类的定义中声明宏DECLARE_DYNAMIC(CClientSocket),在类的实现中声
明宏IMPLEMENT_DYNAMIC(CClientSocket, CSocket)。
为该类加入成员变量,如表11-7 所示。
表11-7 CClientSocket 类成员变量及其说明
成员变量 说明
CChatServerDoc* m_pDoc 关联的文档类,很多数据处理工作在文档类中完成
CSocketFile* m_pFile CArchive 与CSocket 的联系桥梁,通过它来创建CArchive 类
CArchive* m_pArchiveIn 用于数据接收
CArchive* m_pArchiveOut 用于数据发送
int m_nMsgCount 信息的数目
为该类加入成员函数,如表11-8 所示。
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·298·
表11-8 CClientSocket 类成员函数及其说明
成员函数 说明
CClientSocket(CChatServerDoc* m_pDoc) 构造函数
void Init() 对成员变量进行初始化
void SendMsg(CMsg* pMsg) 发送消息
void ReceiveMsg(CMsg* pMsg) 接收消息
virtual void OnReceive(int nErrorCode) 重载CSocket 的OnReceive()函数
在创建的过程中用到了类CMsg 和类CChatServerDoc,因此在文件“ClientSocket.h”的
开始声明两个类。
class CMsg;
class CChatServerDoc;
在“ClientSocket.cpp”文件的开始要把两个头文件包含进来。代码如下:
#include "msg.h"
#include "ChatServerDoc.h"
最后为各函数编写代码。构造函数代码如下:
CClientSocket::CClientSocket(CChatServerDoc* pDoc)
{
m_pDoc = pDoc;
m_nMsgCount = 0;
m_pFile = NULL;
m_pArchiveIn = NULL;
m_pArchiveOut = NULL;
}
析构函数代码如下:
CClientSocket::~CClientSocket()
{
if (m_pArchiveOut != NULL)
delete m_pArchiveOut;
if (m_pArchiveIn != NULL)
delete m_pArchiveIn;
if (m_pFile != NULL)
delete m_pFile;
}
初始化函数Init()代码如下:
void CClientSocket::Init()
{
m_pFile = new CSocketFile(this);
m_pArchiveIn = new CArchive(m_pFile,CArchive::load);
枫叶文学网www.fywxw.com
第11 章 网络编程
·299·
m_pArchiveOut = new CArchive(m_pFile,CArchive::store);
}
发送信息函数SendMsg()代码如下:
void CClientSocket::SendMsg(CMsg* pMsg)
{
if (m_pArchiveOut != NULL)
{
//数据由pMsg 输出到m_pArchiveOut
pMsg->Serialize(*m_pArchiveOut);
//将数据写入文件
m_pArchiveOut->Flush();
}
}
接收信息函数ReceiveMsg()代码如下:
void CClientSocket::ReceiveMsg(CMsg* pMsg)
{
//数据由m_pArchiveIn 读入到pMsg
pMsg->Serialize(*m_pArchiveIn);
}
重载的OnReceive()函数代码如下:
void CClientSocket::OnReceive(int nErrorCode)
{
//调用CSocekt 的OnReceive 函数
CSocket::OnReceive(nErrorCode);
//其余工作jiāo给文档类处理
m_pDoc->ProcessReceive(this);
}
其中,ProcessReceive()函数是CChatServerDoc 类的一个成员函数,在后面将会看到它的
具体实现。
(8)创建设定端口对话框
对服务器来说,它可以通过“设定端口号”对话框来设定服务端口,如图11-9 所示。其
中,对话框的ID 为IDD_SETUPDLG,Edit 控件的ID 为IDC_PORT。
图11-9 “设定端口号”对话框
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·300·
打开“ClassWizard”对话框,为此对话框创建一个类,取名为CPortDlg。然后为Edit
控件增加变量int m_Port,在构造函数中将其初始化为2000。
最后,在菜单栏中新建一菜单项,当单击此菜单项时,就会弹出如图11-10 所示的对话
框。
图11-10 对话框所对应菜单栏
此菜单项的ID 为ID_SETPORT。
(9)实现CChatServerDoc 类
大量的工作都是在CChatServerDoc 类中实现的。在开始编写代码前,首先在文件
“ChatServerDoc.h”中CChatServerDoc 类定义的前面加入如下的类声明语句:
class CListeningSocket;
class CClientSocket;
class CMsg;
在文件“ChatServerDoc.cpp”的开头加入如下的include 语句:
#include "MainFrm.h"
#include"PortDlg.h"
#include "ChattersListView.h"
#include "ChatView.h"
#include "ListeningSocket.h"
#include "ClientSocket.h"
#include "Msg.h"
在文件“ChatServerDoc.cpp”的开头加入下面的定义语句(它定义了5 种不同类型的消
息)。
#define LEAVING_CHAT 1
#define SENDING_CHATTERS_LIST 2
#define SENDING_NICKNAME 3
#define NORMAL_MESSAGE 4
#define USED_NAME 5
这5 种类型消息的具体含义在下面的程序中会详细说明。
接下来,为CChatServerDoc 类添加一些成员变量,如表11-9 所示。
表11-9 CChatServerDoc 类成员变量及其说明
成员变量 说
松语文学免费小说阅读_www.16sy.com
lvi.mask = LVIF_TEXT;
lvi.iSubItem = 2;
lvi.pszText = sIPPort.GetBuffer(sIPPort.GetLength());
GetListCtrl().SetItem(&lvi);
}
添加一个在列表中删除某用户的cāo作,函数名为void DeleteChatter(CString Ncom),其中
Ncom 就是用户名。代码如下:
void CChattersListView::DeleteChatter(CString Ncom)
{
LVFINDINFO lvfi;
lvfi.flags = LVFI_STRING;
lvfi.psz = Ncom.GetBuffer(Ncom.GetLength());
//在列表中查找用户名,得到其索引值
int nFoundAt = GetListCtrl().FindItem(&lvfi);
//如果找到则将其删除
if(nFoundAt != -1)
GetListCtrl().DeleteItem(nFoundAt);
}
(4)编写聊天信息界面
新建一个类,取名为CChatView,其父类为CView,该类用于显示聊天信息。为其添加
一个成员变量:CEdit m_EditBox,负责聊天信息的显示。
为WM_CREATE 消息添加响应函数,在此函数中,对m_EditBox 进行初始化,代码如
下:
int CChatView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CView::OnCreate(lpCreateStruct) == -1)
return -1;
枫叶文学网www.fywxw.com
第11 章 网络编程
·295·
// TODO: Add your specialized creation code here
CRect rect;
GetClientRect(&rect);
m_EditBox.Create(WS_VISIBLE |
WS_BORDER |
WS_CHILD |
ES_MULTILINE |
WS_VSCROLL,
rect, this , 0);
return 0;
}
当窗口大小变化时,会激发WM_SIZE 消息。为此消息编写响应函数,代码如下:
void CChatView::OnSize(UINT nType, int cx, int cy)
{
CView::OnSize(nType, cx, cy);
// TODO: Add yourcomssage handler code here
m_EditBox.MoveWindow(0 , 0 , cx , cy , FALSE);
}
最后,为该类添加一函数void ShowMessage(LPCTSTR lpszMessage),此函数用于消息的
显示,其参数为消息内容。代码如下:
void CChatView::ShowMessage(LPCTSTR lpszMessage)
{
CString strTemp = lpszMessage;
strTemp += _T("\r\n");
int len = m_EditBox.GetWindowTextLength();
m_EditBox.SetSel(len,len);
m_EditBox.ReplaceSel(strTemp);
}
至此,负责显示客户端信息以及聊天信息的两个窗口都已编写完毕,接下来要在主框架
中进行分割,也就是将主框架分割成显示客户端信息的窗口和显示聊天信息的窗口两部分。
(5)分割框架
为类CMainFrcom 添加成员变量CSplitterWnd m_wndSplitter,用于分割主框架。重载函
数virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext),该函数
在主框架生成期间被调用,在此函数中添加分割窗口的cāo作,代码如下:
BOOL CMainFrcom::OnCreateClient(LPCREATESTRUCT /*lpcs*/,
CCreateContext* pContext)
{
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·296·
//将窗口分为两行一列
if (!m_wndSplitter.CreateStatic(this, 2, 1))
return FALSE;
//指定每个窗口的位置及初始大小
if (!m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CChattersListView), CSize(150, 100),
pContext) ||
!m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CChatView), CSize(300, 300), pContext))
{
m_wndSplitter.DestroyWindow();
return FALSE;
}
return TRUE;
}
在此函数中用到了类CChatView 和CChattersListView,因此在文件“MainFrm.cpp”的
开头要加入下面两个语句:
#include "ChatView.h"
#include "ChattersListView.h"
现在编译、运行程序,可以看到主框架已经被分割成两个窗口,如图11-8 所示。
图11-8 主框架被分割为两部分
至此,界面部分已经完成。
(6)创建CListeningSocket 类
该类用户jiān tīng客户端的连接,创建方式与CChattersListView 类的创建方式相同,只是基
类选择CSocket。
在类的定义中声明宏DECLARE_DYNAMIC(CListeningSocket),该宏的作用是可以在运
枫叶文学网www.fywxw.com
第11 章 网络编程
·297·
行时获得类的信息。相应的,如果在类定义中声明了此宏,则在类实现时需要声明宏
IMPLEMENT_DYNAMIC(CListeningSocket, CSocket)。
为该类增加成员变量CChatServerDoc* m_pDoc,这是因为本程序采用文档-视图结构,
所有关于数据的cāo作都可以通过文档类进行,m_pDoc 就是文档类的一个指针。这里用到了
CChatServerDoc 类, 因此在CListeningSocket 类定义的外部, 要加上语句class
CChatServerDoc,在文件“ListeningSocket.cpp”中,要加入语句#include "ChatServerDoc.h"。
为CListeningSocket 类新增加一个构造函数CListeningSocket(CChatServerDoc* pDoc),
代码如下:
CListeningSocket::CListeningSocket(CChatServerDoc* pDoc)
{
m_pDoc = pDoc;
}
最后要重载CSocket 类的OnAccept()函数,代码如下:
void CListeningSocket::OnAccept(int nErrorCode)
{
//调用CScoekt 的OnAccept 函数
CSocket::OnAccept(nErrorCode);
//其余工作jiāo给文档类处理
m_pDoc->ProcessAccept();
}
可以看出,CListeningSocket 类的工作就是接收客户端连接,然后具体的数据传输工作jiāo
给CChatServerDoc 类来处理。这里的ProcessAccept()函数是CChatServerDoc 类的一个成员函
数。在后面的CChatServerDoc 类成员函数的实现中,读者可以看到此函数的具体实现过程。
(7)创建CClientSocket 类
当服务器jiān tīng到一个连接并接收后,会生成一个新的CSocket 对象来处理服务器和客户
端的数据传输。在本程序中,将这新生成的CSocket 封装成CClientSocket 类。创建该类的过
程和创建CListeningSocket 类完全一样。
同样的,在类的定义中声明宏DECLARE_DYNAMIC(CClientSocket),在类的实现中声
明宏IMPLEMENT_DYNAMIC(CClientSocket, CSocket)。
为该类加入成员变量,如表11-7 所示。
表11-7 CClientSocket 类成员变量及其说明
成员变量 说明
CChatServerDoc* m_pDoc 关联的文档类,很多数据处理工作在文档类中完成
CSocketFile* m_pFile CArchive 与CSocket 的联系桥梁,通过它来创建CArchive 类
CArchive* m_pArchiveIn 用于数据接收
CArchive* m_pArchiveOut 用于数据发送
int m_nMsgCount 信息的数目
为该类加入成员函数,如表11-8 所示。
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·298·
表11-8 CClientSocket 类成员函数及其说明
成员函数 说明
CClientSocket(CChatServerDoc* m_pDoc) 构造函数
void Init() 对成员变量进行初始化
void SendMsg(CMsg* pMsg) 发送消息
void ReceiveMsg(CMsg* pMsg) 接收消息
virtual void OnReceive(int nErrorCode) 重载CSocket 的OnReceive()函数
在创建的过程中用到了类CMsg 和类CChatServerDoc,因此在文件“ClientSocket.h”的
开始声明两个类。
class CMsg;
class CChatServerDoc;
在“ClientSocket.cpp”文件的开始要把两个头文件包含进来。代码如下:
#include "msg.h"
#include "ChatServerDoc.h"
最后为各函数编写代码。构造函数代码如下:
CClientSocket::CClientSocket(CChatServerDoc* pDoc)
{
m_pDoc = pDoc;
m_nMsgCount = 0;
m_pFile = NULL;
m_pArchiveIn = NULL;
m_pArchiveOut = NULL;
}
析构函数代码如下:
CClientSocket::~CClientSocket()
{
if (m_pArchiveOut != NULL)
delete m_pArchiveOut;
if (m_pArchiveIn != NULL)
delete m_pArchiveIn;
if (m_pFile != NULL)
delete m_pFile;
}
初始化函数Init()代码如下:
void CClientSocket::Init()
{
m_pFile = new CSocketFile(this);
m_pArchiveIn = new CArchive(m_pFile,CArchive::load);
枫叶文学网www.fywxw.com
第11 章 网络编程
·299·
m_pArchiveOut = new CArchive(m_pFile,CArchive::store);
}
发送信息函数SendMsg()代码如下:
void CClientSocket::SendMsg(CMsg* pMsg)
{
if (m_pArchiveOut != NULL)
{
//数据由pMsg 输出到m_pArchiveOut
pMsg->Serialize(*m_pArchiveOut);
//将数据写入文件
m_pArchiveOut->Flush();
}
}
接收信息函数ReceiveMsg()代码如下:
void CClientSocket::ReceiveMsg(CMsg* pMsg)
{
//数据由m_pArchiveIn 读入到pMsg
pMsg->Serialize(*m_pArchiveIn);
}
重载的OnReceive()函数代码如下:
void CClientSocket::OnReceive(int nErrorCode)
{
//调用CSocekt 的OnReceive 函数
CSocket::OnReceive(nErrorCode);
//其余工作jiāo给文档类处理
m_pDoc->ProcessReceive(this);
}
其中,ProcessReceive()函数是CChatServerDoc 类的一个成员函数,在后面将会看到它的
具体实现。
(8)创建设定端口对话框
对服务器来说,它可以通过“设定端口号”对话框来设定服务端口,如图11-9 所示。其
中,对话框的ID 为IDD_SETUPDLG,Edit 控件的ID 为IDC_PORT。
图11-9 “设定端口号”对话框
枫叶文学网www.fywxw.com
Visual C++ 6.0 程序设计从入门到精通
·300·
打开“ClassWizard”对话框,为此对话框创建一个类,取名为CPortDlg。然后为Edit
控件增加变量int m_Port,在构造函数中将其初始化为2000。
最后,在菜单栏中新建一菜单项,当单击此菜单项时,就会弹出如图11-10 所示的对话
框。
图11-10 对话框所对应菜单栏
此菜单项的ID 为ID_SETPORT。
(9)实现CChatServerDoc 类
大量的工作都是在CChatServerDoc 类中实现的。在开始编写代码前,首先在文件
“ChatServerDoc.h”中CChatServerDoc 类定义的前面加入如下的类声明语句:
class CListeningSocket;
class CClientSocket;
class CMsg;
在文件“ChatServerDoc.cpp”的开头加入如下的include 语句:
#include "MainFrm.h"
#include"PortDlg.h"
#include "ChattersListView.h"
#include "ChatView.h"
#include "ListeningSocket.h"
#include "ClientSocket.h"
#include "Msg.h"
在文件“ChatServerDoc.cpp”的开头加入下面的定义语句(它定义了5 种不同类型的消
息)。
#define LEAVING_CHAT 1
#define SENDING_CHATTERS_LIST 2
#define SENDING_NICKNAME 3
#define NORMAL_MESSAGE 4
#define USED_NAME 5
这5 种类型消息的具体含义在下面的程序中会详细说明。
接下来,为CChatServerDoc 类添加一些成员变量,如表11-9 所示。
表11-9 CChatServerDoc 类成员变量及其说明
成员变量 说
松语文学免费小说阅读_www.16sy.com