日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

WinSock学习笔记

發布時間:2024/4/11 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 WinSock学习笔记 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Socket(套接字)
◆先看定義:
typedef?unsigned?int?u_int;
typedef?u_int?SOCKET;
◆Socket相當于進行網絡通信兩端的插座,只要對方的Socket和自己的Socket有通信聯接,雙方就可以發送和接收數據了。其定義類似于文件句柄的定義。

◆Socket有五種不同的類型:
1、流式套接字(stream?socket)
定義:
#define?SOCK_STREAM?1?
流式套接字提供了雙向、有序的、無重復的以及無記錄邊界的數據流服務,適合處理大量數據。它是面向聯結的,必須建立數據傳輸鏈路,同時還必須對傳輸的數據進行驗證,確保數據的準確性。因此,系統開銷較大。

2、?數據報套接字(datagram?socket)
定義:
#define?SOCK_DGRAM?2?
數據報套接字也支持雙向的數據流,但不保證傳輸數據的準確性,但保留了記錄邊界。由于數據報套接字是無聯接的,例如廣播時的聯接,所以并不保證接收端是否正在偵聽。數據報套接字傳輸效率比較高。

3、原始套接字(raw-protocol?interface)
定義:
#define?SOCK_RAW?3?
原始套接字保存了數據包中的完整IP頭,前面兩種套接字只能收到用戶數據。因此可以通過原始套接字對數據進行分析。
其它兩種套接字不常用,這里就不介紹了。

◆Socket開發所必須需要的文件(以WinSock?V2.0為例):

頭文件:Winsock2.h
庫文件:WS2_32.LIB
動態庫:W32_32.DLL

一些重要的定義

1、數據類型的基本定義:這個大家一看就懂。
typedef?unsigned?char?u_char;
typedef?unsigned?short?u_short;
typedef?unsigned?int?u_int;
typedef?unsigned?long?u_long;
2、?網絡地址的數據結構,有一個老的和一個新的的,請大家留意,如果想知道為什么,
請發郵件給Bill?Gate。其實就是計算機的IP地址,不過一般不用用點分開的IP地
址,當然也提供一些轉換函數。

◆?舊的網絡地址結構的定義,為一個4字節的聯合:
struct?in_addr?
?{
?union?
?{
?struct?{?u_char?s_b1,s_b2,s_b3,s_b4;?}?S_un_b;
?struct?{?u_short?s_w1,s_w2;?}?S_un_w;
?u_long?S_addr;
?}?S_un;
?#define?s_addr?S_un.S_addr?/*?can?be?used?for?most?tcp?&?ip?code?*/
?//下面幾行省略,反正沒什么用處。
?};
其實完全不用這么麻煩,請看下面:

◆?新的網絡地址結構的定義:
非常簡單,就是一個無符號長整數?unsigned?long。舉個例子:IP地址為127.0.0.1的網絡地址是什么呢?請看定義:
#define?INADDR_LOOPBACK?0x7f000001

3、?套接字地址結構
(1)、sockaddr結構:
struct?sockaddr?{
?u_short?sa_family;?/*?address?family?*/
?char?sa_data[14];?/*?up?to?14?bytes?of?direct?address?*/
?};
sa_family?為網絡地址類型,一般為AF_INET,表示該socket在Internet域中進行通信,該地址結構隨選擇的協議的不同而變化,因此一般情況下另一個?與該地址結構大小相同的sockaddr_in結構更為常用,sockaddr_in結構用來標識TCP/IP協議下的地址。換句話說,這個結構是通用?socket地址結構,而下面的sockaddr_in是專門針對Internet域的socket地址結構。

(2)、sockaddr_in結構
struct?sockaddr_in?{
?short?sin_family;
?u_short?sin_port;
?struct?in_addr?sin_addr;
?char?sin_zero[8];
};
sin?_family?為網絡地址類型,必須設定為AF_INET。sin_port為服務端口,注意不要使用已固定的服務端口,如HTTP的端口80等。如果端口設置為0,則?系統會自動分配一個唯一端口。sin_addr為一個unsigned?long的IP地址。sin_zero為填充字段,純粹用來保證結構的大小。

◆?將常用的用點分開的IP地址轉換為unsigned?long類型的IP地址的函數:
unsigned?long?inet_addr(const?char?FAR?*?cp?)
用法:
unsigned?long?addr=inet_addr("192.1.8.84")

◆?如果將sin_addr設置為INADDR_ANY,則表示所有的IP地址,也即所有的計算機。
#define?INADDR_ANY?(u_long)0x00000000

4、?主機地址:

先看定義:
struct?hostent?{
?char?FAR?*?h_name;?/*?official?name?of?host?*/
?char?FAR?*?FAR?*?h_aliases;?/*?alias?list?*/
?short?h_addrtype;?/*?host?address?type?*/
?short?h_length;?/*?length?of?address?*/
?char?FAR?*?FAR?*?h_addr_list;?/*?list?of?addresses?*/
?#define?h_addr?h_addr_list[0]?/*?address,?for?backward?compat?*/
?};
h_name為主機名字。
h_aliases為主機別名列表。
h_addrtype為地址類型。
h_length為地址類型。
h_addr_list為IP地址,如果該主機有多個網卡,就包括地址的列表。
另外還有幾個類似的結構,這里就不一一介紹了。

5、?常見TCP/IP協議的定義:
#define?IPPROTO_IP?0?
#define?IPPROTO_ICMP?1?
#define?IPPROTO_IGMP?2?
#define?IPPROTO_TCP?6?
#define?IPPROTO_UDP?17?
#define?IPPROTO_RAW?255?
具體是什么協議,大家一看就知道了。

套接字的屬性

為了靈活使用套接字,我們可以對它的屬性進行設定。
1、?屬性內容:
//允許調試輸出
#define?SO_DEBUG?0x0001?/*?turn?on?debugging?info?recording?*/
//是否監聽模式
#define?SO_ACCEPTCONN?0x0002?/*?socket?has?had?listen()?*/
//套接字與其他套接字的地址綁定
#define?SO_REUSEADDR?0x0004?/*?allow?local?address?reuse?*/
//保持連接
#define?SO_KEEPALIVE?0x0008?/*?keep?connections?alive?*/
//不要路由出去
#define?SO_DONTROUTE?0x0010?/*?just?use?interface?addresses?*/
//設置為廣播
#define?SO_BROADCAST?0x0020?/*?permit?sending?of?broadcast?msgs?*/
//使用環回不通過硬件
#define?SO_USELOOPBACK?0x0040?/*?bypass?hardware?when?possible?*/
//當前拖延值
#define?SO_LINGER?0x0080?/*?linger?on?close?if?data?present?*/
//是否加入帶外數據
#define?SO_OOBINLINE?0x0100?/*?leave?received?OOB?data?in?line?*/
//禁用LINGER選項
#define?SO_DONTLINGER?(int)(~SO_LINGER)
//發送緩沖區長度
#define?SO_SNDBUF?0x1001?/*?send?buffer?size?*/
//接收緩沖區長度
#define?SO_RCVBUF?0x1002?/*?receive?buffer?size?*/
//發送超時時間
#define?SO_SNDTIMEO?0x1005?/*?send?timeout?*/
//接收超時時間
#define?SO_RCVTIMEO?0x1006?/*?receive?timeout?*/
//錯誤狀態
#define?SO_ERROR?0x1007?/*?get?error?status?and?clear?*/
//套接字類型
#define?SO_TYPE?0x1008?/*?get?socket?type?*/

2、?讀取socket屬性:
int?getsockopt(SOCKET?s,?int?level,?int?optname,?char?FAR?*?optval,?int?FAR?*?optlen)
s為欲讀取屬性的套接字。level為套接字選項的級別,大多數是特定協議和套接字專有的。如IP協議應為?IPPROTO_IP。

optname為讀取選項的名稱
optval為存放選項值的緩沖區指針。
optlen為緩沖區的長度
用法:
int?ttl=0;?//讀取TTL值
int?rc?=?getsockopt(?s,?IPPROTO_IP,?IP_TTL,?(char?*)&ttl,?sizeof(ttl));
//來自MS?platform?SDK?2003

3、?設置socket屬性:
int?setsockopt(SOCKET?s,int?level,?int?optname,const?char?FAR?*?optval,?int?optlen)
s為欲設置屬性的套接字。
level為套接字選項的級別,用法同上。
optname為設置選項的名稱
optval為存放選項值的緩沖區指針。
optlen為緩沖區的長度

用法:
int?ttl=32;?//設置TTL值
int?rc?=?setsockopt(?s,?IPPROTO_IP,?IP_TTL,?(char?*)&ttl,?sizeof(ttl));

套接字的使用步驟
1、啟動Winsock:對Winsock?DLL進行初始化,協商Winsock的版本支持并分配必要的
資源。(服務器端和客戶端)
int?WSAStartup(?WORD?wVersionRequested,?LPWSADATA?lpWSAData?)
wVersionRequested為打算加載Winsock的版本,一般如下設置:
wVersionRequested=MAKEWORD(2,0)
或者直接賦值:wVersionRequested=2

LPWSADATA為初始化Socket后加載的版本的信息,定義如下:
typedef?struct?WSAData?{
?WORD?wVersion;
?WORD?wHighVersion;
?char?szDescription[WSADESCRIPTION_LEN+1];
?char?szSystemStatus[WSASYS_STATUS_LEN+1];
?unsigned?short?iMaxSockets;
?unsigned?short?iMaxUdpDg;
?char?FAR?*?lpVendorInfo;
?}?WSADATA,?FAR?*?LPWSADATA;
如果加載成功后數據為:

wVersion=2?表示加載版本為2.0。
wHighVersion=514?表示當前系統支持socket最高版本為2.2。
szDescription="WinSock?2.0"
szSystemStatus="Running"?表示正在運行。
iMaxSockets=0?表示同時打開的socket最大數,為0表示沒有限制。
iMaxUdpDg=0?表示同時打開的數據報最大數,為0表示沒有限制。
lpVendorInfo?沒有使用,為廠商指定信息預留。

該函數使用方法:
WORD?wVersion=MAKEWORD(2,0);
WSADATA?wsData;
int?nResult=?WSAStartup(wVersion,&wsData);
if(nResult?!=0)
{
//錯誤處理
}

2、創建套接字:(服務器端和客戶端)
SOCKET?socket(?int?af,?int?type,?int?protocol?);
af為網絡地址類型,一般為AF_INET,表示在Internet域中使用。
type為套接字類型,前面已經介紹了。
protocol為指定網絡協議,一般為IPPROTO_IP。
用法:
SOCKET?sock=socket(AF_INET,SOCK_STREAM,IPPROTO_IP);
if(sock==INVALID_SOCKET)
{
//錯誤處理
}
3、套接字的綁定:將本地地址綁定到所創建的套接字上。(服務器端和客戶端)
int?bind(?SOCKET?s,?const?struct?sockaddr?FAR?*?name,?int?namelen?)
s為已經創建的套接字。
name為socket地址結構,為sockaddr結構,如前面討論的,我們一般使用sockaddr_in
結構,在使用再強制轉換為sockaddr結構。
namelen為地址結構的長度。
用法:
sockaddr_in?addr;
addr.?sin_family=AF_INET;
addr.?sin_port=?htons(0);?//保證字節順序
addr.?sin_addr.s_addr=?inet_addr("192.1.8.84")
int?nResult=bind(s,(sockaddr*)&addr,sizeof(sockaddr));
if(nResult==SOCKET_ERROR)
{
//錯誤處理
}

4、?套接字的監聽:(服務器端)
int?listen(SOCKET?s,?int?backlog?)
s為一個已綁定但未聯接的套接字。
backlog為指定正在等待聯接的最大隊列長度,這個參數非常重要,因為服務器一般可
以提供多個連接。
用法:
int?nResult=listen(s,5)?//最多5個連接
if(nResult==SOCKET_ERROR)
{
//錯誤處理
}

5、套接字等待連接::(服務器端)
SOCKET?accept(?SOCKET?s,?struct?sockaddr?FAR?*?addr,?int?FAR?*?addrlen?)
s為處于監聽模式的套接字。
sockaddr為接收成功后返回客戶端的網絡地址。
addrlen為網絡地址的長度。

用法:
sockaddr_in?addr;
SOCKET?s_d=accept(s,(sockaddr*)&addr,sizeof(sockaddr));
if(s==INVALID_SOCKET)
{
//錯誤處理
}

6、套接字的連結:將兩個套接字連結起來準備通信。(客戶端)
int?connect(SOCKET?s,?const?struct?sockaddr?FAR?*?name,?int?namelen?)
s為欲連結的已創建的套接字。
name為欲連結的socket地址。
namelen為socket地址的結構的長度。

用法:
sockaddr_in?addr;
addr.?sin_family=AF_INET;
addr.?sin_port=htons(0);?//保證字節順序
addr.?sin_addr.s_addr=?htonl(INADDR_ANY)?//保證字節順序
int?nResult=connect(s,(sockaddr*)&addr,sizeof(sockaddr));
if(nResult==SOCKET_ERROR)
{
//錯誤處理
}

7、套接字發送數據:(服務器端和客戶端)
int?send(SOCKET?s,?const?char?FAR?*?buf,?int?len,?int?flags?)
s為服務器端監聽的套接字。
buf為欲發送數據緩沖區的指針。
len為發送數據緩沖區的長度。
flags為數據發送標記。
返回值為發送數據的字符數。

◆這里講一下這個發送標記,下面8中討論的接收標記也一樣:

flag取值必須為0或者如下定義的組合:0表示沒有特殊行為。
#define?MSG_OOB?0x1?/*?process?out-of-band?data?*/
#define?MSG_PEEK?0x2?/*?peek?at?incoming?message?*/
#define?MSG_DONTROUTE?0x4?/*?send?without?using?routing?tables?*/
MSG_OOB表示數據應該帶外發送,所謂帶外數據就是TCP緊急數據。
MSG_PEEK表���使有用的數據復制到緩沖區內,但并不從系統緩沖區內刪除。
MSG_DONTROUTE表示不要將包路由出去。

用法:
char?buf[]="xiaojin";
int?nResult=send(s,buf,strlen(buf));
if(nResult==SOCKET_ERROR)
{
//錯誤處理
}

8、?套接字的數據接收:(客戶端)
int?recv(?SOCKET?s,?char?FAR?*?buf,?int?len,?int?flags?)
s為準備接收數據的套接字。
buf為準備接收數據的緩沖區。
len為準備接收數據緩沖區的大小。
flags為數據接收標記。
返回值為接收的數據的字符數。

用法:
char?mess[1000];
int?nResult?=recv(s,mess,1000,0);
if(nResult==SOCKET_ERROR)
{
//錯誤處理
}

9、中斷套接字連接:通知服務器端或客戶端停止接收和發送數據。(服務器端和客戶端)
int?shutdown(SOCKET?s,?int?how)
s為欲中斷連接的套接字。
How為描述禁止哪些操作,取值為:SD_RECEIVE、SD_SEND、SD_BOTH。
#define?SD_RECEIVE?0x00
#define?SD_SEND?0x01
#define?SD_BOTH?0x02
用法:
int?nResult=?shutdown(s,SD_BOTH);
if(nResult==SOCKET_ERROR)
{
//錯誤處理
}

10、?關閉套接字:釋放所占有的資源。(服務器端和客戶端)
int?closesocket(?SOCKET?s?)
s為欲關閉的套接字。

用法:
int?nResult=closesocket(s);
if(nResult==SOCKET_ERROR)
{
//錯誤處理
}

與socket有關的一些函數介紹

1、讀取當前錯誤值:每次發生錯誤時,如果要對具體問題進行處理,那么就應該調用這個函數取得錯誤代碼。?
?int?WSAGetLastError(void?);
?#define?h_errno?WSAGetLastError()
錯誤值請自己閱讀Winsock2.h。

2、將主機的unsigned?long值轉換為網絡字節順序(32位):為什么要這樣做呢?因為不同的計算機使用不同的字節順序存儲數據。因此任何從Winsock函數對IP地址和端口號的引用和傳給Winsock函數的IP地址和端口號均時按照網絡順序組織的。
?
?u_long?htonl(u_long?hostlong);
?舉例:htonl(0)=0
?htonl(80)=?1342177280

3、將unsigned?long數從網絡字節順序轉換位主機字節順序,是上面函數的逆函數。?
?
?u_long?ntohl(u_long?netlong);
?舉例:ntohl(0)=0
?ntohl(1342177280)=?80

4、將主機的unsigned?short值轉換為網絡字節順序(16位):原因同2:?
?
?u_short?htons(u_short?hostshort);
?舉例:htonl(0)=0
?htonl(80)=?20480

5、將unsigned?short數從網絡字節順序轉換位主機字節順序,是上面函數的逆函數。?
?u_short?ntohs(u_short?netshort);
?舉例:ntohs(0)=0
?ntohsl(20480)=?80

6、將用點分割的IP地址轉換位一個in_addr結構的地址,這個結構的定義見筆記(一),實際上就是一個unsigned?long值。計算機內部處理IP地址可是不認識如192.1.8.84之類的數據。?
?unsigned?long?inet_addr(?const?char?FAR?*?cp?);
?舉例:inet_addr("192.1.8.84")=1409810880
?inet_addr("127.0.0.1")=?16777343
如果發生錯誤,函數返回INADDR_NONE值。

7、將網絡地址轉換位用點分割的IP地址,是上面函數的逆函數。?
?char?FAR?*?inet_ntoa(?struct?in_addr?in?);
?舉例:char?*?ipaddr=NULL;
?char?addr[20];
?in_addr?inaddr;
?inaddr.?s_addr=16777343;
?ipaddr=?inet_ntoa(inaddr);
?strcpy(addr,ipaddr);?
這樣addr的值就變為127.0.0.1。
注意意不要修改返回值或者進行釋放動作。如果函數失敗就會返回NULL值。

8、獲取套接字的本地地址結構:?
?int?getsockname(SOCKET?s,?struct?sockaddr?FAR?*?name,?int?FAR?*?namelen?);
s為套接字
name為函數調用后獲得的地址值
namelen為緩沖區的大小。

9、獲取與套接字相連的端地址結構:
?int?getpeername(SOCKET?s,?struct?sockaddr?FAR?*?name,?int?FAR?*?namelen?);
s為套接字
name為函數調用后獲得的端地址值
namelen為緩沖區的大小。

10、獲取計算機名:
?int?gethostname(?char?FAR?*?name,?int?namelen?);
name是存放計算機名的緩沖區
namelen是緩沖區的大小
用法:
?char?szName[255];
?memset(szName,0,255);
?if(gethostname(szName,255)==SOCKET_ERROR)
?{
?????//錯誤處理
?}
返回值為:szNmae="xiaojin"

11、根據計算機名獲取主機地址:?
?struct?hostent?FAR?*?gethostbyname(?const?char?FAR?*?name?);
name為計算機名。
用法:
?hostent?*?host;
?char*?ip;
?host=?gethostbyname("xiaojin");
?if(host->h_addr_list[0])
?{
???struct?in_addr?addr;
????memmove(&addr,?host->h_addr_list[0],4);
???//獲得標準IP地址
???ip=inet_?ntoa?(addr);
?}

返回值為:hostent->h_name="xiaojin"
hostent->h_addrtype=2?//AF_INET
hostent->length=4
ip="127.0.0.1"

Winsock?的I/O操作:

1、?兩種I/O模式?
阻塞模式:執行I/O操作完成前會一直進行等待,不會將控制權交給程序。套接字?默認為阻塞模式。可以通過多線程技術進行處理。?
非阻塞模式:執行I/O操作時,Winsock函數會返回并交出控制權。這種模式使用?起來比較復雜,因為函數在沒有運行完成就進行返回,會不斷地返回?WSAEWOULDBLOCK錯誤。但功能強大。
為了解決這個問題,提出了進行I/O操作的一些I/O模型,下面介紹最常見的三種:

2、select模型:
  通過調用select函數可以確定一個或多個套接字的狀態,判斷套接字上是否有數據,或
者能否向一個套接字寫入數據。?
int?select(?int?nfds,?fd_set?FAR?*?readfds,?fd_set?FAR?*?writefds,?
?fd_set?FAR?*exceptfds,?const?struct?timeval?FAR?*?timeout?);

◆先來看看涉及到的結構的定義:
a、?d_set結構:
#define?FD_SETSIZE?64?
typedef?struct?fd_set?{
?u_int?fd_count;?/*?how?many?are?SET??*/
?SOCKET?fd_array[FD_SETSIZE];?/*?an?array?of?SOCKETs?*/
?}?fd_set;

fd_count為已設定socket的數量
fd_array為socket列表,FD_SETSIZE為最大socket數量,建議不小于64。這是微軟建
議的。

B、timeval結構:?
struct?timeval?{
?long?tv_sec;?/*?seconds?*/
?long?tv_usec;?/*?and?microseconds?*/
?};
tv_sec為時間的秒值。
tv_usec為時間的毫秒值。
這個結構主要是設置select()函數的等待值,如果將該結構設置為(0,0),則select()函數
會立即返回。

◆再來看看select函數各參數的作用:?
nfds:沒有任何用處,主要用來進行系統兼容用,一般設置為0。
readfds:等待可讀性檢查的套接字組。
writefds;等待可寫性檢查的套接字組。
exceptfds:等待錯誤檢查的套接字組。
timeout:超時時間。

函數失敗的返回值:
調用失敗返回SOCKET_ERROR,超時返回0。
readfds、writefds、exceptfds三個變量至少有一個不為空,同時這個不為空的套接字組
種至少有一個socket,道理很簡單,否則要select干什么呢。?

舉例:測試一個套接字是否可讀:
fd_set?fdread;
//FD_ZERO定義
//?#define?FD_ZERO(set)?(((fd_set?FAR?*)(set))->fd_count=0)
FD_ZERO(&fdread);
FD_SET(s,&fdread);?//加入套接字,詳細定義請看winsock2.h
if(select(0,%fdread,NULL,NULL,NULL)>0
{
??//成功
??if(FD_ISSET(s,&fread)?//是否存在fread中,詳細定義請看winsock2.h
??{
????//是可讀的
??}
}

◆I/O操作函數:主要用于獲取與套接字相關的操作參數。?
?int?ioctlsocket(SOCKET?s,?long?cmd,?u_long?FAR?*?argp?);?
s為I/O操作的套接字。
cmd為對套接字的操作命令。
argp為命令所帶參數的指針。

常見的命令:?//確定套接字自動讀入的數據量
#define?FIONREAD?_IOR(''''f'''',?127,?u_long)?/*?get?#?bytes?to?read?*/
//允許或禁止套接字的非阻塞模式,允許為非0,禁止為0
#define?FIONBIO?_IOW(''''f'''',?126,?u_long)?/*?set/clear?non-blocking?i/o?*/
//確定是否所有帶外數據都已被讀入
#define?SIOCATMARK?_IOR(''''s'''',?7,?u_long)?/*?at?oob?mark??*/

3、WSAAsynSelect模型:
WSAAsynSelect模型也是一個常用的異步I/O模型。應用程序可以在一個套接字上接收以
WINDOWS消息為基礎的網絡事件通知。該模型的實現方法是通過調用WSAAsynSelect函
數?自動將套接字設置為非阻塞模式,并向WINDOWS注冊一個或多個網絡時間,并提供一
個通知時使用的窗口句柄。當注冊的事件發生時,對應的窗口將收到一個基于消息的通知。
?int?WSAAsyncSelect(?SOCKET?s,?HWND?hWnd,?u_int?wMsg,?long?lEvent);?
s為需要事件通知的套接字
hWnd為接收消息的窗口句柄
wMsg為要接收的消息
lEvent為掩碼,指定應用程序感興趣的網絡事件組合,主要如下:?
#define?FD_READ_BIT?0
#define?FD_READ?(1?<<?FD_READ_BIT)
#define?FD_WRITE_BIT?1
#define?FD_WRITE?(1?<<?FD_WRITE_BIT)
#define?FD_OOB_BIT?2
#define?FD_OOB?(1?<<?FD_OOB_BIT)
#define?FD_ACCEPT_BIT?3
#define?FD_ACCEPT?(1?<<?FD_ACCEPT_BIT)
#define?FD_CONNECT_BIT?4
#define?FD_CONNECT?(1?<<?FD_CONNECT_BIT)
#define?FD_CLOSE_BIT?5
#define?FD_CLOSE?(1?<<?FD_CLOSE_BIT)
用法:要接收讀寫通知:
int?nResult=?WSAAsyncSelect(s,hWnd,wMsg,FD_READ|FD_WRITE);
if(nResult==SOCKET_ERROR)
{
??//錯誤處理
}
取消通知:
?int?nResult=?WSAAsyncSelect(s,hWnd,0,0);?
當應用程序窗口hWnd收到消息時,wMsg.wParam參數標識了套接字,lParam的低字標明
了網絡事件,高字則包含錯誤代碼。

4、WSAEventSelect模型
WSAEventSelect模型類似WSAAsynSelect模型,但最主要的區別是網絡事件發生時會被發
送到一個事件對象句柄,而不是發送到一個窗口。

使用步驟如下:
a、?創建事件對象來接收網絡事件:
#define?WSAEVENT?HANDLE
#define?LPWSAEVENT?LPHANDLE
WSAEVENT?WSACreateEvent(?void?);
該函數的返回值為一個事件對象句柄,它具有兩種工作狀態:已傳信(signaled)和未傳信
(nonsignaled)以及兩種工作模式:人工重設(manual?reset)和自動重設(auto?reset)。默認未
未傳信的工作狀態和人工重設模式。

b、將事件對象與套接字關聯,同時注冊事件,使事件對象的工作狀態從未傳信轉變未
已傳信。
?int?WSAEventSelect(?SOCKET?s,WSAEVENT?hEventObject,long?lNetworkEvents?);?
s為套接字
hEventObject為剛才創建的事件對象句柄
lNetworkEvents為掩碼,定義如上面所述

c、I/O處理后,設置事件對象為未傳信
BOOL?WSAResetEvent(?WSAEVENT?hEvent?);
Hevent為事件對象

成功返回TRUE,失敗返回FALSE。

d、等待網絡事件來觸發事件句柄的工作狀態:
DWORD?WSAWaitForMultipleEvents(?DWORD?cEvents,
const?WSAEVENT?FAR?*?lphEvents,?BOOL?fWaitAll,
DWORD?dwTimeout,?BOOL?fAlertable?);
lpEvent為事件句柄數組的指針
cEvent為為事件句柄的數目,其最大值為WSA_MAXIMUM_WAIT_EVENTS?
fWaitAll指定等待類型:TRUE:當lphEvent數組重所有事件對象同時有信號時返回;
FALSE:任一事件有信號就返回。
dwTimeout為等待超時(毫秒)
fAlertable為指定函數返回時是否執行完成例程

對事件數組中的事件進行引用時,應該用WSAWaitForMultipleEvents的返回值,減去
預聲明值WSA_WAIT_EVENT_0,得到具體的引用值。例如:
nIndex=WSAWaitForMultipleEvents(…);
MyEvent=EventArray[Index-?WSA_WAIT_EVENT_0];

e、判斷網絡事件類型:
int?WSAEnumNetworkEvents(?SOCKET?s,
WSAEVENT?hEventObject,?LPWSANETWORKEVENTS?lpNetworkEvents?);
s為套接字
hEventObject為需要重設的事件對象
lpNetworkEvents為記錄網絡事件和錯誤代碼,其結構定義如下:
typedef?struct?_WSANETWORKEVENTS?{
??long?lNetworkEvents;
??int?iErrorCode[FD_MAX_EVENTS];
}?WSANETWORKEVENTS,?FAR?*?LPWSANETWORKEVENTS;

f、關閉事件對象句柄:
BOOL?WSACloseEvent(WSAEVENT?hEvent);
調用成功返回TRUE,否則返回FALSE。

總結

以上是生活随笔為你收集整理的WinSock学习笔记的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。