MFC网络编程

这两天学习了MFC网络编程,发现其实网络编程好像也没有想象中的那么难,做个梳理如下:
网络编程人员可以调用windows操作系统套接字访问通信协议,套接字存在与通信区域中,windows套接字只支持一个通信区域即网际域(AF_INET)
套接字的类型有三类:流式套接字(SOCK_STREAM)和数据报式套接字(SOCK_DGRAM)和原始套接字
流式套接字基于TCP协议,数据报式套接字基于UDP协议实现

基于TCP的socket编程的服务器端程序流程如下:
1、创建套接字
2、将套接字绑定到一个本地地址和端口号上(bind)
3、将套接字设为监听模式,准备接受客户请求(listen)
4、等待客户请求,请求到来时接受请求,建立链接,并返回 一个新的基于此次通信的套接字(accept)
5、用返回的套接字和客户端进行通信(send、recv)
6、返回,等待另一客户请求
7、关闭套接字
基于TCP的socket编程的客户端程序流程如下:
1、创建套接字
2、向服务器端发送请求(connect)
3、和服务器端进行通信(send、recv)
4、关闭套接字

基于UDP的socket编程的服务器端程序流程如下:
1、创建套接字
2、将套接字绑定到本地地址和端口号上(bind)
3、等待接收数据(recvfrom)
4、关闭套接字
基于UDP的socket编程的客户端程序流程如下:
1、创建套接字
2、和服务器端进行通信(sendto)
3、关闭套接字

编写自定义消息处理函数
假如说要在Cdlg类中定义消息处理函数则步骤如下:
1、在Cdlg类头文件中定义消息
#define WM_RECVDATA WM_USER+1
2、在Cdlg类的头文件中编写该消息响应函数原型的声明
// Generated message map functions
//{{AFX_MSG(CChatDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
afx_msg void OnPaint();
afx_msg HCURSOR OnQueryDragIcon();
afx_msg void OnBtnSend();
//}}AFX_MSG
afx_msg void OnRecvData(WPARAM wParam,LPARAM lParam);
DECLARE_MESSAGE_MAP()
3、在Cdlg类的实现文件中添加WM_RECVDATA 消息映射

BEGIN_MESSAGE_MAP(CChatDlg, CDialog)
//{{AFX_MSG_MAP(CChatDlg)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BTN_SEND, OnBtnSend)
//}}AFX_MSG_MAP
ON_MESSAGE(WM_RECVDATA,OnRecvData)
END_MESSAGE_MAP()
4、在Cdlg类的实现文件中实现OnRecvData函数

 

 

VC MFC 网络编程入门——简单通信实例学习

 

对于许多初学者来说,网络通信程序的开发,普遍的一个现象就是觉得难以入手。许多概念,诸如:同步(Sync)/异步(Async),阻塞(Block)/非阻塞(Unblock)等,初学者往往迷惑不清,只知其所以而不知起所以然。

同步方式指的是发送方不等接收方响应,便接着发下个数据包的通信方式;而异步指发送方发出数据后,等收到接收方发回的响应,才发下一个数据包的通信方式。

阻塞套接字是指执行此套接字的网络调用时,直到成功才返回,否则一直阻塞在此网络调用上,比如调用recv()函数读取网络缓冲区中的数据,如果没有数据到达,将一直挂在recv()这个函数调用上,直到读到一些数据,此函数调用才返回;而非阻塞套接字是指执行此套接字的网络调用时,不管是否执行成功,都立即返回。比如调用recv()函数读取网络缓冲区中数据,不管是否读到数据都立即返回,而不会一直挂在此函数调用上。在实际Windows网络通信软件开发中,异步非阻塞套接字是用的最多的。平常所说的C/S(客户端/服务器)结构的软件就是异步非阻塞模式的。

对于这些概念,初学者的理解也许只能似是而非,我将用一个最简单的例子说明异步非阻塞Socket的基本原理和工作机制。目的是让初学者不仅对Socket异步非阻塞的概念有个非常透彻的理解,而且也给他们提供一个用Socket开发网络通信应用程序的快速入门方法。操作系统是Windows 98(或NT4.0),开发工具是Visual C++6.0。

MFC提供了一个异步类CAsyncSocket,它封装了异步、非阻塞Socket的基本功能,用它做常用的网络通信软件很方便。但它屏蔽了Socket的异步、非阻塞等概念,开发人员无需了解异步、非阻塞Socket的原理和工作机制。因此,建议初学者学习编网络通信程序时,暂且不要用MFC提供的类,而先用Winsock2 API,这样有助于对异步、非阻塞Socket编程机制的理解。

为了简单起见,服务器端和客户端的应用程序均是基于MFC的标准对话框,网络通信部分基于Winsock2 API实现。

先做服务器端应用程序。

用MFC向导做一个基于对话框的应用程序SocketSever,注意第三步中不要选上Windwos Sockets选项。在做好工程后,创建一个SeverSock,将它设置为异步非阻塞模式,并为它注册各种网络异步事件,然后与自定义的网络异步事件联系上,最后还要将它设置为监听模式。在自定义的网络异步事件的回调函数中,你可以得到各种网络异步事件,根据它们的类型,做不同的处理。下面将详细介绍如何编写相关代码。

在SocketSeverDlg.h文件的类定义之前增加如下定义:

#define NETWORK_EVENT WM_USER+166 file://定义网络事件

SOCKET ServerSock; file://服务器端Socket

在类定义中增加如下定义:

class CSocketSeverDlg : CDialog

{

public:

SOCKET ClientSock[CLNT_MAX_NUM]; file://存储与客户端通信的Socket的数组

/*各种网络异步事件的处理函数*/

void OnClose(SOCKET CurSock); file://对端Socket断开

void OnSend(SOCKET CurSock); file://发送网络数据包

void OnReceive(SOCKET CurSock); file://网络数据包到达

void OnAccept(SOCKET CurSock); file://客户端连接请求

BOOL InitNetwork(); file://初始化网络函数

void OnNetEvent(WPARAM wParam, LPARAM lParam); file://异步事件回调函数

};

在SocketSeverDlg.cpp文件中增加消息映射,其中OnNetEvent是异步事件回调函数名:

ON_MESSAGE(NETWORK_EVENT,OnNetEvent)

定义初始化网络函数,在SocketSeverDlg.cpp文件的OnInitDialog()中调此函数即可。

BOOL CSocketSeverDlg::InitNetwork()

{

WSADATA wsaData;

file://初始化TCP协议

BOOL ret = WSAStartup(MAKEWORD(2,2), &wsaData);

if(ret != 0)

{

MessageBox("初始化网络协议失败!");

return FALSE;

}

file://创建服务器端套接字

ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if(ServerSock == INVALID_SOCKET)

{

MessageBox("创建套接字失败!");

closesocket(ServerSock);

WSACleanup();

return FALSE;

}

file://绑定到本地一个端口上

sockaddr_in localaddr;

localaddr.sin_family = AF_INET;

localaddr.sin_port = htons(8888); file://端口号不要与其他应用程序冲突

localaddr.sin_addr.s_addr = 0;

if(bind(ServerSock ,(struct sockaddr*)&localaddr,sizeof(sockaddr))

= = SOCKET_ERROR)

{

MessageBox("绑定地址失败!");

closesocket(ServerSock);

WSACleanup();

return FALSE;

}

file://将SeverSock设置为异步非阻塞模式,并为它注册各种网络异步事件,其 中 m_hWnd

file://为应用程序的主对话框或主窗口的句柄

if(WSAAsyncSelect(ServerSock, m_hWnd, NETWORK_EVENT,

FD_ACCEPT | FD_CLOSE | FD_READ | FD_WRITE) == SOCKET_ERROR)

{

MessageBox("注册网络异步事件失败!");

WSACleanup();

return FALSE;

}

listen(ServerSock, 5); file://设置侦听模式

return TRUE;

}

下面定义网络异步事件的回调函数

void CSocketSeverDlg::OnNetEvent(WPARAM wParam, LPARAM lParam)

{

file://调用Winsock API函数,得到网络事件类型

int iEvent = WSAGETSELECTEVENT(lParam);

file://调用Winsock API函数,得到发生此事件的客户端套接字

SOCKET CurSock= (SOCKET)wParam;

switch(iEvent)

{

case FD_ACCEPT: file://客户端连接请求事件

OnAccept(CurSock);

break;

case FD_CLOSE: file://客户端断开事件:

OnClose(CurSock);

break;

case FD_READ: file://网络数据包到达事件

OnReceive(CurSock);

break;

case FD_WRITE: file://发送网络数据事件

OnSend(CurSock);

break;

default: break;

}

}

以下是发生在相应Socket上的各种网络异步事件的处理函数,其中OnAccept传进来的参数是服务器端创建的套接字,OnClose()、OnReceive()和OnSend()传进来的参数均是服务器端在接受客户端连接时新创建的用与此客户端通信的Socket。

void CSocketSeverDlg::OnAccept(SOCKET CurSock)

{

file://接受连接请求,并保存与发起连接请求的客户端进行通信Socket

file://为新的socket注册异步事件,注意没有Accept事件

}

void CSocketSeverDlg::OnClose(SOCET CurSock)

{

file://结束与相应的客户端的通信,释放相应资源

}

void CSocketSeverDlg::OnSend(SOCET CurSock)

{

file://在给客户端发数据时做相关预处理

}

void CSocketSeverDlg::OnReceive(SOCET CurSock)

{

file://读出网络缓冲区中的数据包

}

用同样的方法建立一个客户端应用程序。初始化网络部分,不需要将套接字设置为监听模式。注册异步事件时,没有FD_ACCEPT,但增加了FD_CONNECT事件,因此没有OnAccept()函数,但增加了OnConnect()函数。向服务器发出连接请求时,使用connect()函数,连接成功后,会响应到OnConnect()函数中。下面是OnConnect()函数的定义,传进来的参数是客户端Socket和服务器端发回来的连接是否成功的标志。

void CSocketClntDlg::OnConnect(SOCKET CurSock, int error)

{

if(0 = = error)

{

if(CurSock = = ClntSock)

MessageBox("连接服务器成功!");

}

}

定义OnReceive()函数,处理网络数据到达事件;

定义OnSend()函数,处理发送网络数据事件;

定义OnClose()函数,处理服务器的关闭事件。

以上就是用基于Windows消息机制的异步I/O模型做服务器和客户端应用程序的基本方法。另外还可以用事件模型、重叠模型或完成端口模型,读者可以参考有关书籍。

在实现了上面的例子后,你将对Winsock编网络通信程序的机制有了一定的了解。接下来你可以进行更精彩的编程, 不仅可以在网上传输普通数据,而且还以传输语音、视频数据,你还可以自己做一个网络资源共享的服务器软件,和你的同学在实验室的局域网里可以共同分享你的成果。

 

 

CAsyncSocket网络编程(MFC)

 

     随着计算机网络化的深入,计算机网络编程在程序设计的过程中变得日益重要。由于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_READ、FD_WRITE、FD_ACCEPT、FD_CONNECT分别调用OnReceive、OnSend、OnAccept、OnConnect函数。由于MFC把这些事件处理函数定义为虚函数,所以要生成一个新的C++类,以重载这些函数,做法如下:

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

---- 为MySock类添加虚函数OnReceive、OnConnect、OnSend

---- 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_Addr、IDC_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类的OnReceive、OnSend函数,

如何进行信息的显示和发送可以参考Client程序。本例中采用将收到信息原封不动

发回的方法来实现Echo功能,代码如下CNewSocket::OnReceive(int nErrorCOde){ 
m_nLength=Receive(m_szBuffer,sizeof(m_szBuffer),0); 
// 直接转发消息AsyncSelect(FD_WRITE);}CNewSocket::OnSend(int nErrorCode){ Send(m_szBuffer,m_nLength,0);}

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

----在MyServerSocket.h中声明变量public::CNewSocket* m_pSocket;
void CMyServerSocket::OnAccept(int 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_STREAM,FD_ACCEPT); 
if (!bFlag) { AfxMessageBox(“Socket Error!”); 
M_srvrSocket.Close(); 
PostQuitMessage(0); 
Return; } }
//“侦听”成功,等待连接请求if (!m_srvrSocket。Listen(1)){ 
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提供了大量的类库,我们若能灵活的使用他们,便会大大提高编程的效

 

 

 

MFC下的网络编程

 

Visual C++的MFC提供了CSocket类用来实现网络通信。下图给出了CSocket 类的继承关系。


  下面介绍VC++在Windows 95中实现Socket的 CSocket 类相关成员函数(这些成员函数实际上是从CAsyncSocket 类继承来的)的使用。 

(1) BOOL Create( UINT nSocketPort = 0, int nSocketType = SOCK_STREAM, long lEvent = FD_READ |FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT| FD_CLOSE,LPCTSTR lpszSocketAddress = NULL ) 
  该函数用来建立Socket。 其中,nSocketPort 为所选择的Socket 端口,一般要大于 1023, 如果该参数为0,则由系统选定一端口,默认值为0 ;nSocketType 为套接字类型:SOCK_STREAM 表示为流套接字,SOCK_DGRAM 表示为数据报套接字,默认值为SOCK_STREAM ;lEvent 标识该Socket 要完成哪种工作,默认值为FD_READ|FD_WRITE|FD_OOB| FD_ACCEPT|FD_CONNECT|FD_CLOSE ;lpszSockAddress 为网络地址信息结构指针,包含网络地址, 默认值为NULL 。 

(2)BOOL Bind( UINT nSocketPort, LPCTSTR lpszSocketAddress = NULL ) 
  该函数的作用是将Socket 端口与网络地址连接起来。参数含义同上 。 
(3)BOOL Listen( int nConnectionBacklog = 5 ) 
  该函数的作用是等待Socket请求。其中,nConnec-tionBacklog 表示等待队列的长度,默认值为最大值5 。 

(4)virtual BOOL Accept( CAsyncSocket& rConnectedSocket, SOCKADDR* lpSockAddr = NULL, int* lpSockAddrLen = NULL ) 
  该函数的作用是取得队列上第一个连接请求并建立一个具有与Socket相同特性的套接字。其中,rConnectedSocket 表示一个新的Socket 。 

(5)BOOL Connect( LPCTSTR lpszHostAddress, UINT nHostPort ) 
  该函数的作用是提出请求。其中,lpszHostAddress 和 nHostPort 为接受请求进程的网络地址和Socket 端口号。 

(6)virtual void Close( ) 
  该函数的作用是关闭该Socket 。 

  利用CSocket类直接进行数据通信有两种方式:一种是利用CSocketFile 类和Archive 类去实现,另一种是利用CSocket的成员函数 Receive、Send、ReceiveFrom、SendTo、Listen 和 Accept 等来实现(这些成员函数实际上也是从CAsyncSocket 类继承的)。 
  两种方法的实现步骤如下 : 

  Server : Construct-> Creat-> Bind -> Listen-> Accept-> Send->Close ; 

  Cilent : Construct ->Creat-> Connect-> Receive-> Close。 

   下面就用VC++的代码分别介绍如何运用上述两种方法来实现Socket 编程。 

  1、 利用CSocketFile类和Archive 类实现

  (1)服务器程序流程
  // 创建一个套接字对象
  CSocket sockSrvr; 

  //为上述套接字对象创建一个套接字

  sockSrvr.Create(nPort); 

  //开始侦听
  sockSrvr.Listen( ); 

  //创建一个新的套接字对象
  CSocket sockRecv; 

  //接受连接
  sockSrvr.Accept( sockRecv ); 

// 创建文件对象
CSocketFile file(&sockRecv); 

  //创建一个archive对象
  CArchive arIn(&file, CArchive::load); 

  /*or*/_CArchive arOut(&file, CArchive::store); 

  //使用archive对象传输数据
  arIn >> dwValue; 

  /*or*/ arOut < < dwValue; 

  (2)客户端程序流程
  //创建一个套接字对象
  CSocket sockClient; 

  //为这个对象创建一个套接字
  sockClient.Create( ); 

  //寻找一个连接
  sockClient.Connect(strAddr, nPort); 

  //创建一个文件对象
  CSocketFile file(&sockClient); 

  //创建一个archive对象
  CArchive arIn(&file, CArchive::load); 

  /*or*/_CArchive arOut(&file, CArchive::store); 

  //使用这个对象传输数据
  arOut < < dwValue; 

  /*or*/ arIn >> dwValue; 

  上述程序中, nPort 是Socket 的端口号,strAddr 是该机器的IP地址(如202.197.1.3 或 FTP://RedAlert.com等),这两个变量在Server和Client中要一致。当Server进程运行至Listen 后便处于睡眠状态直到Client进程执行Connect 时才被唤醒,而后两个进程便开始传输数据了。 

  2、利用CSocket的成员函数实现 
  (1)服务器流程
  //套接字初始化
  if(!AfxSocketInit()){ 
   MessageBox("WindowsSocket initial failed!","Send",MB_ICONSTOP); 
   Return; 
  }

  // 创建两个套接字对象
  CSocket ChatSend,server; 

  // 创建一个套接字
  if(!ChatSend.Create(nPort)) // nPort=1025 
   MessageBox("SendSocket create failed!", "Send",MB_ICONSTOP); 
  else{ 
   // 把本地地址给套接字
ChatSend.Bind(nProt,strAddr); 
  // strAddr="202.196.111.1" 
   // 开始侦听
   ChatSend.Listen(); 

   // 创建一个新的套接字并和他相连
   ChatSend.Accept(Server); 
  }
  //发送一个CString 对象
  Server.SendTo(csSendText,csCounts,nPort,strAddr); 

  // 关闭这两个套接字
  Server.Close(); 
  ChatSend.Close(); 

  (2)客户端程序流程

  // 套接字初始化
  if(!AfxSocketInit()){ 
   MessageBox("WindowsSocket initial failed!", "Receive",MB_ICONSTOP); 
   return; 
  }

  // 创建一个套接字对象
  CSocket ChatRecieve; 

  // 创建一个套接字
  if(!ChatReceive.Create()){ 
   MessageBox("ReceiveSocket create failed!","Receive",MB_ICONSTOP); 
   return; 
  }
  else{ 
   // 创建一个对等套接字
   ChatReceive.Connect(strAddr,nPort); 
  }

  //接受一个CString 对象
  ChatReceive.ReceiveFrom(csReceiveText,csCounts,strAddr,nPort); 

  // 关闭套接字
  ChatReceive.Close(); 

  上述两个进程完成的工作是:由Server 进程发送一字符串,Client 进程接收。 strAddr 和 nPort 的含义与方法1 中的相同 ;csSendText 和 csReceiveText 为发送与接收的字符串;csCounts为字串长度,这一长度在两个进程中要求接收长度小于或等于发送长度,否则会导致数据传输错误。另外,在程序中要加入头文件afxsock.h, CSocket 类的有关说明均在afxsock.h 中。 

方法1 适合于对多个不同类型数据的通信,方法2 适合于对字符串的通信,具体选用何种方法则取决于具体应用的需求。  

环境:Windows XP SP3、 VC++ 6.0、 Windows 2003 SDK 使用步骤: 1、下载解压之后,使用VC++ 6.0打开两个工程:一个是SocketServer和一个ClientSocket工程。 2、首先运行服务器端工程,选默认的端口1008 3、然后运行客户端工程,选默认的端口1008和默认的服务器地址 4、再运行多个客户端进程 5、如果一切正常,可以每个客户端的消息发送,我们可以在服务端和各个客户端同步看到消息 实现一个服务器对多个客户端的关键是,在服务端的使用集合CPtrList类用保存客户端的socket对象,思想与Java中的编程思想一样,只不过Java中会使用多线程技术,在Vector集合保存客户端的socket对象,而MFC框架提供了CSocket类,它是一个异步通信的类,所以看上去代码比较Java的多线程代码简单的实现了一个对多的即时通讯功能。另外,MFC提供了CSocketFile类和CArchive类与CSocket类实现了C++的网络通讯编程功能。 本示例注释非常详细,所有的辅助类都放一个util目录中,然后在工程中分了一个目录来管理这些辅助类,使用代码非常清晰。手动书写部分的代码是按Java的规范书写,当然其它代码由IDE生成的,所以是MS的风格,所以当你看代码时,只要是使用“骆驮命名法”的方法都是本人书写的功能性代码。 参看的思路:在服务端要从回调方法onAccept读起;而客户端代码主要从OnSendButton方法读起,即可理解整个代码的意思。 阅读对象:具有Java的Socket编程经验的人员,并且希望能够书写出比Java效率更高的即时通讯程序的人员
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值