Windows Socket API編程例子__編程

來源:互聯網
上載者:User

Windows Socket API編程例子

已有 408 次閱讀  2010-05-19 13:50

網上找了一個簡單的demo:

先看伺服器端程式:

#include <winsock2.h>
#include <iostream>
using namespace std;
#define PORT_SERVER 6666    //自訂連接埠6666
#define NUM_CLIENTS 10
#pragma comment(lib,"ws2_32.lib")

int main(int argc, char* argv[])
{
cout << "Start server..." << endl;

WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 0);
    if (0 != WSAStartup(sockVersion, &wsaData))
{
/*
使用Socket的程式在使用 Socket之前必須調用WSAStartup函數。該函數的第一個參數指明程式請求使用的Socket版本,其中高位位元組指明副版本、低位位元組指明主版 本;作業系統利用第二個參數返回請求的Socket的版本資訊。當一個應用程式調用WSAStartup函數時,作業系統根據請求的Socket版本來搜 索相應的Socket庫,然後綁定找到的Socket庫到該應用程式中。以後應用程式就可以調用所請求的Socket庫中的其它Socket函數了。該函 數執行成功後返回0。
*/
cout << "Failed to retrive socket version."
<< endl;
return 0;
}
SOCKET sock_sev;
sock_sev = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
/*
應用程式調用socket函數來建立一個能夠進行網路通訊的通訊端。第一個參數指定應用程式使用的通訊協定的協議族,對於TCP/IP協議族,該參數置PF_INET;第二個參數指定要建立的通訊端類型,流通訊端類型為SOCK_STREAM、資料報套 接字類型為SOCK_DGRAM;第三個參數指定應用程式所使用的通訊協定。該函數如果調用成功就返回新建立的通訊端的描述符,如果失敗就返回 INVALID_SOCKET。通訊端描述符是一個整數類型的值。每個進程的進程空間裡都有一個通訊端描述符表,該表中存放著通訊端描述符和通訊端資料結構的對應關係。該表中有一個欄位存放新建立的通訊端的描述符,另一個欄位存放通訊端資料結構的地址,因此根據通訊端描述符就可以找到其對應的通訊端資料結 構。每個進程在自己的進程空間裡都有一個通訊端描述符表但是通訊端資料結構都是在作業系統的核心緩衝裡。
*/
    if (INVALID_SOCKET == sock_sev)
{
cout << "Invalid socket." << endl;
WSACleanup();
/*
應用程式在完成對請求的Socket庫的使用後,要調用WSACleanup函數來解除與Socket庫的綁定並且釋放Socket庫所佔用的系統資源。
*/
       return 0;
}
sockaddr_in addr_sev;
addr_sev.sin_family = AF_INET;
addr_sev.sin_port = htons(PORT_SERVER);
addr_sev.sin_addr.s_addr = INADDR_ANY;
if (SOCKET_ERROR == bind(sock_sev, (sockaddr *)&addr_sev, sizeof(addr_sev)))
{
/*
  當建立了一個Socket以後,通訊端資料結構中有一個預設的IP地址和預設的連接埠號碼。一個服務程式必須調用bind函數來給其綁定一個IP地址和一個特定的連接埠號碼。客戶程式一般不必調用bind函數來為其Socket綁定IP地址和斷口號。
當對TCP/IP協議族的通訊端進行綁定時,我們通常使用地址結構:
     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指明連接埠號碼;sin_addr結構體中只有一個唯一的欄位s_addr,表示IP地址,該字 段是一個整數,一般用函數inet_addr()把字串形式的IP地址轉換成unsigned long型的整數值後再置給s_addr。有的伺服器是多重主機機,至少有兩個網卡,那麼運行在這樣的伺服器上的服務程式在為其Socket綁定IP地址時 可以把htonl(INADDR_ANY)置給s_addr,這樣做的好處是不論哪個網段上的客戶程式都能與該服務程式通訊;如果只給運行在多重主機機上的 服務程式的Socket綁定一個固定的IP地址,那麼就只有與該IP地址處於同一個網段上的客戶程式才能與該服務程式通訊。我們用0來填充 sin_zero數組,目的是讓sockaddr_in結構的大小與sockaddr結構的大小一致。
*/
cout << "Failed to bind." << endl;
WSACleanup();
return 0;
}
if (SOCKET_ERROR == listen(sock_sev, NUM_CLIENTS))
{
/*
  服務程式可以調用listen函數使其流通訊端s處於監聽狀態。處於監聽狀態的流通訊端s將維護一個客戶串連請求隊列,該隊列最多容納backlog個客戶串連請求。假如該函數執行成功,則返回0;如果執行失敗,則返回SOCKET_ERROR。
*/
       cout << "Failed to listen." << endl;
WSACleanup();
return 0;
}

SOCKET sock_client;
sockaddr_in addr_client;
int nAddrLen = sizeof(addr_client);
while (true)
{
char tmp[512];
sock_client = accept(sock_sev, (sockaddr *)&addr_client, &nAddrLen);
/*
    服務程式調用accept函數從處於監聽狀態的流通訊端s的客戶串連請求隊列中取出排在最前的一個客戶請求,並且建立一個新的通訊端來與客戶通訊端建立串連通道,如果串連成功,就返回新建立的通訊端的描述符,以後與客戶通訊端交換資料的是新建立的通訊端;如果失敗就返回INVALID_SOCKET。該函數的第一個參數指定處於監聽狀態的流通訊端;作業系統利用第二個參數來返回新建立的通訊端的地址結構;作業系統利用第三個參數來返回新建立的通訊端的地址 結構的長度。
*/
       if (INVALID_SOCKET == sock_client)
{
cout << "Failed to accept." << endl;
continue;
}
sprintf(tmp, "Your IP is %s/n", inet_ntoa(addr_client.sin_addr));
send(sock_client, tmp, strlen(tmp), 0);
/*
不論是客戶還是伺服器應用程式都用send函數來向TCP串連的另一端發送資料。客戶程式一般用send函數向伺服器發送請求,而伺服器則通常用send 函數來向客戶程式發送應答。該函數的第一個參數指定發送端通訊端描述符;第二個參數指明一個存放應用程式要發送資料的緩衝區;第三個參數指明實際要發送的資料的位元組數;第四個參數一般置0。這裡只描述同步Socket的send函數的執行流程。當調用該函數時,send先比較待發送資料的長度len和套接 字s的發送緩衝區的長度,如果len大於s的發送緩衝區的長度,該函數返回SOCKET_ERROR;如果len小於或者等於s的發送緩衝區的長度,那麼 send先檢查協議是否正在發送s的發送緩衝中的資料,如果是就等待協議把資料發送完,如果協議還沒有開始發送s的發送緩衝中的資料或者s的發送緩衝中沒 有資料,那麼send就比較s的發送緩衝區的剩餘空間和len,如果len大於剩餘空間大小send就一直等待協議把s的發送緩衝中的資料發送完,如果 len小於剩餘空間大小send就僅僅把buf中的資料copy到剩餘空間裡(注意並不是send把s的發送緩衝中的資料傳到串連的另一端的,而是協議傳 的,send僅僅是把buf中的資料copy到s的發送緩衝區的剩餘空間裡)。如果send函數copy資料成功,就返回實際copy的位元組數,如果 send在copy資料時出現錯誤,那麼send就返回SOCKET_ERROR;如果send在等待協議傳送資料時網路斷開的話,那麼send函數也返 回SOCKET_ERROR。要注意send函數把buf中的資料成功copy到s的發送緩衝的剩餘空間裡後它就返回了,但是此時這些資料並不一定馬上被 傳到串連的另一端。如果協議在後續的傳送過程中出現網路錯誤的話,那麼下一個Socket函數就會返回SOCKET_ERROR。(每一個除send外的 Socket函數在執行的最開始總要先等待通訊端的發送緩衝中的資料被協議傳送完畢才能繼續,如果在等待時出現網路錯誤,那麼該Socket函數就返回 SOCKET_ERROR)
注意:在Unix系統下,如果send在等待協議傳送資料時網路斷開的話,調用send的進程會接收到一個SIGPIPE訊號,進程對該訊號的預設處理是進程終止。
*/
       cout << "Connection from " << inet_ntoa(addr_client.sin_addr)
<< endl;
closesocket(sock_client);
/*
   closesocket函數用來關閉一個描述符為s通訊端。由於每個進程中都有一個通訊端描述符表,表中的每個通訊端描述符都對應了一個位於作業系統緩衝 區中的通訊端資料結構,因此有可能有幾個通訊端描述符指向同一個通訊端資料結構。通訊端資料結構中專門有一個欄位存放該結構的被引用次數,即有多少個通訊端描述符指向該結構。當調用closesocket函數時,作業系統先檢查通訊端資料結構中的該欄位的值,如果為1,就表明只有一個通訊端描述符指向它,因此作業系統就先把s在通訊端描述符表中對應的那條表項清除,並且釋放s對應的通訊端資料結構;如果該欄位大於1,那麼作業系統僅僅清除s在通訊端描述符表中的對應表項,並且把s對應的通訊端資料結構的引用次數減1。
closesocket函數如果執行成功就返回0,否則返回SOCKET_ERROR。
*/
}

return 0;
}


再看用戶端程式:
#include <winsock2.h>
#include <iostream>
using namespace std;
#define PORT_SERVER 6656
#pragma comment(lib,"ws2_32.lib")

int main(int argc, char* argv[])
{
cout << "Start up tcp client." << endl;

WSADATA wsaData;
WORD sockVersion = MAKEWORD(2, 0);
if (0 != WSAStartup(sockVersion, &wsaData))
{
cout << "Failed to retrive socket version."
<< endl;
return 0;
}

SOCKET sock_client;
sock_client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == sock_client)
{
cout << "Invalid socket." << endl;
WSACleanup();
return 0;
}
sockaddr_in addr_sev;
addr_sev.sin_family = AF_INET;
addr_sev.sin_port = htons(PORT_SERVER);
addr_sev.sin_addr.s_addr = inet_addr("127.0.0.1");
if (SOCKET_ERROR == connect(sock_client, (sockaddr *)&addr_sev, sizeof(addr_sev)))
{
/*
客戶程式調用connect函數來使客戶Socket s與監聽於name所指定的電腦的特定連接埠上的服務Socket進行串連。如果串連成功,connect返回0;如果失敗則返回SOCKET_ERROR。
*/
cout << "Failed to connect." << endl;
WSACleanup();
return 0;
}
char buf[512];
int recv_size = 0;
recv_size = recv(sock_client, buf, 512, 0);
/*
    不論是客戶還是伺服器應用程式都用recv函數從TCP串連的另一端接收資料。該函數的第一個參數指定接收端通訊端描述符;第二個參數指明一個緩衝區,該緩衝區用來存放recv函數接收到的資料;第三個參數指明buf的長度;第四個參數一般置0。這裡只描述同步Socket的recv函數的執行流程。當應用程式調用recv函數時,recv先等待s的發送緩衝中的資料被協議傳送完畢,如果協議在傳送s的發送緩衝中的資料時出現網路錯誤,那麼recv函數返 回SOCKET_ERROR,如果s的發送緩衝中沒有資料或者資料被協議成功發送完畢後,recv先檢查通訊端s的接收緩衝區,如果s接收緩衝區中沒有數 據或者協議正在接收資料,那麼recv就一直等待,只到協議把資料接收完畢。當協議把資料接收完畢,recv函數就把s的接收緩衝中的資料copy到 buf中(注意協議接收到的資料可能大於buf的長度,所以在這種情況下要調用幾次recv函數才能把s的接收緩衝中的資料copy完。recv函數僅僅是copy資料,真正的接收資料是協議來完成的),recv函數返回其實際copy的位元組數。如果recv在copy時出錯,那麼它返回 SOCKET_ERROR;如果recv函數在等待協議接收資料時網路中斷了,那麼它返回0。
注意:在Unix系統下,如果recv函數在等待協議接收資料時網路斷開了,那麼調用recv的進程會接收到一個SIGPIPE訊號,進程對該訊號的預設處理是進程終止。
*/
if (512 > recv_size)
{
buf[recv_size] = '/0';
cout << "receive data is :" << buf << endl;
memset(buf, 0, sizeof(buf));
}
else
{
cout << "receive data is overflow" << endl;
}
closesocket(sock_client);
WSACleanup();
system("PAUSE");
return 0;
}

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.