windows socket編程流程

導讀:
一個簡單的TCP客戶端程序流程
1、使用WSAStartup()初始化WinSock庫。
2、使用socket()創建一個IPPROTO_TCP SOCKET。
3、使用gethostbyname()/gethostbyaddr()獲取主機信息。
4、使用connect()和我們創建的套接字連接服務器。
5、使用send()/recv()發送和接收數據,直到我們的TCP會話結束。
6、使用closesocket()關閉套接字連接。
7、使用WSACleanup()釋放WinSock。
初始化WinSock
正如其它每個WinSock程序一樣,我們需要初始化WinSock庫。這也基本上是一種檢查WinSock是否在當前系統可用的方法,對於以前的版本,我們當然希望是這樣。
int wsaret=WSAStartup(0x101,&wsaData);
if(wsaret)
     return;
創建SOCKET
套接字是一種實體,它擔當了客戶端和服務器之間的端點。當客戶端連接到服務器之後,就會存在兩個套接字——客戶端一邊的套接字和相應的服務器一邊的套接字。讓我們來稱它們爲CLIENTSOCK和SERVERSOCK。當客戶端在CLIENTSOCK使用send()時,服務器可以在SERVERSOCK使用recv()來接收客戶端所發送的數據,反之亦然。對於我們的目的,我們使用一個名爲socket()的函數來創建套接字。
SOCKET conn;
conn=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(conn==INVALID_SOCKET) 
     return;
獲取主機信息
顯然,我們在連接到主機(服務器)之前,要獲取它的信息。我們可以使用兩個函數——gethostbyname()和gethostbyaddr()。當我們擁有服務器的DNS名稱時,我們可以使用gethostbyname()函數,例如codeproject.com或ftp.myserver.org之類的名稱。當我們擁有要連接的服務器的IP地址時,可以使用gethostbyaddr()函數,例如192.168.1.1或202.54.1.100。
顯然,我們希望能使我們的最終用戶既能使用DNS名稱,也能使用IP地址。那麼,爲了這些工作對他來說透明,我們需要像下面這樣玩一個小把戲。我們對入口字符串使用inet_addr(),這個函數會把一個IP地址轉換成一個標準的網絡地址格式。這樣一來,如果它返回失敗,我們就可以知道這個字符串不是一個IP地址,如果它成功的話,我們就可以假設它是一個有效的IP地址了。
if(inet_addr(servername)==INADDR_NONE)
{
      hp=gethostbyname(servername);
}
else
{
      addr=inet_addr(servername);
      hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
}
if(hp==NULL)

      closesocket(conn);
      return;
}
連接到服務器
connect()函數用於向目標服務器建立連接。我們向它傳遞我們先前創建的套接字和一個sockaddr結構。我們使用由gethostbyname()/gethostbyaddr()返回的主機地址爲sockaddr成員賦值,並輸入一個要連接的有效端口。
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
server.sin_family=AF_INET;
server.sin_port=htons(80);
if(connect(conn,(struct sockaddr*)&server,sizeof(server)))

      closesocket(conn);
      return;
}
會話
當套接字連接建立後,客戶端和服務器就可以通過send()和recv()來發送/接收數據了。這通常稱爲TCP會話。對於我們的特定情況,我們需要進行HTTP會話。和那些複雜的SMTP或POP3協議相比,它還是比較簡單的。HTTP的GET命令用於從HTTP服務器上獲取文件。這個文件可以是HTML文件、圖像文件、壓縮文件、MP3文件等等。這樣,這個文件就會被髮送了(這是它最簡單的形式)。當然,還有一些更復雜的方法來使用這個命令。
GET http-path-to-file/r/n/r/n
在我們的程序中,我們像這樣來發送GET命令:
sprintf(buff,"GET %s/r/n/r/n",filepath);
send(conn,buff,strlen(buff),0);
當我們發送了這個命令的時候,我們就應該知道服務器就要開始把我們所請求的文件發送給我們了。就像我們使用send()來發送我們的命令一樣,我們可以使用recv()來接收服務器發送給我們的數據。我們循環調用recv(),直到它返回零,這時候我們就會知道服務器已經將數據發送完畢了。並且,對於我們的特定情況,我們可以將這些數據寫入文件,就像我們要下載並保存這個文件一樣。
while(y=recv(conn,buff,512,0))
{
     f.Write(buff,y);
}
關閉連接
現在我們的會話結束了,我們必須關閉連接。在我們的情況下,HTTP連接在文件發送完畢之後就會被服務器關閉了,但是這不要緊,我們仍然需要關閉我們的套接字並釋放資源。在更加複雜的會話中,我們通常在調用closesocket()之前調用shutdown()來確定緩衝區已經被刷新,否則可能會有部分數據丟失。
closesocket(conn);
釋放WinSock
我們調用WSACleanup()來結束WinSock的使用。
WSACleanup();
感謝您的閱讀。

本文轉自
http://vipchyl.spaces.live.com/blog/cns!339bdc92ae7869f9!188.entry
發佈了21 篇原創文章 · 獲贊 1 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章