CAsyncSocket与CSocket的区别
前者是异步通信,后者是同步通信;前者是非阻塞模式,后者是阻塞
模式。另外,异步非阻塞模式有时也被称为长连接,同步阻塞模式则
被称为短连接。为了更明白地讲清楚两者的区别,举个例子:
设想你是一位体育老师,需要测验100位同学的400米成绩。
你当然不会让这100位同学一起起跑,因为当同学们返回终点时,你
根本来不及掐表记录各位同学的成绩。
如果你每次让一位同学起跑并直到他回到终点你记下成绩后再让下一
位起跑,直到所有同学都跑完。恭喜你,你已经掌握了同步阻塞模式。
你设计了一个函数,传入参数是学生号和起跑时间,返回值是到达终
点的时间。你调用该函数100次,就能完成这次测验任务。
这个函数是同步的,因为只要你调用它,就能得到结果;这个函数也
是阻塞的,因为你一旦调用它,就必须等待,直到它给你结果,不能
去干其他事情。
如果你一边每隔10秒让一位同学起跑,直到所有同学出发完毕;另一
边每有一个同学回到终点就记录成绩,直到所有同学都跑完。恭喜你,
你已经掌握了异步非阻塞模式。
你设计了两个函数,其中一个函数记录起跑时间和学生号,该函数你
会主动调用100次;另一个函数记录到达时间和学生号,该函数是一个
事件驱动的callback函数,当有同学到达终点时,你会被动调用。
你主动调用的函数是异步的,因为你调用它,它并不会告诉你结果;
这个函数也是非阻塞的,因为你一旦调用它,它就马上返回,你不用
等待就可以再次调用它。但仅仅将这个函数调用100次,你并没有完成
你的测验任务,你还需要被动等待调用另一个函数100次。
当然,你马上就会意识到,同步阻塞模式的效率明显低于异步非阻塞
模式。那么,谁还会使用同步阻塞模式呢?
不错,异步模式效率高,但更麻烦,你一边要记录起跑同学的数据,
一边要记录到达同学的数据,而且同学们回到终点的次序与起跑的次
序并不相同,所以你还要不停地在你的成绩册上查找学生号。忙乱之
中你往往会张冠李戴。
你可能会想出更聪明的办法:你带了很多块秒表,让同学们分组互相
测验。恭喜你!你已经掌握了多线程同步模式!
每个拿秒表的同学都可以独立调用你的同步函数,这样既不容易出错,
效率也大大提高,只要秒表够多,同步的效率也能达到甚至超过异步。
可以理解,你现的问题可能是:既然多线程同步既快又好,异步模式
还有存在的必要吗?
很遗憾,异步模式依然非常重要,因为在很多情况下,你拿不出很多
秒表。你需要通信的对端系统可能只允许你建立一个SOCKET连接,很
多金融、电信行业的大型业务系统都如此要求。
现在,你应该已经明白了:
CAsyncSocket用于在少量连接时,处理大批量无步骤依赖性的业务。
CSocket用于处理步骤依赖性业务,或在可多连接时配合多线程使用。
二、CAsyncSocket异步机制
当你获得了一个异步连接后,实际上你扫除了发送动作与接收动作之
间的依赖性。所以你随时可以发包,也随时可能收到包。发送、接收
函数都是异步非阻塞的,顷刻就能完成,所以收发交错进行着,你可
以一直工作,保持很高的效率。
但是,正因为发送、接收函数都是异步非阻塞的,所以仅调用它们并
不能保障发送或接收的完成。
例如发送函数Send,调用它可能有3种结果:错误、部分完成、全部完
成。其中错误又分两种情况:一种是由各种网络问题导致的失败,你
需要马上决定是放弃本次操作,还是启用某种对策;另一种是“忙”,
你实际上不用马上理睬。你需要调用GetLastError来判断是哪种情况,
GetLastError返回WSAEWOULDBLOCK,代表“忙”,为什么当你Send得
到WSAEWOULDBLOCK却不用理睬呢?因为CAsyncSocket会记得你的Send
WSAEWOULDBLOCK了,待发送的数据会写入CAsyncSocket内部的发送缓
冲区,并会在不忙的时候自动调用OnSend,发送内部缓冲区里的数据。
同样,如果Send只完成了一部分,你也不需要理睬,尚未发送的数据
同样会写入CAsyncSocket内部的发送缓冲区,并在适当的时候自动调
用OnSend完成发送。
与OnSend协助Send完成工作一样,OnRecieve、OnConnect、OnAccept
也会分别协助Recieve、Connect、Accept完成工作。这一切都通过消
息机制完成:
在你使用CAsyncSocket之前,必须调用AfxSocketInit初始化WinSock
环境,而AfxSocketInit会创建一个隐藏的CSocketWnd对象,由于这个
对象由Cwnd派生,因此它能够接收Windows消息。一方面它会接受各个
CAsyncSocket的状态报告,另一方面它能捕捉系统发出的各种SOCKET
事件。所以它能够成为高层CAsyncSocket对象与WinSock底层之间的
桥梁:例如某CAsyncSocket在Send时WSAEWOULDBLOCK了,它就会发送
一条消息给CSocketWnd作为报告,CSocketWnd会维护一个报告登记表,
当它收到底层WinSock发出的空闲消息时,就会检索报告登记表,然后
直接调用报告者的OnSend函数。所以前文所说的CAsyncSocket会自动
调用OnXxx,实际上是不对的,真正的调用者是CSocketWnd——它是
一个CWnd对象,运行在独立的线程中。
三、CAsyncSocket的用法
你会发现直接使用CAsyncSocket类来创建对象很难满足业务需要。因
为CAsyncSocket隐藏了很多细节并不告诉你,你对业务的控制能力得
不到保障。
例如你需要知道每笔订单发给对端业务系统的确切时间,如果你直接
使用CAsyncSocket的话,你无从得知,因为每次只要Send函数不是马
上全部完成,而是进入了缓冲区等待CSocketWnd调用OnSend,这个订
单到底是啥时候发出去的就很难说了。OnSend内部毕竟还是调用Send
来发送的,所以可能OnSend一次也并不能完全发送,还要等待下一次
甚至多次OnSend。而且这笔糊涂账全闷在CSocketWnd肚子里,它并不
提供一个让你了解的机会。
接收就更成问题了,通常业务系统会定义多种类型的数据包格式,你
既不知道下一个将收到的包是哪种包,也不知道它将何时抵达。所以
你在业务逻辑上永远不应该主动调用Recieve,而应该被动等待数据
抵达时OnRecieve被自动调用,但OnRecieve也不懂你的业务包的格式,
甚至也不会向你报告请示。CAsyncSocket只能处理字节流,但你需要
处理的通常是业务流。
所以,使用CAsyncSocket,通常是需要派生的,一般你至少需要重载
OnSend和OnRecieve,你需要参与其中以进行业务控制。当然,既然
你重载了OnSend和OnRecieve,你得自己编码实现协助Send、Recieve
保证完成收发,你可能发现你自己并不能访问CAsyncSocket的内部缓
冲区,所以你还要自己定义收发缓冲区并记录收发进度。
其实你不大可能在OnSend或OnRecieve里面做好一切,你往往不得不
与你的主线程交互,你可以参考以下两种模式:
第一种,在你的CXxxSocket类中申明一个你的主线程类指针,作为成
员变量,典型的情况下,主线程是一个CXxxDlg,所以:
//CXxxSocket类头文件
Class CXxxDlg;
Class CXxxSocket : public CAsyncSocket
{
CXxxDlg* m_pDlg;
...
virtual void OnReceive(int nErrorCode);
}
//CXxxSocket类实现文件
...
void CXxxSocket::OnReceive(int nErrorCode)
{
...
m_pDlg->SomeFunc();
...
}
...
//CXxxDlg类实现文件
...
CXxxSocket sock;
sock.m_pDlg=this;
sock.Create(...);
...
这种方法很方便,但并不灵活,代码可重用性低,只有CXxxDlg类能够
使用CXxxSocket类。
第二种,在你的CXxxSocket类中申明一个窗口句柄作为成员变量,当
你需要主线程参与工作时,向主线程发送自定义消息:
//CXxxSocket类头文件
static UINT WM_SOCKET_MSG=::RegisterWindowMessage("SocketMsg");
Class CXxxSocket : public CAsyncSocket
{
HWND m_hWnd;
...
virtual void OnReceive(int nErrorCode);
}
//CXxxSocket类实现文件
...
void CXxxSocket::OnReceive(int nErrorCode)
{
...
SendMessage(m_hWnd,WM_SOCKET_MSG,...);
...
}
...
//CXxxDlg类实现文件
...
CXxxSocket sock;
sock.m_hWnd=GetSafeHwnd();
sock.Create(...);
...
四、CAsyncSocket的运作流程
使用CAsyncSocket时,Send流程和Recieve流程是不同的,不理解这
一点就不可能顺利使用CAsyncSocket。
MSDN对CAsyncSocket的解释很容易让你理解为:只有OnSend被触发时
你Send才有意义,你才应该Send,同样只有OnRecieve被触发时你才
应该Recieve。很不幸,你错了:
你会发现,连接建立的同时,OnSend就第一次被触发了,嗯,这很好,
但你现在还不想Send,你让OnSend返回,干点其他的事情,等待下一
次OnSend试试看?实际上,你再也等不到OnSend被触发了。因为,除
了第一次以外,OnSend的任何一次触发,都源于你调用了Send,但碰
到了WSAEWOULDBLOCK或只完成了部分发送!
所以,使用CAsyncSocket时,针对发送的流程逻辑应该是:你需两个
成员变量,一个发送任务表,一个记录发送进度。你可以,也应该,
在任何你需要的时候,主动调用Send来发送数据,同时更新任务表和
发送进度。而OnSend,则是你的负责擦屁股工作的助手,它被触发时
要干的事情就是根据任务表和发送进度调用Send继续发,若此次发送
没能将任务表全部发送完成,根据发送结果更新发送进度;若任务表
已全部发送完毕,则清空任务表及发送进度。
使用CAsyncSocket的接收流程逻辑是不同的:你永远不需要主动调用
Recieve,你只应该在OnRecieve中等待。由于你不可能知道将要抵达
的数据类型及次序,所以你需要一个成员变量来存储已收到但尚未处
理的数据。每次OnRecieve被触发,你只需要被动调用一次Recieve来
接受固定长度的数据,并添加到你的已收数据表后。然后你需要扫描
已收数据表,若其中已包含一条或数条完整的可解析的业务数据包,
截取出来,调用主线程的处理函数来处理或作为消息参数发送给主线
程。而已收数据表中剩下的数据,将等待下次OnRecieve中被再次组合
、扫描并处理。
在长连接应用中,连接可能因为各种原因中断,所以你需要自动重连。
你需要根据CAsyncSocket的成员变量m_hSocket来判断当前连接状态:
if(m_hSocket==INVALID_SOCKET)
当然,很奇怪的是,即使连接已经中断,OnClose也已经被触发,你
还是需要在OnClose中调用Close,否则m_hSocket并不会被自动赋值
为INVALID_SOCKET。
在很多长连接应用中,除建立连接以外,还需要先Login,然后才能
进行业务处理,连接并Login是一个步骤依赖性过程,用异步方式处
理反而会很麻烦,而CAsyncSocket是支持切换为同步模式的,你应该
掌握在适当的时候切换同异步模式的方法:
DWORD dw;
//切换为同步模式
dw=0;
IOCtl(FIONBIO,&dw);
...
//切换回异步模式
dw=1;
IOCtl(FIONBIO,&dw);
五、CSocket的用法
CSocket在CAsyncSocket的基础上,修改了Send、Recieve等成员函数,
帮你内置了一个用以轮询收发缓冲区的循环,变成了同步短连接模式。
短连接应用简单明了,CSocket经常不用派生就可以直接使用,但也
有些问题:
1、用作监听的时候
曾经看到有人自己创建线程,在线程中创建CSocket对象进行Listen、
Accept,若Accept成功则再起一个线程继续Listen、Accept。。。
可以说他完全不理解CSocket,实际上CSocket已经内置了多线程机制,
你只需要从CSocket派生,然后重载OnAccept:
//CListenSocket头文件
class CListenSocket : public CSocket
{
public:
CListenSocket(HWND hWnd=NULL);
HWND m_hWnd; //事件处理窗口
virtual void OnAccept(int nErrorCode);
};
//CListenSocket实现文件
#include "ListenSocket.h"
CListenSocket::CListenSocket(HWND hWnd){m_hWnd=hWnd;}
void CListenSocket::OnAccept(int nErrorCode)
{
SendMessage(m_hWnd,WM_SOCKET_MSG,SOCKET_CLNT_ACCEPT,0);
CSocket::OnAccept(nErrorCode);
}
//主线程
...
m_pListenSocket=new CListenSocket(m_hWnd);
m_pListenSocket->Create(...);
m_pListenSocket->Listen();
...
LRESULT CXxxDlg::OnSocketMsg(WPARAM wParam, LPARAM lParam)
{
UINT type=(UINT)wParam;
switch(type)
{
case SOCKET_CLNT_ACCEPT:
{
CSocket* pSocket=new CSocket;
if(!m_pListenSocket->Accept(*pSocket))
{
delete pSocket;
break;
}
...
}
...
}
}
2、用于多线程的时候
常看到人说CSocket在子线程中不能用,其实不然。实际情况是:
直接使用CSocket动态创建的对象,将其指针作为参数传递给子线程,
则子线程中进行收发等各种操作都没问题。但如果是使用CSocket的
派生类创建的对象,就要看你重载了哪些方法,假如你仅仅重载了
OnClose,则子线程中你也可以正常收发,但不能Close!
因为CSocket是用内部循环做到同步的,并不依赖各OnXxx,它不需
要与CSocketWnd交互。但当你派生并重载OnXxx后,它为了提供消息
机制就必须与CSocketWnd交互。当你调用AfxSocketInit时,你的主
线程会获得一个访问CSocketWnd的句柄,对CSocketWnd的访问是MFC
自动帮你完成的,是被隐藏的。而你自己创建的子线程并不自动具
备访问CSocketWnd的机制,所以子线程中需要访问CSocketWnd的操
作都会失败。
常看到的解决办法是不要给子线程传递CSocket对象指针,而是传递
SOCKET句柄,然后在子线程中创建CSocket临时对象并Attach传入的
句柄,用完后再Dettach并delete临时对象。俺没有这么干过,估计
是因为Attach方法含有获取CSocketWnd句柄的内置功能。
俺的解决方案还是使用自定义消息,比如俺不能在子线程中Close,
那么,俺可以给主线程发送一条消息,让主线程的消息处理函数来
完成Close,也很方便。
六、CSocket的运作流程
CSocket一般配合多线程使用,只要你想收发数据,你就可以创建一
个CSocket对象,并创建一个子线程来进行收发。所以被阻塞的只是
子线程,而主线程总是可以随时创建子线程去帮它干活。
由于可能同时有很多个CSocket对象在工作,所以你一般还要创建一
个列表来储存这些CSocket对象的标识,这样你可能通过在列表中检
索标识来区分各个CSocket对象,当然,由于内存地址的唯一性,对
象指针本身就可以作为标识。
相对CAsyncSocket而言,CSocket的运作流程更直观也更简单,至于
CSocketFile、CArchive之类的,似乎也不需要多说什么,就这样结
束吧。 ------------------------------------
CASyncSocket类和CSocket类编程
本文主要讲解以下内容
(1)CAsyncSocket类编程模式。
(2)CSocket类编程模式。
(3)CSocketFile类和CArchive类简介。
通过学习,可以掌握CAsyncSocket类和CSocket类的编程模式,了解CSocketFile类和
CArchive类。CAsyncSocket类逐个封装了Winsock函数,以前介绍过的Winsock函数在CAsyncSocket类
的成员函数中都可以找到它们对应的函数。一个CAsyncSocket对象就代表着一个Windows Socket,
使用这个类进行网络编程,就要求程序员对网络通信和Socket编程模式有相当的了解,因为程序员
要自己编写程序处理阻塞、字节顺序以及Unicode与MBCS之间的转换等问题。
为使程序员可以更方便地编写Socket程序,MFC又给出了CSocket类。CSocket类是CAsyncSocket
类的派生类,它在继承了CAsyncSocket类的所有函数的同时提供了比CAsyncSocket类更高层的网络
编程接口。利用CSocket类和与之搭配的CSocketFile类以及CArchive类编写网络程序更加简单便利,
这主要是由于在CSocket类中提供了阻塞模式,因而可以利用CArchive进行同步操作。
提示:
在这里需要特别强调一下阻塞函数的概念,一个Socket可以处于“阻塞模式”或“非阻塞模式”,
当一个Socket处于“阻塞模式”时,它的阻塞函数(如send函数、recv函数之类的函数)直到其操
作完成后才会返回给程序控制权,而Socket的阻塞在此之前不能作其他操作。如果Socket处于“非
阻塞模式”(即异步模式),调用函数将会立即返回给程序控制权。
对于阻塞函数引起的错误,调用CAsyncSocket类的GetLastError成员函数时,将返回错误
WSAEWOULDBLOCK。而对于CSocket类,由于它有自己的阻塞管理,则不会出现该类错误代码,
CSocket类的许多成员函数都带有阻塞性质,但可以结合CArchive类处理。在Win32环境下,如果
要使用具有阻塞性质的Socket,应该将其放在单独的工作者线程中,充分利用多线程编程不干预其他
线程的便利。多线程编程的方法,可以使程序员充分利用CSocket类的阻塞管理方式,而不影响到用
户界面线程。
要在MFC中进行Socket编程,需要在应用程序类的Initlnstance中调用AfxSocketlnit初始化套
接字。如果使用AppWizard创建应用程序的基本框架时,选中了“WindowsSockets”复选框,那么将
自动添加初始化代码。
1 CAsyncSocket类编程模式
在用MFC进行Winsock编程时,使用CAsyncSocket类的优点为:可以轻松处理多个网络协议,
效率高,灵活性好;它的缺点则是需要程序员编写程序来处理诸如阻塞等情况,比较麻烦。
提示:
使用CAsyncSocket类和CSocket类编程,需要加载头文件afxsock.h。
以下为CAsyncSocket类的编程模式。
1)创建一个Socket
创建一个Socket分为两步,首先调用CAsyncSocket类的构造函数创建一个CAsyncSocket类的
对象;然后调用成员函数Create创建底层套接字。构造套接字时,对于服务器端的程序,需要用一
个众所周知的端口提供服务;而对于客户端程序,使用缺省的参数调用Create函数创建Socket即可。
以下是创建Socket的程序代码举例。
(1)服务器端程序:
CAsyncSocketServerSocket;
int nPort=27;
ServerSocket.Create(nPort,SOCK_DGRAM);
以上程序在栈中创建了一个CAsyncSocket对象,并用调用该对象的Create函数创建了一个数据
报式套接字。
CAsyncSocket* pServer=newCAsyncSocket;
intnPort=27;
pServer-> Create(nPort);
以上程序在堆中创建了一个CAsyncSocket对象,并用调用该对象的Create函数创建了一个字节
流式套接字,创建字节流式套接字的Create函数的第二个参数可省略。
(2)客户端程序;
CAsyncSocketClientSocket;
ClientSocket.Create();
以上程序在栈中创建了一个CAsyncSocket对象,并使用以缺省参数调用该对象的Create函数创
建了一个数据报式套接字。对于客户端程序的Socket采用Create函数的缺省调用,可以使套接字自
主地选择一个能够使用的端口。
为了进一步说明Create函数的用法,下面给出该函数的原型:
BOOLCreate(UINT nSocketPort=0,int nSocketType=SOCK_STREAM,longlEvent=FD_READ |
FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE, LPCTSTR
lpszSocketAddress=NULL);
参数说明:
nSocketPort:为套接字指定的端口,缺省时将自动为套接字选择可用的端口。
nSocketType:套接字类型,默认为字节流方式,如果把该参数设为SOCK—DGRAM,将创建数
据报式套接字。
lEevent:该参数用于指定可以通知相关窗口的消息,缺省为列出的所有事件都将生成相应的消
息通知相关的窗口。
lpszSocketAddress:套接字的网络地址,缺省时为本机的网络地址。
提示:
MFC定义了一个内部的类CSocketWnd, 当调用Create函数创建一个套接字时,就会将该套接
字连接到一个窗口(CSoketWnd的对象),CAsyncSocket的DoCallBack函数为该窗口的回调函数。
这样, 当一个网络事件发生时,经过MFC的消息循环,DoCallBack函数会根据不同的事件调用相应
的消息处理函数。MFC将这些消息处理函数定义为虚函数,在编程时必须重载需要的消息处理函数。
2)客户端连接与服务端监听
对于客户端程序,需要调用Connect成员函数连接到服务器,如以下程序所示:
ClientSocket.Connect(addr,nPort);
参数说明:
addr:服务器地址,例如“202.12.15.1”或www.microsoft.com之类的网络地址。
nPort:服务器Socket端口号。
对于服务器端程序,需要调用Listen成员函数对Socket所在的端口进行监听,一旦收到连接请
求,则调用Accept成员函数开始接收,如以下程序所示:
ServerSocket.Listen();
ServerSocket.Accept(CAsyncSocket& rConnectedSocket);
Accept函数的参数为一个空的CAsyncSocket对象,即由CAsyncSocket的构造函数构造的还未调
用Create成员函数创建套接字的CAsyncSocket对象。
提示:
为了进一步说明Accept函数的用法,下面给出该函数的原型:
virtual BOOL Accept(CAsyncSocket& rConnectedSocket,SOCKADDR lpSockAddr=NULL,int
lpSockAddrLen=NULL);
(1)调用其他的CasyncSocket成员函数进行通信管理。
(2) 网络通信结束后,对于在栈中创建的CAsyncSocket对象,如果对象超出定义的范围时将自
动调用析构函数释放相关资源;对于在堆中创建的CAsyncSocket,需要调用delete操作销毁对象符
释放相关资源。
2 CSOCket类编程模式
CSocket类是CAsyncSocket类的派生类,它们都是从CObject类继承下来的.CSocket类代表
了比CAsyncSocket类更高层次的网络接口抽象。CSocket可以和CSocketFile类、CArchive类一
起管理数据的接收和发送。
以下为CSocket类的编程模式:
(1)创建Socket
●服务器端程序:
CSocketsockSrvr;
sockSrvr.Create(nPort); //用指定端口创建套接字
●客户端程序:
CSocketsockClient;
sockClient.Create(); //用缺省的端口创建套接字
(2)连接
●服务器端程序:
sockSrvr.Listen(); 服务器端程序对指定连接端口进行监听
CSockersockRecv; //创建一个空的CSocket对象
sockSrvr.Accept(sockRecv); //接受客户端的连接请求
●客户端程序:
sockClient.Connect(strAddr,nPort);//连接指定地址的服务器(参数strAddr中指定)
(3)数据传输
●服务器端程序:
CSocketFile file(&sockRecv);
//创建与CSocket类的对象相连接的CSoeketFile类对象
CArchive arin(&file,CArchive::load);
//创建与CSocketFile类的对象相连接的CArchive类对象,用于存储将要发送的数据
CArchive arout(&file,CArchive::load);
//创建与CSocketFile类的对象相连接的CArchive类对象,用于存储接收的数据
arin> > dwValue; //发送数据dwValue
arout < <dwValue; //接收数据dwValue
●客户端程序:
CSocketFile file(&sockClient);
//创建与CSocket类的对象相连接的CSocketFile类对象
CArchive arin(&file,CArchive::load);
//创建与CSocketFile类的对象相连接的CArchive类对象,用于存储将要发送的数据
CArchive arout(&file,CArchive::load)
//创建与CSocketFile类的对象相连接的CArchive类对象,用于存储接收的数据
arin> > dwValue; //发送数据dwValue
arout < <dwValue; //接收数据dwValue
(4) 网络通信结束后,对于在栈中创建的CSocket对象、CArchive对象、CSocketFile对象,如
果对象超出定义的范围时将自动调用析构函数释放相关资源;对于在堆中创建的对象,需要调用delete
操作销毁对象符释放相关资源。
提示:
关于CArchive类和CSocketFile类的知识将在后面的小节中介绍,在这里只需要有个大致的了解
即可。
除了利用CSocketFile类的对象和CArchive类的对象辅助数据传输外,利用CSocket的成员函数
同样可以实现数据的网络传输。
下面为利用CSocket成员函数的编程模式:
(1)创建Socket
●服务器端程序:
CSoeket soekSrvr;
sockSrvr.Create(nPort); //用指定端口创建套接字
●客户端程序:
CSocket sockClient;
sockClient.CreateO; //用缺省的端口创建套接字
(2)连接
●服务器端程序:
sockSrvr.Listen(); //服务器端程序对指定连接端口进行监听
CSocket sockRecv; "创建一个空的CSocket对象
sockSrvr.Accept(sockRecv); //接受客户端的连接请求
●客户端程序:
sockClient.Connect(strAddr,nPort)//连接指定地址的服务器(参数strAddr中指定)
(3)数据传输
●服务器端程序:
sockRecv.SendTo(esSendText,csCounts,nPort,strAddr);
//调用SendTo成员函数将csSendText指向的缓冲区数据传送到参数strAddr所代表的
//网络地址。
●客户端程序:
sockClient.RecieveFrom(csRecieveText,csCounts,strAddr,nPort);
//调用ReciveFrom成员函数将从strAddr所代表的网络主机传送来的数据保存在//csReciveText
指向的缓冲区中。
(4)通话结束处理
●服务器端程序:
sockSrvr.Close();
sockRecv.Close();
●客户端程序,
sockClient.C!ose();
此外,还要删除相应的CSocket对象,释放资源。 |