Winsock 函數簡介

1、WSAStartup函數用於初始化Winsock [聲明] nt WSAStarup(WORD wVersionRequested,LPWSADATA lpWSAData); [參數] wVersionRequested - 要求使用Winsock的最低版本號 lpWSAData - Winsock的詳細資料 [返回值] 當函數成功調用時返回0 失敗時返回非0的值

2、socket函數用於生成socket(soket Descriptor) [聲明] SOCKET socket(int af,int type,int protocol); [參數] af - 地址家族(通常使用:AF_INET) type - socket的擲?br> SOCK_STREAM : 用於TCP協議 SOCK_DGRAM : 用於UDP協議 protocol - 所使用的協議 [返回值] 當函數成功調用時返回一個新的SOCKET(Socket Descriptor) 失敗時返回INVALID_SOCKET.

3、inet_addr函數把好象"xxx.xxx.xxx.xxx"的10進制的IP地址轉換爲32位整數表示方法 [聲明] unsigned long inet_addr ( const char FAR *cp ); [參數] cp - 指向用"xxx.xxx.xxx.xxx"的10進制來表示的IP地址字符串的指針 [返回值] 當函數成功調用時返回用32位整數表示的IP地址(按網絡字節排列順序) 失敗時返回INADDR_NONE.

4、gethostbyname函數可以從主機名獲取主機資料. [聲明] struct hostent FAR * gethostbyname ( const char FAR *name ); [參數] name - 指向主機名字符串的指針 [返回值] 當函數成功調用時返回主機信息失敗時返回NULL(空值)

5、Bind函數指定本地IP地址所使用的端口號時候使用 [聲明] int bind ( SOCKET s , const struct sockaddr FAR *addr , int namelen ); [參數] s - 指向用Socket函數生成的Socket Descriptor addr - 指向Socket地址的指針 namelen - 該地址的長度. [返回值] 當函數成功調用時返回0 調用失敗時返回 SOCKET_ERROR

6、connect函數用於與服務器建立連接,發出連接請求,必須在參數中指定服務器的IP地址和端口號 [聲明] int connect (SOCKET s , const struct sockaddr FAR *name , int namelen ); [參數] s - 指向用Socket函數生成的Socket Descriptor name - 指向服務器地址的指針 namelen - 該地址的長度. [返回值] 當函數成功調用時返回0 調用失敗時返回 SOCKET_ERROR

7、select函數可以用於調查一個或多個SOCKET的狀態. [聲明] int select ( int nfds , fd_set FAR *readfds , fd_set FAR *writefds , fd_set FAR *exceptfds , const struct timeval FAR *timeout ); [參數] nfds - 在WINDOWS SOCKET API 中該參數可以忽略,通常賦予NILL值 readfds - 由於接受的SOCKET設備的指針 writefds - 用於發送數據的SOCKET設備的指針 exceptfds - 檢查錯誤的狀態 timeout - 超時設定 [返回值] 返回大於0的值時,表示與條件相符的SOCKET數返回0表示超時失敗時返回SOCKET_ERROR 8、recv函數利用Socket進行接受數據. [聲明] int recv ( SOCKET s , char FAR *buf , int len , int flags ); [參數] s - 指向用Socket函數生成的Socket Descriptor buf - 接受數據的緩衝區(數組)的指針 len - 緩衝區的大小 flag - 調用方式(MSG_PEEK 或 MSG_OOB) [返回值] 成功時返回收到的字節數. 如果連接被中斷則返回0 失敗時返回 SOCKET_ERROR

9、sendto函數利用Socket進行發送數據. [聲明] int sendto ( SOCKET s , const char FAR *buf , int len , int flags , const struct sockaddr FAR *to , int token ); [參數] s - 指向用Socket函數生成的Socket Descriptor buf - 接受數據的緩衝區(數組)的指針 len - 緩衝區的大小 flag - 調用方式(MSG_DONTROUTE , MSG_OOB) to - 指向發送方SOCKET地址的指針 token - 發送方SOCKET地址的大小 [返回值] 成功時返回已經發送的字節數. 失敗時返回SOCKET_ERROR Winsock開發網絡通信程序的經典入門

 

對於許多初學者來說,網絡通信程序的開發,普遍的一個現象就是覺得難以入手。許多概念,諸如:同步(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; //初始化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編網絡通信程序的機制有了一定的瞭解。接下來你可以進行更精彩的編程, 不僅可以在網上傳輸普通數據,而且還以傳輸語音、視頻數據,你還可以自己做一個網絡資源共享的服務器軟件,和你的同學在實驗室的局域網裏可以共同分享你的成果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章