使用MFC快速实现网络编程 CAsyncSock

本文通过客户端和服务端的示例,介绍了使用CAsyncSocket类进行Socket编程的具体步骤。客户端实现连接、发送和接收数据的功能,服务端则实现监听、接受连接和回显数据的功能。

 

随着计算机网络化的深入,计算机网络编程在程序设计的过程中变得日益重要。由于  C++语言对底层操作的优越性,许多文章都曾经介绍过用  VC++进行  Socket  编程的方法。但由于都是直接利用动态连接库  wsock32.dll进行操作,实现比较繁琐。其实,VC++  MFC  类库中提供了  CAsyncSocket  这样一个套接字类,用他来实现  Socket  编程,是非常方便的。

---- 本文将用一个  Echo  例程来介绍  CAsyncSocket  类的用法。

---- 一. 客户端

---- 1 创建一个  Dialog Based  项目:CSockClient

---- 2 设计对话框

---- 去掉  Ok    Cancle  两个按钮,增加  ID_Connect(连接)、ID_Send(发送)、ID_Exit(关闭)按钮,增加  ListBox  控件  IDC_LISTMSG    Edit  控件  IDC_EDITMSG,并按下表在  ClassWizard  中为  CCSockClientDlg  类添加变量。

Control ID
Type Member
IDC_EDITMSG
CEdit m_MSGIDC_LISTMSG
ClistBox
m_MSGS

---- 3 CAsyncSocket  类用  DoCallBack  函数处理  MFC  消息,当一个网络事件发生时,DoCallBack  函数按网络事件类型:FD_READFD_WRITEFD_ACCEPTFD_CONNECT  分别调用  OnReceiveOnSendOnAcceptOnConnect  函数。由于  MFC  把这些事件处理函数定义为虚函数,所以要生成一个新的  C++类,以重载这些函数,做法如下:

----   Public  方式继承  CAsyncSocket  类,生成新类  MySock

----   MySock  类添加虚函数  OnReceiveOnConnectOnSend

---- 4   MySock.ccp  中添加以下代码 #include "CSockClient.h"#include "CSockClientDlg.h"

---- 5   MySock.h  中添加以下代码  public: BOOL m_bConnected;
//
是否连接  UINT m_nLength;
//
消息长度  char m_szBuffer[4096];
//
消息缓冲区

---- 6   MySock.ccp  中重载各函数  MySock::MySock(){ m_nLength=0;
memset(m_szBuffer,0,sizeof(m_szBuffer));
m_bConnected=FALSE;}MySock::~MySock(){
//
关闭套接字  if(m_hSocket!=INVALID_SOCKET)
Close();}
void MySock::OnReceive(int nErrorCode) {
m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0);
//
下面两行代码用来获取对话框指针  CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp();
CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd;
pDlg- >m_MSGS.InsertString(0,m_szBuffer); memset(m_szBuffer,0,sizeof(m_szBuffer));
CAsyncSocket::OnReceive(nErrorCode);}void MySock::OnSend(int nErrorCode) {
Send(m_szBuffer,m_nLength,0); m_nLength=0;
memset(m_szBuffer,0,sizeof(m_szBuffer));
//
继续提请一个的网络事件,接收  Server  消息  AsyncSelect(FD_READ);
CAsyncSocket::OnSend(nErrorCode);}void MySock::OnConnect(int nErrorCode) {
if (nErrorCode==0) {
m_bConnected=TRUE;
CCSockClientApp* pApp=(CCSockClientApp*)AfxGetApp();
CCSockClientDlg* pDlg=(CCSockClientDlg*)pApp- >m_pMainWnd;
memcpy(m_szBuffer,"Connected to ",13);
strncat(m_szBuffer,pDlg- >m_szServerAdr,
sizeof(pDlg- >m_szServerAdr));
pDlg- >m_MSGS.InsertString(0,m_szBuffer);
AsyncSelect(FD_READ);
////
提请一个的网络事件,准备接收 }
CAsyncSocket::OnConnect(nErrorCode);}

---- 7 新建对话框  IDD_Addr,用来输入  IP  地址和  Port;生成新类  CAddrDlg。增加两个  Edit  控件:IDC_AddrIDC_Port  按下表在  ClassWizard  中为  CAddrDlg  类添加变量。 Control ID Type MemberIDC_Addr CString m_AddrIDC_Port Int m_Port

---- 8   CSockClientDlg.ccp  中添加代码 #include "AddrDlg.h"protected: int TryCount;
MySock m_clientSocket;
UINT m_szPort;public: char m_szServerAdr[256];

---- 9 双击  IDD_CSOCKCLIENT_DIALOG  对话框中的连接按钮,添加以下代码  void CCSockClientDlg::OnConnect() {
m_clientSocket.ShutDown(2);
m_clientSocket.m_hSocket=INVALID_SOCKET;
m_clientSocket.m_bConnected=FALSE;
CAddrDlg m_Dlg; //
默认端口1088m_Dlg.m_Port=1088;
if (m_Dlg.DoModal()==IDOK && !m_Dlg.m_Addr.IsEmpty()) {
memcpy(m_szServerAdr,m_Dlg.m_Addr,sizeof(m_szServerAdr));
m_szPort=m_Dlg.m_Port; //
建立计时器,每1秒尝试连接一次,直到连上或  TryCount>10SetTimer(1,1000,NULL);
TryCount=0; }}

---- 10 添加  Windows  消息  WM_TIMER  响应函数  OnTimer void CCSockClientDlg::OnTimer(UINT nIDEvent) {
if (m_clientSocket.m_hSocket==INVALID_SOCKET) {
BOOL bFlag=m_clientSocket.Create(0,SOCK_STREAM,FD_CONNECT);
if(!bFlag) { AfxMessageBox("Socket Error!");
m_clientSocket.Close();
PostQuitMessage(0);
return;
} }
m_clientSocket.Connect(m_szServerAdr,m_szPort);
TryCount++;
if (TryCount >=10 || m_clientSocket.m_bConnected) {
KillTimer(1);
if (TryCount >=10) AfxMessageBox("Connect Failed!");
return; }
CDialog::OnTimer(nIDEvent);}

---- 11 双击  IDD_CSOCKCLIENT_DIALOG  对话框中的发送按钮,添加以下代码  void CCSockClientDlg::OnSend() {
if (m_clientSocket.m_bConnected)
{m_clientSocket.m_nLength=m_MSG.GetWindowText(m_clientSocket.m_szBuffer, sizeof(m_clientSocket.m_szBuffer));
m_clientSocket.AsyncSelect(FD_WRITE);
m_MSG.SetWindowText(""); }}

---- 12 双击  IDD_CSOCKCLIENT_DIALOG  对话框中的关闭按钮,添加以下代码  void CCSockClientDlg::OnExit() {
//
关闭  Socketm_clientSocket.ShutDown(2); //关闭对话框  EndDialog(0); }

----12.运行此项目,连接时输入主机名或  IP  均可,CAsyncSocket  类会自动处理。

----二. 服务端

----Server  端的编程与  Client  端的类似,下面主要介绍他的  Listen    Accept  函数

----1 建立一个  CNewSocket  类,重载  CAsyncSocket  类的  OnReceiveOnSend  函数,

如何进行信息的显示和发送可以参考  Client  程序。本例中采用将收到信息原封不动发回的方法来实现 Echo  功能,代码如下  CNewSocket::OnReceiveint nErrorCOde{
m_nLength=Receive
m_szBuffersizeofm_szBuffer),0);
//
直接转发消息  AsyncSelectFD_WRITE);}CNewSocket::OnSendint nErrorCode{ Sendm_szBufferm_nLength0);}

----2 建立一个  CMyServerSocket  类,重载  CAsyncSocket  类的  OnAccept  函数代码如下

----  MyServerSocket.h  中声明变量  public:CNewSocket* m_pSocket
void CMyServerSocket
::OnAcceptint nErrorCode{
//
侦听到连接请求,调用  Accept  函数
CNewSocket* pSocket = new CNewSocket
();
if
Accept*pSocket)) {
pSocket- >AsyncSelect
FD_READ);
m_pSocket=pSocket

} else
delete pSocket
}

----3 为对话框添加一个侦听按钮,添加如下代码

----  CsockServerDlg.ccp  中声明变量  public CMyServerSocket m_srvrSocket;
void CCSockServerDlg
::OnListen(){ if
m_srvrSocket.m_hSocket==INVALID_SOCKET {
BOOL bFlag=m_srvrSocket.Create
(UserPort
SOCK_STREAMFD_ACCEPT)
if
(!bFlag { AfxMessageBox“Socket Error);
M_srvrSocket.Close
();
PostQuitMessage
0);
Return
} }
//“
侦听成功,等待连接请求  if (!m_srvrSocketListen1)){
int nErrorCode = m_srvrSocket.GetLastError
();
if
nError=WSAEWOULDBLOCK
{
AfxMessageBox
“Socket Error);
M_srvrSocket.Close
();
PostQuitMessage
0);
Return

} }}

----4 目前程序只能实现  Echo  功能,将信息原封不动的转发,若能将  Accept  中由

CNewSocket* pSocket = new CNewSocket();得到的  Socket  指针存入一个  CList  或一个数组中,便像  Client  端那样,对所有的连接进行读写控制。

----三. 总结

----CAsyncSocket  类为我们使用  Socket  提供了极大方便。建立  Socket    WSAStartup  过程和  bind  过程被简化成为  Create  过程,IP  地址类型转换、主机名和  IP  地址转换的过程中许多复杂的变量类型都被简化成字符串和整数操作,特别是  CAsyncSocket  类的异步特点,完全可以替代繁琐的线程操作。MFC  提供了大量的类库,我们若能灵活的使用他们,便会大大提高编程的效率。 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值