)Winsock API 編程介紹

來源:互聯網
上載者:User

相信很多人都對網路編程感興趣,下面我們就來介紹,在網路編程
中應用最廣泛的編程介面Winsock API.

使用Winsock API的編程,應該瞭解一些TCP/IP的基礎知識.雖然你可
以直接使用Winsock API來寫網路應用程式,但是,要寫出優秀的網路
應用程式,還是必須對TCP/IP協議有一些瞭解的.

1. TCP/IP協議與Winsock網路編程介面的關係.

在開始之前,我們先說一下Winsock和TCP/IP到底是什麼關係.

我碰到很多人問我:怎樣使用Winsock協議編程? 其實,這話說的有點
錯誤,Winsock並不是一種網路通訊協定,他只是一個網路編程介面,也就
是說,他不是協議,但是他可以訪問很多種網路通訊協定,你可以把他當作
一些協議的封裝.現在的Winsock已經基本上實現了與協議無關.你可
以使用Winsock來調用多種協議的功能.

那麼,Winsock和TCP/IP協議到底是什麼關係呢?實際上,Winsock就是
tcp/ip協議的一種封裝,你可以通過調用winsock的介面函數來調用
tcp/ip的各種功能.例如我想用Tcp/ip協議發送資料,你就可以使用
winsock的介面函數send()來調用tcp/ip的發送資料功能,至於具體
怎麼發送資料,Winsock已經幫你封裝好了這種功能.

2.TCP/IP協議介紹

現在來介紹一些tcp/ip的原理.tcp/ip協議包含的範圍非常的廣,他
是一種四層協議,包含了各種,硬體軟體需求的定義,我們這裡只介紹
軟體方面的知識.tcp/ip協議確切的說法應該是tcp/udp/ip協議.

udp協議(User Datagram Protocol 使用者資料包通訊協定).是一種保護消
息邊界的,不保障可靠資料的傳輸.
tcp協議(Transmission Control Protocol 傳輸控制通訊協定).是一種
流傳輸的協議.他提供可靠的,有序的,雙向的,連線導向的傳輸.

3.保護訊息邊界和流

那麼什麼是保護訊息邊界和流呢?

保護訊息邊界,就是指傳輸協議把資料當作一條獨立的訊息在網上
傳輸,接收端只能接收獨立的訊息.也就是說存在保護訊息邊界,接收
端一次只能接收發送端發出的一個資料包.
而面向流則是指無保護訊息保護邊界的,如果發送端連續發送資料,
接收端有可能在一次接收動作中,會接收兩個或者更多的資料包.

我們舉個例子來說,例如,我們連續發送三個資料包,大小分別是2k,
4k , 8k,這三個資料包,都已經到達了接收端的網路堆棧中,如果使
用UDP協議,不管我們使用多大的接收緩衝區去接收資料,我們必須有
三次接收動作,才能夠把所有的資料包接收完.而使用TCP協議,我們
只要把接收的緩衝區大小設定在14k以上,我們就能夠一次把所有的
資料包接收下來.只需要有一次接收動作.

這就是因為UDP協議的保護訊息邊界使得每一個訊息都是獨立的.而
流傳輸,卻把資料當作一串資料流,他不認為資料是一個一個的訊息.

所以有很多人在使用tcp協議通訊的時候,並不清楚tcp是基於流的
傳輸,當連續發送資料的時候,他們時常會認識tcp會丟包.其實不然,
因為當他們使用的緩衝區足夠大時,他們有可能會一次接收到兩個甚
至更多的資料包,而很多人往往會忽視這一點,只解析檢查了第一個
資料包,而已經接收的其他資料包卻被忽略了.所以大家如果要作這
類的網路編程的時候,必須要注意這一點.

4。Winsock編程簡單流程

下面我們介紹一下Win32平台的Winsock編程方法.
通訊則必須有伺服器端,和用戶端.
我們簡單介紹tcp伺服器端的大體流程.

對於任何基於Winsock的編程首先我們必須要初始化Winsock DLL庫.
int WSAStarup( WORD wVersionRequested , LPWSADATA lpWsAData ).
wVersionRequested是我們要求使用的Winsock的版本.
調用這個介面函數可以幫我們初始化Winsock .然後我們必須建立一
個通訊端(socket).
SOCKET socket( int af , int type , int protocol );
通訊端可以說是Winsock通訊的核心.Winsock通訊的所有資料轉送,
都是通過通訊端來完成的,通訊端包含了兩個資訊,一個是IP地址,一
個是Port連接埠號碼,使用這兩個資訊,我們就可以確定網路中的任何一
個通訊節點.

當我們調用了socket()介面函數建立了一個通訊端後,我們必須把套
接字與你需要進行通訊的地址建立聯絡,我們可以通過綁定函數來實
現這種聯絡.
int bind(SOCKET s , const struct sockaddr FAR* name , int namelen ) ;
struct sockaddr_in
{
short           sin_family ;
u_short           sin_prot ;
struct in_addr    sin_addr ;
char              sin_sero[8] ;
}
就包含了我們需要建立串連的本地的地址,包括,地址族,ip和連接埠信
息.sin_family欄位我們必須把他設為AF_INET,這是告訴Winsock使
用的是IP地址族.sin_prot    就是我們要用來通訊的連接埠號碼.sin_addr
就是我們要用來通訊的ip地址資訊.

在這裡,必須還得提一下有關'大頭(big-endian)'小頭(little-endian)'.
因為各種不同的電腦處理資料時的方法是不一樣的,Intel 86處理
器上是用'小頭'形勢來表示多位元組的編號,就是把低位元組放在前面,
把高位元組放在後面,而互連網標準卻正好相反,所以,我們必須把主機
位元組轉換成網路位元組的順序.Winsock API提供了幾個函數.

把主機位元組轉化成網路位元組的函數;
u_long htonl( u_long hostlong );
u_short htons( u_short hostshort );
把網路位元組轉化成主機位元組的函數;
u_long ntohl( u_long netlong ) ;
u_short ntohs( u_short netshort ) ;
這樣,我們設定ip地址,和port連接埠時,就必須把主機位元組轉化成網路
位元組後,才能用bind()函數來綁定通訊端和地址.

當綁定完成之後,伺服器端必須建立一個監聽的隊列來接收用戶端的
串連請求.
int listen( SOCKET s ,int backlog );
這個函數可以讓我們把通訊端轉成監聽模式.
如果用戶端有了串連請求,我們還必須使用
int accept( SOCKET s , struct sockaddr FAR* addr , int FAR* addrlen );
來接受用戶端的請求.
現在我們基本上已經完成了一個伺服器的建立,
而用戶端的建立的流程則是初始化WinSock ,然後建立socket通訊端
,再使用
int connect( SOCKET s , const struct sockaddr FAR* name , int namelen ) ;
來串連服務端.

下面是一個最簡單的建立伺服器端和用戶端的例子:
伺服器端的建立 :
WSADATA wsd ;
SOCKET sListen ;
SOCKET sclient ;
UINT port = 800 ;
int iAddrSize ;
struct sockaddr_in local , client ;
WSAStartup( 0x11 , &wsd );
sListen = socket ( AF_INET , SOCK_STREAM , IPPOTO_IP ) ;
local.sin_family = AF_INET ;
local.sin_addr = htonl( INADDR_ANY ) ;
local.sin_port = htons( port ) ;
bind( sListen , (struct sockaddr*)&local , sizeof( local ) ) ;
listen( sListen , 5 ) ;
sClient = accept( sListen , (struct sockaddr*)&client , &iAddrSize ) ;

用戶端的建立:
WSADATA wsd ;
SOCKET sClient ;
UINT port = 800 ;
char szIp[] = "127.0.0.1" ;
int iAddrSize ;
struct sockaddr_in server ;
WSAStartup( 0x11 , &wsd );
sClient = socket ( AF_INET , SOCK_STREAM , IPPOTO_IP ) ;
server.sin_family = AF_INET ;
server.sin_addr = inet_addr( szIp ) ;
server.sin_port = htons( port );
connect( sClient , (struct sockaddr*)&server , sizeof( server ) ) ;

當伺服器端和用戶端建立串連以後,無論是用戶端,還是伺服器端都
可以使用
int send( SOCKET s , const char FAR* buf , int len , int flags );
int recv( SOCKET s , char FAR* buf , int len , int flags );
函數來接收和發送資料,因為,TCP串連是雙向的.
當要關閉通訊連結的時候,任何一方都可以調用
int shutdown( SOCKET s , int how ) ;
來關閉通訊端的指定功能。再調用
int closesocket( SOCKET s) ;
來關閉通訊端控制代碼。這樣一個通訊過程就算完成了。

注意:上面的代碼沒有任何檢查函數傳回值,如果你作網路編程就一定要
檢查任何一個Winsock API函數的調用結果,因為很多時候函數調用
並不一定成功.上面介紹的函數,傳回值類型是int的話,如果函數調
用失敗的話,返回的都是SOCKET_ERROR.

5。Winsock編程的五種模型

上面介紹的僅僅是最簡單的winsock通訊的方法,而實際中很多網路
通訊的卻很多難以解決的意外情況.

例如,Winsock提供了兩種通訊端模式:鎖定和非鎖定.當我們使用鎖
定通訊端的時候,我們使用的很多函數,例如accpet,send,recv等等,
如果沒有資料需要處理,這些函數都不會返回,也就是說,你的應用程
序會阻塞在那些函數的調用處.而 如果使用非阻塞模式,調用這些函
數,不管你有沒有資料到達,他都會返回,所以,有可能我們在非阻塞
模式裡,調用這些函數大部分的情況下會返回失敗,所以就需要我們
來處理很多的意外出錯.

這顯然不是我們想要看到的情況.我們可以採用Winsock的通訊模型
來避免這些情況的發生。

Winsock提供了五種通訊端I/O模型來解決這些問題.他們分別是
select(選擇),WSAAsyncSelect(非同步選擇),
WSAEventSelect (事件選擇), overlapped(重疊) , completion
port(完成連接埠) .

我們在這裡詳細介紹一下select,WSAASyncSelect兩種模型.

select模型是最常見的I/O模型.
使用
int select( int nfds , fd_set FAR* readfds , fd_set FAR* writefds , fd_set FAR* exceptfds ,
        const struct timeval FAR * timeout ) ;
函數來檢查你要調用的socket通訊端是否已經有了需要處理的資料.
select包含三個socket隊列,分別代表:
readfds ,檢查可讀性,writefds,檢查可寫性,exceptfds,例外資料.
timeout是select函數的返回時間.
例如,我們想要檢查一個通訊端是否有資料需要接收,我們可以把套
接字控制代碼加入可讀性檢查隊列中,然後調用select,如果,該通訊端沒
有資料需要接收,select函數會把該通訊端從可讀性檢查隊列中刪除
掉,所以我們只要檢查該通訊端控制代碼是否還存在於可讀性隊列中,就
可以知道到底有沒有資料需要接收了.

Winsock提供了一些宏用來操作通訊端隊列fd_set.
FD_CLR( s,*set) 從隊列set刪除控制代碼s.
FD_ISSET( s, *set) 檢查控制代碼s是否存在與隊列set中.
FD_SET( s,*set )把控制代碼s添加到隊列set中.
FD_ZERO( *set ) 把set隊列初始化成空隊列.

WSAAsyncSelect(非同步選擇)模型:
WSAASyncSelect模型就是把一個視窗和通訊端控制代碼建立起串連,套接
字的網路事件發生時時候,就會把某個訊息發送到視窗,然後可以在
視窗的訊息響應函數中處理資料的接收和發送.
int WSAAsyncSelect( SOCKET s, HWND hWnd , unsigned int wMsg , long lEvent ) ;
這個函數可以把通訊端控制代碼和視窗建立起串連,
wMsg 是我們必須自訂的一個訊息.
lEvent就是制定的網路事件.包括FD_READ , FD_WRITE , FD_ACCEPT
, FD_CONNECT , FD_CLOSE .
幾個事件.
例如,我需要接收FD_READ , FD_WRITE , FD_CLOSE 的網路事件.可
以調用
WSAAsyncSelect( s , hWnd , WM_SOCKET , FD_READ | FD_WRITE | FD_CLOSE ) ;
這樣,當有FD_READ , FD_WRITE 或者 FD_CLOSE網路事件時,視窗
hWnd將會收到WM_SOCKET訊息,訊息參數的lParam標誌了是什麼事件
發生.

其實大家應該見過這個模型,因為MFC的CSocket類,就是使用這個模
型.

上面為大家介紹了Winsock編程的一些方法.

轉自:http://hi.baidu.com/ailiss/blog/item/75ef0c6deff49afa43169476.html

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.