友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
VC语言6.0程序设计从入门到精通-第44部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
OnMessagePending 当等待完成一个阻塞调用时调用此函数来处理未处理的消息
·287 ·
…………………………………………………………Page 299……………………………………………………………
Visual C++ 6。0 程序设计从入门到精通
11。4 网络编程的实现
前面介绍了很多关于 Windows 网络编程的知识,也详细介绍了 WinSock 类。接下来,本
节将按照上小节所说的编程步骤,利用 CSocket 类编写一个网络聊天程序。相信通过这个程
序,读者会对 CSocket 的基本应用有更加深入的认识。
实例 11…1:网络聊天程序实例。源代码在光盘中“11实例 11…1”目录下。
11。4。1 程序设计的基本任务
为了让读者易于理解而又能体现出网络编程的一般步骤,本程序抛弃很多网络应用程序
的辅助功能,只实现网络聊天系统的基本功能。该系统包括服务器和客户端两部分,服务器
允许多个客户端连接,连接到服务器的客户端可以聊天,聊天的内容会在服务器和每个客户
端显示出来,同时,在服务器上还可以显示出每个客户端的基本信息,比如登录时间和 IP 等。
11。4。2 服务器端程序设计
1.设计概述
在进行服务器端程序设计之前,首先要考虑一下消息在程序中如何处理。在整个系统中,
服务器和客户端的交互都是通过消息完成的,并且对于一般的网络应用程序来说,网络数据
的有效封装有利于用户对网络通信的管理,也有利于在源程序的基础上作进一步的开发。因
此在本程序中,会对消息进行简单的封装。
接下来考虑界面设计 。服务器端应该有两个窗口,分别显示各客户端的详细信息(如用
户名、登录时间、IP 等)和用户的聊天信息。
关于程序流程在 11。3。2 节的第 2 小节已有说明。除此之外,在网络编程中还有很重要的
一点,就是服务器和客户端双方要约定好信息的格式以及发送顺序,只有约定好这些,双方
才知道对方所发信息的意义。其实这就是通常所说的协议的一个重要部分,在网络应用程序
的设计中,协议是必不可少的,越是大型的网络应用程序,协议就越复杂。对于本例这个很
小的程序,可以根据消息的类型(封装在消息类中)对消息进行分类,并且约定当用户登录
或离开时,都要先发送用户名给服务器,然后再发送正式信息。这样通过消息的种类,服务
器就能够知道当前消息是一般消息还是用户登录或离开的消息。如果是前者,就将其发送给
每个客户端,并且在服务器界面将信息显示出来;如果是后者,则将登录或离开的用户名发
送给各客户端,以使各客户端对用户列表更新,同时更新服务器端的用户列表。
最后,本程序采用文档…视图结构,视图负责显示,而数据的处理则通过文档类来实现。
2 .编写代码
(1)创建工程
新建一个 MFC 工程,取名为 ChatServer 。利用 AppWizard 创建 SDI 程序,在第一步中
·288 ·
…………………………………………………………Page 300……………………………………………………………
第 11 章 网络编程
选择“Single Document”项,在第四步中选中“Windows Sockets ”复选框,如图 11…2 所示。
在最后一步的“Base Class ”下拉列表中选择“ClistView ”作为基类,其他选项保持默认,单
击“Finish ”按钮即可完成工程的创建,如图 11…3 所示。
图 11…2 选中“Windows Sockets ”复选框 图 11…3 选择“ClistView ”作为基类
(2 )封装消息类
本程序中为消息建立一个类 CMsg,它的父类是 CObject 。在“ChatServer classes ”上单
击右键,在弹出的对话框中选择“New Class ”,如图 11…4 所示。在弹出的“New Class ”对话
框中的“Class type ”下拉列表中选择“Generic Class ”项,在“Name ”栏中填写类名称 CMsg,
在“Derived From ”栏中填写其父类名称 CObject,如图 11…5 所示。单击“OK ”按钮,弹出
提示对话框,单击“确定”即可。
图 11…4 为工程新建一个类 图 11…5 新建消息类 CMsg
在类CMsg 的定义(文件“Msg。h ”)中加入宏DECLARE_DYNCREATE(CMsg) ,声明此
宏可以使得由CObject继承来的类在程序运行的时候能够动态地创建。然后为其加入成员变量
和成员函数,分别如表11…5和表11…6所示。
·289 ·
…………………………………………………………Page 301……………………………………………………………
Visual C++ 6。0 程序设计从入门到精通
表 11…5 CMsg 类成员变量及其说明
成员变量 说明
CString m_strText 消息的内容
BOOL m_bClose 连接是否关闭
UINT code 消息的种类
表 11…6 CMsg 类成员函数及其说明
成员函数 说明
void Init() 对成员变量进行初始化
virtual void Serialize(CArchive &ar) 进行数据的发送和接收
完成消息类的定义后,要在文件“Msg。cpp ”中实现此类。首先要声明另外一个宏
IMPLEMENT_DYNCREATE(CMsg; CObject) ,它是与宏 DECLARE_DYNCREATE 相对应的
在类实现时声明的宏。然后为其添加代码。在类的构造函数中对成员变量进行初始化,代码
如下:
CMsg::CMsg()
{
Init();
}
函数 Init()代码如下:
void CMsg::Init()
{
code = 0;
m_bClose = FALSE;
m_strText = _T(〃〃);
}
最后是重载的串行化函数的代码如下:
void CMsg::Serialize(CArchive& ar)
{
//读入
if (ar。IsStoring())
{
ar 》 m_strText;
·290 ·
…………………………………………………………Page 302……………………………………………………………
第 11 章 网络编程
ar 》》 (UINT)code;
}
}
(3 )编写客户端信息界面
封装好消息类后,接下来要对界面编写代码。在上小节的设计中已经说明,服务器端应
有两个窗口,一个用来显示各客户端的信息,另一个用来显示聊天信息。
首先编写显示各客户端信息的界面。与创建消息类的步骤一样,为工程新建一个类,取
名为 CChattersListView,在“Class type ”下拉列表中选择“MFC Class ”项,在“Base class ”
下拉列表中选择“ClistView ”作为其父类,设置完成后单击“OK ”按钮即可,如图 11…6 所
示。
图 11…6 新建类 CChattersListView
然后为该类添加成员变量 int m_Row,它记录列表视图的当前行的位置,在类的构造函
数中将其初始化为 0 。
接 下 来 添 加 各 成 员 函 数 并 为 其 编 写 代 码 。 首 先 重 载 函 数 virtual BOOL
PreCreateWindow(CREATESTRUCT& cs) ,通过此函数可以设定窗口风格。代码如下:
BOOL CChattersListView::PreCreateWindow(CREATESTRUCT& cs)
{
//设定风格为详细报表显示
cs。style |= LVS_REPORT;
return CListView::PreCreateWindow(cs);
}
接着在窗口创建时进行初始化。打开“ClassWizard ”对话框,在“Class name ”下拉列
表中选择 CChattersListView,在“Message ”列表中选择 WM_CREATE (如图11…7 所示)。
然后依次单击“Add Function ”按钮和“Edit Code ”按钮,定位到 OnCreate()函数,添加如下
代码:
·291 ·
…………………………………………………………Page 303……………………………………………………………
Visual C++ 6。0 程序设计从入门到精通
图 11…7 为 WM_CREATE 消息添加响应函数
int CChattersListView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CListView::OnCreate(lpCreateStruct) == …1)
return …1;
// TODO: Add your specialized creation code here
CRect rect;
GetClientRect(&rect);
GetListCtrl()。InsertColumn(0; 〃用户名〃 );
GetListCtrl()。InsertColumn(1; 〃登录时间〃);
GetListCtrl()。InsertColumn(2; 〃地址〃);
return 0;
}
当窗口大小变化时,会激发 WM_SIZE 消息。利用上述办法可以为此消息编写响应函数,
代码如下:
void CChattersListView::OnSize(UINT nType; int cx; int cy)
{
CListView::OnSize(nType; cx; cy);
// TODO: Add your message handler code here
GetListCtrl()。SetColumnWidth(0; cx/3);
GetListCtrl()。SetColumnWidth(1; cx/3);
GetListCtrl()。SetColumnWidth(2; cx/3);
}
当有新用户加入或者有用户退出时,应该更新列表,为此需要两个更新函数,分别用于
添加用户和删除用户的操作。
首先为该类添加一个添加用户信息的函数:
·292 ·
…………………………………………………………Page 304……………………………………………………………
第 11 章 网络编程
void AddChatter(CString Name ; CString IPAddress; UINT Port);
各参数意义如下。
o Name :用户名。
o IPAddress :客户端 IP 地址。
o Port :客户端的端口号。
为此函数编写代码如下:
void CChattersListView::AddChatter(CString Name; CString IPAddress; UINT Port)
{
//插入用户名
LVITEM lvi;
lvi。mask = LVIF_TEXT;
lvi。iItem = m_Row++;
lvi。iSubItem = 0;
lvi。pszText = Name。GetBuffer(Name。GetLength());
int iActualItem = GetListCtrl()。InsertItem(&lvi);
//插入登录时间
//得到系统时间
time_t t;
struct tm *ptm;
time(&t);
ptm = localtime(&t);
//将其转化成字符形式
CString sDate;
sDate。Format(〃%。19s〃; asctime(ptm));
//得到小时、分钟、秒
int iHours ; iMins ; iSecs;
iHours = atoi(sDate。Mid(11;2));
iMins =atoi(sDate。Mid(14;2));
iSecs = atoi(sDate。Mid(17;2));
//将其转换成字符串
CString sHours; sMins; sSecs;
sHours。Format(〃%d:〃; iHours);
if(iHours 《 10)
sHours。Insert(0; ’0’);
sMins。Format(〃%d:〃; iMins);
if(iMins 《 10)
sMins。Insert(0; ’0’);
sSecs。Format(〃%d〃; iSecs);
if(iSecs 《 10)
·293 ·
…………………………………………………………Page 305……………………………………………………………
Visual C++ 6。0 程序设计从入门到精通
sSecs。Insert(0; ’0’);
CString sDisplayTime;
sDisplayTime = sHours+sMins+sSecs;
lvi。mask = LVIF_TEXT;
lvi。iItem = iActualItem;
lvi。iSubItem = 1;
lvi。pszText = sDisplayTime。GetBuffer(sDisplayTime。GetLength());
BOOL test = GetListCtrl()。SetItem(&lvi);
//插入客户端地址
CString sIPPort;
sIPPort。Format(〃%s:%d〃; IPAddress; Port);
lvi。mask = LVIF_TEXT;
lvi。iSubItem = 2;
lvi。pszText = sIPPort。GetBuffer(sIPPort。GetLength());
GetListCtrl()。SetItem(&lvi);
}
添加一个在列表中删除某用户的操作,函数名为 void DeleteChatter(CString Name) ,其中
Name 就是用户名。代码如下:
void CChattersListView::DeleteChatter(CString Name)
{
LVFINDINFO lvfi;
lvfi。flags = LVFI_STRING;
lvfi。psz = Name。GetBuffer(Name。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;
·294 ·
…………………………………………………………Page 306……………………………………………………………
第 11 章 网络编程
// 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 your message 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(〃rn〃);
int len = m_EditBox。GetWindowTextLength();
m_EditBox。SetSel(len;len);
m_EditBox。ReplaceSel(strTemp);
}
至此,负责显示客户端信息以及聊天信息的两个窗口都已编写完毕,接下来要在主框架
中进行分割,也就是将主框架分割成显示客户端信息的窗口和显示聊天信息的窗口两部分。
(5 )分割框架
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!