VC++ 網絡編程總結(一)


1、套接字編程原理

        一個完整的網間通信進程需要由兩個進程組成,並且只能用同一種高層協議。也就是說,不可能通信的一段用TCP,而另一端用UDP。一個完整的網絡信息需要一個五元組來標識:協議、本地地址、本地端口號、遠端地址、遠端端口號。

1.1Client/Server通信模型

        在客戶端/服務器模式中我們將請求服務的一方成為客戶,將提供某種服務的一方稱為服務器(Server)

       一個服務程序通常在一個眾所周知的地址監聽對服務的請求,也就是說服務進程一直處於休眠狀態,直到一個客戶對這個服務的地址提出了連接請求。在這個時刻,服務程序被“驚醒”,並且為客戶提供服務----對客戶的請求做出適當的反應。雖然基於鏈接的服務是設計客戶機/服務器應用程序時的標准,但有些服務也是可以通過無連接的接口提供的。

         客戶及/服務器的請求/響應過程示意圖如下所示:

                 2013-08-18_094539

1.2Windows Sockets規范

        Windows Sockets規范從90年代的1.0版本開始,經過不斷的完善和發展,目前已經有了Windows Sockets 2版本。值得注意的是,Microsoft的MFC庫現在只支持Windows Sockets1版本,不支持WindowsSockets2版本。

        MFC提供了兩個類用以封裝Windows Sockets API。一個是CAsyncSocket類,它主要是提供給那些具有一定網絡編程經驗,希望同時擁有Socket API編程的靈活性和類庫編程便利性的開發者。另一個是CSocket類,它有CAsyncSocket類派生,它具有更高的抽象化,致力於簡化網絡編程所需的操作。

1.3套接字

1.3.1套接字定義

      套接字是一個通信終結點,它是Sockets應用程序用來在網絡上發送或接收數據包的對象。套接字具有類型,與正在運行的進程相關聯,並且可以有名稱。目前,套接字一般只與使用網際協議組的同一“通信域”中德其他套接字交換數據。使用套接字的應用程序間通信模型如圖:

                2013-08-18_095755

1.3.2分類

   1.3.2.1流式套接字

       流式套接字提供沒有記錄邊界的數據流,即字節流。字節流能確保以正確的順序無重復地被送達。

2013-08-18_100404

1.3.2.2數據報套接字

        數據報套接字支持面向記錄的數據流,但不能確保能被送達,也無法確保按照發送順序或不重復。

       2013-08-19_172014

     “有序”指數據包按發送的順序送達。“不重復”指一個特定的數據包只能獲取一次。這兩種套接字都是雙向,是可以同時在兩個方向上(全雙工)進行通信的數據流.

       注意:在某些網絡協議下(如XNS),流可以面向記錄,即作為記錄流而非字節流。但在更常用的TCP/IP協議下,流為字節流,Win Sockets提供與基礎協議無關的抽象化級別。

1.3.3套接字的作用

      套接字的作用非常大,至少在下面三種通信上下文中如此:

  • 客戶端/服務器模型
  • 對等網絡方案,如聊天應用程序
  • 通過讓接受應用程序將消息解釋為函數調用來進行遠程過程調用

1.3.4 端口與地址

     在網絡上,一個套接字的標識主要借助於地址和端口來描述。

     套接字的地址指該套接字所在計算機的網絡地址,可以為域名或IP地址的形式。通常創建套接字時不必指明網絡地址,只有在擁有多個網絡地址的機器時,才需要顯式指定的一個網絡地址。

      同一機器上可以運行多個網絡應用程序,每個應用程序都有自己的套接字用以進行網絡通信,此時如果只有地址表示套接字,則當一個通信包到達機器時,將無法確定究竟是哪個應用程序的套接字需要接收此信息。由此增加了端口的概念,以協助區分同一機器上不同應用程序的套接字。

      段開口用於標識進程,同一機器上不同的網絡應用程序各有不同的端口,這樣,通過“網絡地址+端口號”的標識方法,便唯一標識了機器上的應用程序了。

實例應用程序:

======客戶端

#include < WINSOCK2.H> 
#pragma comment( lib, "WS2_32" ) 
#include < stdio.h> 

int main() 
{ 
     printf( "------------------------\n| 客戶端 |\n|---------------------------------------\n" ); 
     //------①加載動態鏈接庫winsock DLL-----------
     printf( "|加載等待中.... " ); 
     WSADATA wsaData; 
     WORD wVersionRequested= MAKEWORD( 2 ,2 ); 
     if ( WSAStartup( wVersionRequested,& wsaData)!= 0 )
     { 
         printf( "|WSAStartup Failed\n" ); 
         printf( "|WSAStartup Error=%d\n" , WSAGetLastError()); 
     } 
     else 
     { 
         printf( "加載Winsock 庫成功 |\n" ); 
     } 
     printf( "|---------------------------------------\n" ); 
     //-------②創建用於監聽的流式套接口s,使用socket()-----------------
     SOCKET s= socket( AF_INET, SOCK_STREAM, IPPROTO_TCP); 
     if ( s== INVALID_SOCKET) 
     { 
         printf( "|Failed socket\n" ); 
         printf( "|socket Error=%d\n" , WSAGetLastError()); 
     } 
     else 
         printf( "|已創建用於監聽的套接口,套接口號:[%u]\n" , s); 
     printf( "|---------------------------------------\n" ); 
     //------③本地地址bind(可以不做這部分,如果不綁定,系統將自動分配)--------
     /*struct sockaddr_in Cadd;
     Cadd.sin_family=AF_INET;
     Cadd.sin_port=htons(4444);
     Cadd.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
     if (bind(s,(sockaddr*)&Cadd,sizeof(Cadd))==SOCKET_ERROR)
     {
         printf("|Failed bind()/n");
     }*/ 
     //-------填寫要連接的服務器地址信息---------
     struct sockaddr_in Sadd; 
     Sadd.sin_family= AF_INET; 
     Sadd.sin_port= htons( 5555 ); 
     Sadd.sin_addr.S_un.S_addr= inet_addr( "127.0.0.1" ); 
     //--------④將套接口s與遠程主機相連--------------
     if ( connect( s,( sockaddr*)& Sadd, sizeof ( Sadd))== INVALID_SOCKET) 
     { 
         printf( "|Failed connect()\n" ); 
         printf( "|connect Error=%d\n" , WSAGetLastError()); 
     } 
     else 
     { 
         //####################開始發接數據########################
         printf( "|連接成功,可以開始發送接收數據了!\n" ); 
         printf( "|服務器IP地址:[%s]\n 端口號:[%u]\n" , inet_ntoa( Sadd.sin_addr), ntohs( Sadd.sin_port)); 
         //####################結束發接數據########################
     } 
     //--------------⑤關閉套接字s,終止對動態鏈接庫的訪問----------
     closesocket( s); 
     printf( "|---------------------------------------\n" ); 
     printf( "|連接完畢\n" ); 
     WSACleanup(); 
     return 0 ;
}

========服務端

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32")

int main(int argc, char* argv[])
{
    printf("---------\n| 服務端 |\n-----------");
    //----------1、加載動態鏈接庫Winsock Dll---------
    WORD wVersionRequested = MAKEWORD(2,2);
    WSADATA wsaData;
    if(WSAStartup(wVersionRequested,&wsaData) != 0)
    {
        printf("WSAStartup Failed \n");
        printf("WSAStartup Error = %d \n",WSAGetLastError());
    }
    else
    {
        printf("加載WinSocket成功 \n");
        printf("調用者希望使用的WinSocket版本號=%x\n",wsaData.wVersion);
        printf("加載的WinSock庫支持最高WinSock版本號=%x \n",wsaData.wHighVersion);
        printf("wWinSock庫的說明字符串=%s \n",&wsaData.szDescription[0]);
        printf("系統狀態或配置信息的說明字符串=%s\n",&wsaData.szSystemStatus[0]);
    }
    //----------2、創建監聽的流式套接口----------
    
    SOCKET s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(s == INVALID_SOCKET)
    {
        printf("| Failed socket\n");
        printf("| Socket Error = %d\n",WSAGetLastError());
    }
    else
        printf("毅創建用於監聽的套接口,套接口號:[%u]\n",s);

    //----3、綁定使用bind()
    struct sockaddr_in Sadd;
    Sadd.sin_family = AF_INET;
    Sadd.sin_port = htons(5555);
    Sadd.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    if(bind(s,(sockaddr*)&Sadd,sizeof(Sadd)) == SOCKET_ERROR)
    {
        printf("Failed bind()\n");
        printf("Bind Error = %d\n",WSAGetLastError());
    }
    else
    {
        printf("綁定成功\n");
        printf("本地Ip地址:[%s],本地端口號:[%u]\n",inet_ntoa(Sadd.sin_addr),ntohs(Sadd.sin_port));
    }
    //---------------④監聽狀態-------------
     if ( listen( s, 2 )== SOCKET_ERROR) 
     { 
         printf( "Failed listen()\n" ); 
         printf( "listen Error=%d\n" , WSAGetLastError()); 
     } 
     //----------------⑤循環接受客戶的連接請求---------------------------
     struct sockaddr_in Cadd; 
     int caddLen= sizeof ( Cadd); 
     SOCKET c; 
     while ( TRUE ) 
     { 
         printf( "|----------------------------\n" ); 
         printf( "|進入監聽狀態.....\n|--------------------------------|\n" ); 
         c= accept( s,( sockaddr*)& Cadd,& caddLen); 
         if ( c== INVALID_SOCKET) 
         { 
             printf( "|Failed accept()\n" ); 
             printf( "|accept Error=%d\n" , WSAGetLastError()); 
         } 
         else 
             printf( "|可以在套接口[%u]上發送接收數據了!\n" , c); 
         //#########################開始發送、接收###################### 注意都要在新套接口c上進行
         //#########################結束發送、接收######################
         closesocket( c); 
         printf( "|與主機IP地址是:[%s]\n|端口號是:[%u]的連接完畢\n" , inet_ntoa( Cadd.sin_addr), ntohs( Cadd.sin_port)); 
         char xx; 
         printf( "|-------------------------------------\n需要退出嗎?(Y\n)" ); 
         scanf( "%c" ,& xx); 
         if ( xx== 'Y' || xx== 'y' ) 
         { 
             break ; 
         } 
     } 
     closesocket( s); 
     WSACleanup();
    return 0;
}

運行結果:

2013-08-19_1742062013-08-19_174227


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM