要學習好網路編程,主要看以下幾個方面:
1、掌握概念,諸如:同步(Sync)/異步(Async),阻塞(Block)/非阻塞(Unblock)等。
2、在實際Windows網絡通信軟件開發中,異步非阻塞套接字是用的最多的。平常所說的C/S(客戶端/服務器)結構的軟件就是異步非阻塞模式的。
3、在異步非阻塞模式下的通信,要借助於多線程來開發。
4、許多文章都曾經介紹過用VC++進行Socket編程的方法,直接利用動態連接庫wsock32.dll進行操作,實現比較繁瑣。但實際上網絡編程概括為三種套接字方法:流式套接字(SOCK_STREAM)和數據報式套接字(SOCK_DGRAM)和原始套接字。流式套接字基於TCP協議,數據報式套接字基於UDP協議實現
網絡編程,當然要用到Windows Socket(套接字)技術。Socket相關的操作由一系列API函數來完成,比如socket、bind、listen、connect、accept、send、sendto、recv、recvfrom等。調用這些API函數有一定的先后次序,有些函數的參數還比較復雜,對於開發者來說,不是很好用。於是,微軟的MFC提供了兩個類:CAsyncSocket和CSocket,極大地方便了Socket功能的使用。
CAsyncSocket類在較低層次上封裝了Windows Socket API,並且通過內建一個(隱藏的)窗口,實現了適合Windows應用的異步機制(Windows Socket API默認情況下工作在阻塞模式,不方便直接在消息驅動的Windows程序上使用)。CSocket類從CAsyncSocket類派生,進一步簡化了Socket功能的應用。不過很遺憾,正因為這兩個類都內建了一個窗口,它們並不是線程安全的(thread-safe);如果要在多線程環境下應用Socket功能,建議自行封裝Socket API函數。
基於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、關閉套接字
異步方式指的是發送方不等接收方響應,便接着發下個數據包的通信方式;而同步指發送方發出數據后,等收到接收方發回的響應,才發下一個數據包的通信方式。
阻塞套接字是指執行此套接字的網絡調用時,直到成功才返回,否則一直阻塞在此網絡調用上,比如調用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;
//初始化TCP協議
BOOL ret = WSAStartup(MAKEWORD(2,2), &wsaData);
if(ret != 0)
{
MessageBox('初始化網絡協議失敗!');
return FALSE;
}
//創建服務器端套接字
ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(ServerSock == INVALID_SOCKET)
{
MessageBox('創建套接字失敗!');
closesocket(ServerSock);
WSACleanup();
return FALSE;
}
//綁定到本地一個端口上
sockaddr_in localaddr;
localaddr.sin_family = AF_INET;
localaddr.sin_port = htons(8888); //端口號不要與其他應用程序沖突
localaddr.sin_addr.s_addr = 0;
if(bind(ServerSock ,(struct sockaddr*)&localaddr,sizeof(sockaddr))
= = SOCKET_ERROR)
{
MessageBox('綁定地址失敗!');
closesocket(ServerSock);
WSACleanup();
return FALSE;
}
//將SeverSock設置為異步非阻塞模式,並為它注冊各種網絡異步事件,其中m_hWnd
//為應用程序的主對話框或主窗口的句柄
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)
{
//調用Winsock API函數,得到網絡事件類型
int iEvent = WSAGETSELECTEVENT(lParam);
//調用Winsock API函數,得到發生此事件的客戶端套接字
SOCKET CurSock= (SOCKET)wParam;
switch(iEvent)
{
case FD_ACCEPT: //客戶端連接請求事件
OnAccept(CurSock);
break;
case FD_CLOSE: //客戶端斷開事件:
OnClose(CurSock);
break;
case FD_READ: //網絡數據包到達事件
OnReceive(CurSock);
break;
case FD_WRITE: //發送網絡數據事件
OnSend(CurSock);
break;
default: break;
}
}
以下是發生在相應Socket上的各種網絡異步事件的處理函數,其中OnAccept傳進來的參數是服務器端創建的套接字,OnClose()、OnReceive()和OnSend()傳進來的參數均是服務器端在接受客戶端連接時新創建的用與此客戶端通信的Socket。
void CSocketSeverDlg::OnAccept(SOCKET CurSock)
{
//接受連接請求,並保存與發起連接請求的客戶端進行通信Socket
//為新的socket注冊異步事件,注意沒有Accept事件
}
void CSocketSeverDlg::OnClose(SOCET CurSock)
{
//結束與相應的客戶端的通信,釋放相應資源
}
void CSocketSeverDlg::OnSend(SOCET CurSock)
{
//在給客戶端發數據時做相關預處理
}
void CSocketSeverDlg::OnReceive(SOCET CurSock)
{
//讀出網絡緩沖區中的數據包
}
用同樣的方法建立一個客戶端應用程序。初始化網絡部分,不需要將套接字設置為監聽模式。注冊異步事件時,沒有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編網絡通信程序的機制有了一定的了解。接下來你可以進行更精彩的編程, 不僅可以在網上傳輸普通數據,而且還以傳輸語音、視頻數據,你還可以自己做一個網絡資源共享的服務器軟件,和你的同學在實驗室的局域網里可以共同分享你的成果。
同步服務器套接字掛起應用程序的執行,直到套接字上接收到連接請求。同步服務器套接字不適用於在操作中大量使用網絡的應用程序,但它們可能適用於簡單的網絡應用程序。使用 Bind 和 Listen 方法設置 Socket 以在終結點上偵聽之后,Socket 就可以隨時使用 Accept 方法接受傳入的連接請求了。應用程序被掛起,直到調用 Accept 方法時接收到連接請求。
接收到連接請求時,Accept 返回一個與連接客戶端關聯的新 Socket 實例。下面的示例讀取客戶端數據,在控制台上顯示該數據,然后將該數據回顯到客戶端。Socket 不指定任何消息協議,因此字符串“<EOF>”標記消息數據的結尾。它假定一個名為 listener 的 Socket 已初始化,並綁定到一個終結點。
Console.WriteLine("Waiting for a connection...");
Socket handler = listener.Accept();
String data = null;
while (true) {
bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes,0,bytesRec);
if (data.IndexOf("<EOF>") > -1) {
break;
}
}
Console.WriteLine( "Text received : {0}", data);
byte[] msg = Encoding.ASCII.GetBytes(data);
handler.Send(msg);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
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 適合於對字符串的通信,具體選用何種方法則取決於具體應用的需求。