使用CSocket类的网络通信实例
本例采用CSocket编程模型建立一个聊天程序的简单实例。使用vc++6建立一个基于对话框的工程,建立工程时注意选上“Windows套接字”复选框。
一、服务器端应用程序设计(工程名称SocSvr)
1.1、界面及各控件属性
界面如下:
本例采用CSocket编程模型建立一个聊天程序的简单实例。使用vc++6建立一个基于对话框的工程,建立工程时注意选上“Windows套接字”复选框。
一、服务器端应用程序设计(工程名称SocSvr)
1.1、界面及各控件属性
界面如下:
各控件属性如下
控件类型 控件ID Caption属性 控件变量 变量类型
列表框 IDC_LST_Log m_lst_log CListBox
编辑框 IDC_EDT_SenMsg m_edt_senMsg CEdit
按钮 IDC_BTN_Send m_btn_send CButton
控件类型 控件ID Caption属性 控件变量 变量类型
列表框 IDC_LST_Log m_lst_log CListBox
编辑框 IDC_EDT_SenMsg m_edt_senMsg CEdit
按钮 IDC_BTN_Send m_btn_send CButton
列表框控件IDC_LST _Log的属性“sort”值为false,属性“Horizontal Scroll”值为true。
按钮IDC_BTN_Send属性Disable值为true。
1.2、定义CSocket类的派生类CSocListen和CSocRecv
从CSocket编程模型知道,服务器端需要两种套接字,一个用来侦听连接请求,一个用来与请求连接的套接字建立连接。因此,为程序添加两个CSocket派生类:SSocListen和CSocRecv,它们与对话框类密切配合,共同完成程序所要求实现的功能。
在vc集成环境左侧的工程管理栏中,切换到“class view”页,对准“SocSvr Classes”点右键,在弹出的菜单中点击“new class”,弹出“new class”对话框。在此对话框的“Class type”组合框中选择“MFC Class”,在“Base class” 组合框中选择“CSocket”,然后再在“Name”文本框中填入类的名称“CSocListen”,点击“OK”按钮。这样,一个CSocket类的派生类“CSocListen”就创建好了。
用同样的方法创建CSocket类的另一个派生类“CSocRecv”。创建过程的对话框如下图所示。
按钮IDC_BTN_Send属性Disable值为true。
1.2、定义CSocket类的派生类CSocListen和CSocRecv
从CSocket编程模型知道,服务器端需要两种套接字,一个用来侦听连接请求,一个用来与请求连接的套接字建立连接。因此,为程序添加两个CSocket派生类:SSocListen和CSocRecv,它们与对话框类密切配合,共同完成程序所要求实现的功能。
在vc集成环境左侧的工程管理栏中,切换到“class view”页,对准“SocSvr Classes”点右键,在弹出的菜单中点击“new class”,弹出“new class”对话框。在此对话框的“Class type”组合框中选择“MFC Class”,在“Base class” 组合框中选择“CSocket”,然后再在“Name”文本框中填入类的名称“CSocListen”,点击“OK”按钮。这样,一个CSocket类的派生类“CSocListen”就创建好了。
用同样的方法创建CSocket类的另一个派生类“CSocRecv”。创建过程的对话框如下图所示。
1.3、建立套接字与对话框类的关联
在程序中,对话框类要用到套接字类,而套接字类在响应某些消息,如在函数OnAccept、OnReceive中进行处理时,也要改变对话框的某些控件状态,以反映给用户这些事情的发生。
这里存在着两个类相互使用的情况,把套接字类对象定义成对话框类的成员变量,同时在套接字类中也把对话框类定义为成员变量。如何实现这样的用法呢?在对话框类实现文件(cpp文件)中加入套接字头文件的声明,然后在套接字类实现文件(cpp文件)中加入对话框类头文件的声明,然后在对话框类的头文件里把套接字的指针变量定义为对话框的成员变量,再在套接字类的头文件里把对话框的指针变量定义为对该套接字的成员变量。
具体做法应该如下:
首先,在SocSvrDlg.h文件中加入套接字类头文件的声明,语句#pragma once的后面加入如下语句:
class CSocListen; // 声明侦听类
class CSocRecv; // 声明接收客户端消息类
然后在该文件中为CSocSvrDlg类增加两个公有成员变量,语句如下:
CSocListen *m_pCSocListen; // 定义一个侦听类的指针
CSocRecv *m_pCSocRecv; // 定义一个接收客户端消息类的指针
再在SocSvrDlg.cpp文件中把两个套接字类的头文件包含进来:
#include "SocListen.h" // 侦听类,包含进来
#include "SocRecv.h" // 接收客户端消息类,包含进来
这样在对话框类中就可以使用套接字类了。
接下来在套接字类中加入对话框类信息。
首先,在SocListen.h文件的开头,语句#pragma once的后面加入如下语句:
class CSocSvrDlg; // 声明对话框类
然后,在该文件中为CSocListen类添加一个公有成员变量和一个构造函数:
CSocListen(CSocSvrDlg *pDlgSocSvr);
CSocSvrDlg *m_pDlgSocSvr;
接着在SocListen.cpp文件中添加新的构造函数的实现,并添加一条关于SocSvrDlg.h文件的预编译声明,代码如下:
#include "SocSvrDlg.h" // 对话框类,包含进来
CSocListen::CSocListen(CSocSvrDlg * pDlgSocSvr)
{
m_pDlgSocSvr = pDlgSocSvr; // 初始化对话框指针
}
这样,在套接字类中也可以通过成员变量使用对话框了。
使用同样的方法对CSocRecv类进行设置,使其也可以通过成员变量使用对话框。
1.4、为套接字添加串行化读写信息的功能
在服务器端的两个套接字中,只有CSocRecv套接字是真正与客户端套接字建立连接,发送与接收数据的,因此,我们只为该类添加串行化读写信息功能。在SocRecv.h文件中为类CSocRecv添加三个公有成员变量。
CsocketFile *m_pCSocFile;
Carchive *m_pCArcIn;
Carchive *m_pCArcOut;
1.5、在对话框中初始化套接字并侦听连接请求
在OnInitDialog函数中添加如下代码:
在程序中,对话框类要用到套接字类,而套接字类在响应某些消息,如在函数OnAccept、OnReceive中进行处理时,也要改变对话框的某些控件状态,以反映给用户这些事情的发生。
这里存在着两个类相互使用的情况,把套接字类对象定义成对话框类的成员变量,同时在套接字类中也把对话框类定义为成员变量。如何实现这样的用法呢?在对话框类实现文件(cpp文件)中加入套接字头文件的声明,然后在套接字类实现文件(cpp文件)中加入对话框类头文件的声明,然后在对话框类的头文件里把套接字的指针变量定义为对话框的成员变量,再在套接字类的头文件里把对话框的指针变量定义为对该套接字的成员变量。
具体做法应该如下:
首先,在SocSvrDlg.h文件中加入套接字类头文件的声明,语句#pragma once的后面加入如下语句:
class CSocListen; // 声明侦听类
class CSocRecv; // 声明接收客户端消息类
然后在该文件中为CSocSvrDlg类增加两个公有成员变量,语句如下:
CSocListen *m_pCSocListen; // 定义一个侦听类的指针
CSocRecv *m_pCSocRecv; // 定义一个接收客户端消息类的指针
再在SocSvrDlg.cpp文件中把两个套接字类的头文件包含进来:
#include "SocListen.h" // 侦听类,包含进来
#include "SocRecv.h" // 接收客户端消息类,包含进来
这样在对话框类中就可以使用套接字类了。
接下来在套接字类中加入对话框类信息。
首先,在SocListen.h文件的开头,语句#pragma once的后面加入如下语句:
class CSocSvrDlg; // 声明对话框类
然后,在该文件中为CSocListen类添加一个公有成员变量和一个构造函数:
CSocListen(CSocSvrDlg *pDlgSocSvr);
CSocSvrDlg *m_pDlgSocSvr;
接着在SocListen.cpp文件中添加新的构造函数的实现,并添加一条关于SocSvrDlg.h文件的预编译声明,代码如下:
#include "SocSvrDlg.h" // 对话框类,包含进来
CSocListen::CSocListen(CSocSvrDlg * pDlgSocSvr)
{
m_pDlgSocSvr = pDlgSocSvr; // 初始化对话框指针
}
这样,在套接字类中也可以通过成员变量使用对话框了。
使用同样的方法对CSocRecv类进行设置,使其也可以通过成员变量使用对话框。
1.4、为套接字添加串行化读写信息的功能
在服务器端的两个套接字中,只有CSocRecv套接字是真正与客户端套接字建立连接,发送与接收数据的,因此,我们只为该类添加串行化读写信息功能。在SocRecv.h文件中为类CSocRecv添加三个公有成员变量。
CsocketFile *m_pCSocFile;
Carchive *m_pCArcIn;
Carchive *m_pCArcOut;
1.5、在对话框中初始化套接字并侦听连接请求
在OnInitDialog函数中添加如下代码:
if(m_pCSocListen=new CSocListen(this))
{
if(m_pCSocListen->Create(1000)) // 创建端口号
{
m_lst_log.AddString("等待连接……");
m_pCSocListen->Listen();
}
else
{
m_lst_log.AddString("初始化失败,请重新启动程序");
delete m_pCSocListen; // 清除侦听socket所占内存
}
}
else
{
m_lst_log.AddString("初始化失败,请重新启动程序");
}
{
if(m_pCSocListen->Create(1000)) // 创建端口号
{
m_lst_log.AddString("等待连接……");
m_pCSocListen->Listen();
}
else
{
m_lst_log.AddString("初始化失败,请重新启动程序");
delete m_pCSocListen; // 清除侦听socket所占内存
}
}
else
{
m_lst_log.AddString("初始化失败,请重新启动程序");
}
上述代码主要是创建并初始化ServSock套接字,并开始侦听连接请求。
1.6、接受连接请求
由于是CSocListen类的*m_pCSocListen对象在侦听连接请求,因此由该类来接受连接请求。
首先,在SocListen.h文件中加入如下语句:
#iinclude “SocRecv.h”
然后,重载该类的OnAccept函数,在该函数中添加如下代码:
CSocRecv *pCSocRecv; // 接收客户端消息类指针
1.6、接受连接请求
由于是CSocListen类的*m_pCSocListen对象在侦听连接请求,因此由该类来接受连接请求。
首先,在SocListen.h文件中加入如下语句:
#iinclude “SocRecv.h”
然后,重载该类的OnAccept函数,在该函数中添加如下代码:
CSocRecv *pCSocRecv; // 接收客户端消息类指针
if(pCSocRecv=new CSocRecv(this->m_pDlgSocSvr))
{
if(Accept(*pCSocRecv))
{
pCSocRecv->m_pCSocFile = new CSocketFile(pCSocRecv);
pCSocRecv->m_pCArcIn = new CArchive(pCSocRecv->m_pCSocFile,CArchive::load);
pCSocRecv->m_pCArcOut= new CArchive(pCSocRecv->m_pCSocFile,CArchive::store);
m_pDlgSocSvr->m_pCSocRecv = pCSocRecv;
pCSocRecv = NULL;
m_pDlgSocSvr->m_lst_log.AddString("连接成功,可以开始传递消息咯!");
m_pDlgSocSvr->m_btn_send.EnableWindow(TRUE);
}
else
{
m_pDlgSocSvr->m_lst_log.AddString("试图连接客户端失败!");
delete pCSocRecv;
}
}
else
{
m_pDlgSocSvr->m_lst_log.AddString("连接套接字初始化失败!");
}
上述代码首先调用Accept函数接受连接请求,然后为该连接创建一个CSocRecv类型的套接字,并为该套接字关联CArchive对象,使其能实现串行化传输信息的功能。最后把关联好的套接字传回给对话框对象供其使用。这样,对话框对象的成员变量RecvSock套接字便与客户端套接字之间建立了一条信息通道,信息将在两个套接字之间传递。
1.7、接收信息
连接建立成功后,当有信息到达服务器端时,就会引发SocRecv套接字对象的OnReceive函数,因此需要重载CSocRecv类的OnReceive函数。添加代码如下:
CString csRecMsg; // 接收到的信息
{
if(Accept(*pCSocRecv))
{
pCSocRecv->m_pCSocFile = new CSocketFile(pCSocRecv);
pCSocRecv->m_pCArcIn = new CArchive(pCSocRecv->m_pCSocFile,CArchive::load);
pCSocRecv->m_pCArcOut= new CArchive(pCSocRecv->m_pCSocFile,CArchive::store);
m_pDlgSocSvr->m_pCSocRecv = pCSocRecv;
pCSocRecv = NULL;
m_pDlgSocSvr->m_lst_log.AddString("连接成功,可以开始传递消息咯!");
m_pDlgSocSvr->m_btn_send.EnableWindow(TRUE);
}
else
{
m_pDlgSocSvr->m_lst_log.AddString("试图连接客户端失败!");
delete pCSocRecv;
}
}
else
{
m_pDlgSocSvr->m_lst_log.AddString("连接套接字初始化失败!");
}
上述代码首先调用Accept函数接受连接请求,然后为该连接创建一个CSocRecv类型的套接字,并为该套接字关联CArchive对象,使其能实现串行化传输信息的功能。最后把关联好的套接字传回给对话框对象供其使用。这样,对话框对象的成员变量RecvSock套接字便与客户端套接字之间建立了一条信息通道,信息将在两个套接字之间传递。
1.7、接收信息
连接建立成功后,当有信息到达服务器端时,就会引发SocRecv套接字对象的OnReceive函数,因此需要重载CSocRecv类的OnReceive函数。添加代码如下:
CString csRecMsg; // 接收到的信息
(*m_pCArcIn)>>csRecMsg; // 接收信息
m_pDlgSocSvr->m_lst_log.AddString("客户端发来的信息如下:");
m_pDlgSocSvr->m_lst_log.AddString(csRecMsg); // 添加到列表框
m_pDlgSocSvr->m_lst_log.AddString("客户端发来的信息如下:");
m_pDlgSocSvr->m_lst_log.AddString(csRecMsg); // 添加到列表框
// 选中列表框中刚刚收到的消息
m_pDlgSocSvr->m_lst_log.SetCurSel(m_pDlgSocSvr->m_lst_log.GetCount()-1);
1.8、发送信息
为对话框“发送”按钮添加事件处理函数OnBTNSend(),代码如下:
void CSocSvrDlg::OnBTNSend()
{
// TODO: Add your control notification handler code here
CString csSenMsg; // 要发送的消息
m_pDlgSocSvr->m_lst_log.SetCurSel(m_pDlgSocSvr->m_lst_log.GetCount()-1);
1.8、发送信息
为对话框“发送”按钮添加事件处理函数OnBTNSend(),代码如下:
void CSocSvrDlg::OnBTNSend()
{
// TODO: Add your control notification handler code here
CString csSenMsg; // 要发送的消息
m_edt_senMsg.GetWindowText(csSenMsg); // 获得编辑框内容
if(csSenMsg=="")
{
AfxMessageBox("空字符串,不能发送!");
return;
}
if(csSenMsg=="")
{
AfxMessageBox("空字符串,不能发送!");
return;
}
m_lst_log.AddString("你发出的信息如下:");
m_lst_log.AddString(csSenMsg);
m_lst_log.SetCurSel(m_lst_log.GetCount()-1);
m_lst_log.AddString(csSenMsg);
m_lst_log.SetCurSel(m_lst_log.GetCount()-1);
*(m_pCSocRecv->m_pCArcOut)<<csSenMsg;
m_pCSocRecv->m_pCArcOut->Flush();
}
m_pCSocRecv->m_pCArcOut->Flush();
}
二、客户端应用程序设计(工程名称SocClt)
2.1、界面及各控件属性
界面如下:
2.1、界面及各控件属性
界面如下:
各控件属性如下
控件类型 控件ID Caption属性 控件变量 变量类型
列表框 IDC_LST_Log m_lst_log CListBox
编辑框 IDC_EDT_SenMsg m_edt_senMsg CEdit
按钮 IDC_BTN_Send m_btn_send CButton
控件类型 控件ID Caption属性 控件变量 变量类型
列表框 IDC_LST_Log m_lst_log CListBox
编辑框 IDC_EDT_SenMsg m_edt_senMsg CEdit
按钮 IDC_BTN_Send m_btn_send CButton
列表框控件IDC_LST _Log的属性“sort”值为false,属性“Horizontal Scroll”值为true。
按钮IDC_BTN_Send属性Disable值为true。
2.2、创建套接字类(从CSocket类派生)
客户端只需要一个套接字,命名为CSocSend。
2.3、建立对话框类与套接字类的关联
首先,在SocCltDlg.h文件的开头,语句#pragma once后面加入如下语句:
class CSocSend; // 声明socket类
然后,在该文件中为CSocCltDlg类添加一个公有成员变量,语句如下:
CSocSend *m_pCSocSend; // 定义socket类指针
再在CsocSend中添加对话框类的成员变量。
首先,在SocSend.h文件的开头,语句#pragma once后面加入如下语句:
class CSocCltDlg; // 声明对话框类
然后,在该文件中为CSocSend类添加一公有成员变量和一个构造函数,语句如下:
CSocSend(CSocCltDlg *pDlgSocClt);
CSocCltDlg *m_pDlgSocClt; // 定义一个对话框类的指针
接着,在SocSend.cpp文件中添加新的构造函数的实现代码,并添加一条关于CSocCltDlg.h文件的预编译声明,代码如下:
#include "SocCltDlg.h" // 对话框类,包含进来
CSocSend::CSocSend(CSocCltDlg *pDlgSocClt)
{
m_pDlgSocClt = pDlgSocClt;
}
这样,便完成了对话框和套接字之间的连接了。
2.4、为套接字添加串行化读写信息的功能
在SocSend.h文件中,为类CSocSend添加三个公有成员变量,代码如下:
CSocketFile *m_pCSocFile;
CArchive *m_pCArcIn;
CArchive *m_pCArcOut;
2.5、在对话框中初始化套接字并建立连接
在对话框类的OnInitDialog函数中添加如下代码
m_lst_log.AddString("正在连接……");
if(m_pCSocSend=new CSocSend(this))
{
if(m_pCSocSend->Create()) // 创建套接字
{
//if(m_pCSocSend->Connect("localhost",1000)) // 服务器名称、端口号
if(m_pCSocSend->Connect("cy-sys",1000)) // 服务器名称、端口号
{
m_pCSocSend->m_pCSocFile = new CSocketFile(m_pCSocSend);
m_pCSocSend->m_pCArcIn = new CArchive(m_pCSocSend->m_pCSocFile,
CArchive::load);
m_pCSocSend->m_pCArcOut = new CArchive(m_pCSocSend->m_pCSocFile,
CArchive::store);
m_lst_log.AddString("连接成功,可以开始传递消息咯!");
m_btn_send.EnableWindow(TRUE);
}
else
{
m_lst_log.AddString("连接不成功!");
delete m_pCSocSend; // 清除socket所占内存
}
}
else
{
m_lst_log.AddString("初始化失败,请重新启动程序");
delete m_pCSocSend; // 清除socket所占内存
}
}
else
{
m_lst_log.AddString("初始化失败,请重新启动程序");
}
2.6、接收消息
消息到来时,会引发套接字的OnReceive消息,因此要重载CSocSend类的OnReceive函数,在其中添加代码如下:
CString csRecMsg;
按钮IDC_BTN_Send属性Disable值为true。
2.2、创建套接字类(从CSocket类派生)
客户端只需要一个套接字,命名为CSocSend。
2.3、建立对话框类与套接字类的关联
首先,在SocCltDlg.h文件的开头,语句#pragma once后面加入如下语句:
class CSocSend; // 声明socket类
然后,在该文件中为CSocCltDlg类添加一个公有成员变量,语句如下:
CSocSend *m_pCSocSend; // 定义socket类指针
再在CsocSend中添加对话框类的成员变量。
首先,在SocSend.h文件的开头,语句#pragma once后面加入如下语句:
class CSocCltDlg; // 声明对话框类
然后,在该文件中为CSocSend类添加一公有成员变量和一个构造函数,语句如下:
CSocSend(CSocCltDlg *pDlgSocClt);
CSocCltDlg *m_pDlgSocClt; // 定义一个对话框类的指针
接着,在SocSend.cpp文件中添加新的构造函数的实现代码,并添加一条关于CSocCltDlg.h文件的预编译声明,代码如下:
#include "SocCltDlg.h" // 对话框类,包含进来
CSocSend::CSocSend(CSocCltDlg *pDlgSocClt)
{
m_pDlgSocClt = pDlgSocClt;
}
这样,便完成了对话框和套接字之间的连接了。
2.4、为套接字添加串行化读写信息的功能
在SocSend.h文件中,为类CSocSend添加三个公有成员变量,代码如下:
CSocketFile *m_pCSocFile;
CArchive *m_pCArcIn;
CArchive *m_pCArcOut;
2.5、在对话框中初始化套接字并建立连接
在对话框类的OnInitDialog函数中添加如下代码
m_lst_log.AddString("正在连接……");
if(m_pCSocSend=new CSocSend(this))
{
if(m_pCSocSend->Create()) // 创建套接字
{
//if(m_pCSocSend->Connect("localhost",1000)) // 服务器名称、端口号
if(m_pCSocSend->Connect("cy-sys",1000)) // 服务器名称、端口号
{
m_pCSocSend->m_pCSocFile = new CSocketFile(m_pCSocSend);
m_pCSocSend->m_pCArcIn = new CArchive(m_pCSocSend->m_pCSocFile,
CArchive::load);
m_pCSocSend->m_pCArcOut = new CArchive(m_pCSocSend->m_pCSocFile,
CArchive::store);
m_lst_log.AddString("连接成功,可以开始传递消息咯!");
m_btn_send.EnableWindow(TRUE);
}
else
{
m_lst_log.AddString("连接不成功!");
delete m_pCSocSend; // 清除socket所占内存
}
}
else
{
m_lst_log.AddString("初始化失败,请重新启动程序");
delete m_pCSocSend; // 清除socket所占内存
}
}
else
{
m_lst_log.AddString("初始化失败,请重新启动程序");
}
2.6、接收消息
消息到来时,会引发套接字的OnReceive消息,因此要重载CSocSend类的OnReceive函数,在其中添加代码如下:
CString csRecMsg;
*m_pCArcIn>>csRecMsg; // 获得服务器端传来的消息
m_pDlgSocClt->m_lst_log.AddString("服务器端发来的消息如下:");
m_pDlgSocClt->m_lst_log.AddString("服务器端发来的消息如下:");
// 在列表框中添加服务器端发来的消息
m_pDlgSocClt->m_lst_log.AddString(csRecMsg);
m_pDlgSocClt->m_lst_log.AddString(csRecMsg);
// 使刚刚收到的消息高亮显示
m_pDlgSocClt->m_lst_log.SetCurSel(m_pDlgSocClt->m_lst_log.GetCount()-1);
2.7、发送信息
为对话框“发送”按钮添加事件处理函数OnBTNSend(),代码如下:
void CSocCltDlg::OnBTNSend()
{
// TODO: Add your control notification handler code here
CString csSenMsg;
m_pDlgSocClt->m_lst_log.SetCurSel(m_pDlgSocClt->m_lst_log.GetCount()-1);
2.7、发送信息
为对话框“发送”按钮添加事件处理函数OnBTNSend(),代码如下:
void CSocCltDlg::OnBTNSend()
{
// TODO: Add your control notification handler code here
CString csSenMsg;
m_edt_senMsg.GetWindowText(csSenMsg);
if(csSenMsg=="")
{
AfxMessageBox("空字符串,不能发送!");
return;
}
if(csSenMsg=="")
{
AfxMessageBox("空字符串,不能发送!");
return;
}
m_lst_log.AddString("您发送的信息如下:");
m_lst_log.AddString(csSenMsg);
m_lst_log.SetCurSel(m_lst_log.GetCount()-1);
*(m_pCSocSend->m_pCArcOut)<<csSenMsg; // 向socket发送字符串
m_pCSocSend->m_pCArcOut->Flush();
}
三、运行工程
在主机名称为“cy-sys”的机器上运行SocSvr(服务器端)工程,在另一台机器上运行SocClt(客户端)工程(注意,一定要先启动服务器端工程,再启动客户端工程),出现“连接成功”字样后,就可以互相发送消息了,效果图如下所示。

服务器端界面
m_lst_log.AddString(csSenMsg);
m_lst_log.SetCurSel(m_lst_log.GetCount()-1);
*(m_pCSocSend->m_pCArcOut)<<csSenMsg; // 向socket发送字符串
m_pCSocSend->m_pCArcOut->Flush();
}
三、运行工程
在主机名称为“cy-sys”的机器上运行SocSvr(服务器端)工程,在另一台机器上运行SocClt(客户端)工程(注意,一定要先启动服务器端工程,再启动客户端工程),出现“连接成功”字样后,就可以互相发送消息了,效果图如下所示。
服务器端界面
客户端界面