Windows程式設計__孫鑫C++Lesson14《網路編程》

來源:互聯網
上載者:User

Windows程式設計__孫鑫C++Lesson14《網路編程》
本節要點:
1.網路通訊協定參考模型簡介
2.通訊端簡介
3.網路位元組順序
4.客戶機/伺服器模式簡介
5.Windows Sockets的實現
6.Windows網路編程函數準備
7.基於TCP的通訊端編程
8.基於UDP的通訊端編程
//*************************************************************************************
1.網路通訊協定參考模型簡介
OSI七層參考模型 TCP/IP的四層模型  這部分內容涉及理論知識比較豐富,請參見謝希仁《電腦網路》.
這裡僅把協助理解的圖列在下面:

2.通訊端簡介
通訊端存在於通訊地區中。通訊地區也叫地址族,它是一個抽象的概念,主要用於將通過通訊端通訊的進程的公有特性綜合結合在一起。
通訊端通常只與同一地區的通訊端交換資料(也有可能跨地區通訊,但這隻在執行了某種轉換進程後才能實現)。Windows Sockets只支援一個通訊地區:網際域(AF_INET),這個域被使用網際協議簇通訊的進程使用。
3.網路位元組順序
不同的電腦存放多位元組值得順序不同,有的機器在起始地址存放低位位元組(低位先存),有的機器在起始地址存放高位位元組(高位先存)。基於Intel的cpu,即我們常用的pc機採用的是低位先存。為保證資料的正確性,在網路通訊協定中需要使用指定的網路位元組順序。TCP/IP協議使用16位整數和32位整數的高位先存格式。
4.客戶機/伺服器模式簡介  詳細內容請參見謝希仁《電腦網路》.
這裡介紹如所示:

5.Windows Sockets的實現
(1)Windows Sockets是從伯克利通訊端擴充而來,以動態連結程式庫的形式提供給我們使用。Windows Sockets擴充主要是提供了一些非同步函數,並增加了符合Windows訊息驅動特性的網路事件非同步選擇機制。
(2)通訊端的類型
流式通訊端(SOCK_STREAM)提供連線導向、可靠的Data Transmission Service,資料無差錯、無重複的發送,且按發送順序接收,基於TCP協議實現的。
資料報式通訊端(SOCK_DGRAM)提供無串連服務。資料包以獨立包形式發送,不提供無錯保證,資料可能丟失或者重複,並且接收順序混亂,基於UDP協議實現的。
原始通訊端(SOCK_RAM) 這裡不做介紹。
6.Windows網路編程函數準備
(1)
int WSAStartup(
  WORD wVersionRequested,//Windows Sockets版本資訊 高位元組指定最低版本,
                         //低位位元組表示主要版本 
  LPWSADATA lpWSAData  //
);
該函數載入了Ws2_32.dll動態連結程式庫,每次成功調用後應用程式必須在使用完後調用WSACleanup 釋放Ws2_32.dll的資源,終止其使用.

(2)SOCKET socket(
  int af,       //指定地址族 對於TCP/IP協議的通訊端
  int type,     //指定Socket類型
  int protocol  //是與特定的地址家族相關的協議 指定為0那麼他就會根據
                 地址格式和通訊端類別,自動為你選擇一個合適的協議
);
建立一個和指定服務提供者綁定的通訊端。
(3)int bind(
  SOCKET s,     //待綁定的通訊端                    
  const struct sockaddr FAR *name, //指定了該通訊端的本地地址資訊 指向sockaddr指標
  int namelen                       //指定第二個參數的長度
);
用來綁定一個本地地址和通訊端。
struct sockaddr {
  u_short    sa_family;
  char       sa_data[14];
};  
在TCP/IP協議中,我們可以用SOCKADDR_IN結構來替換sockaddr,以方便我們填寫地址資訊。
struct sockaddr_in {
        short   sin_family;
        u_short sin_port;
        struct  in_addr sin_addr;
        char    sin_zero[8];
};
(4)inet_addr  將包含一個IPv4地址的字串轉換為IN_ADDR結構的合適的地址
   inet_ntoa  將一個IPv4地址轉換為形如a.b.c.d形式的字串
   htonl      將一個 u_long結構的32位主機位元組序的數轉換為TCP/IP網路位元組序
   htons      將一個 u_short結構的16位主機位元組序的數轉換為TCP/IP網路位元組序
(5)TCP發送資料
int send(
  SOCKET s,             
  const char FAR *buf, 
  int len,              
  int flags             
);
TCP接受資料
int recv(
  SOCKET s,      
  char FAR *buf, 
  int len,       
  int flags      
);
(6)UDP發送資料
int sendto(
  SOCKET s,                       
  const char FAR *buf,           
  int len,                        
  int flags,                      
  const struct sockaddr FAR *to, 
  int tolen                       
);
UDP接受資料
int recvfrom(
  SOCKET s,                  
  char FAR* buf,             
  int len,                   
  int flags,                 
  struct sockaddr FAR *from, 
  int FAR *fromlen           
);
7.基於TCP的通訊端編程
注意:添加兩個程式到一個工程,通過選中組建來切換工程。先啟動伺服器端,後啟動用戶端。
TCP用戶端和伺服器端的編寫過程如所示:

//實驗代碼如下
//*************************************************************************************
//TcpSrv.cpp
#include <Winsock2.h>
#include <stdio.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD( 1, 1 );
//載入Ws2_32.dll
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
           return;
}

if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1) {
  WSACleanup( );
  return;
}

SOCKET SocketSrv=socket(AF_INET,SOCK_STREAM,0 );//step1 建立通訊端
    SOCKADDR_IN  AddrSrv;
    AddrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//網路位元組序
    AddrSrv.sin_family=AF_INET;
AddrSrv.sin_port=htons(6000);//連接埠號碼兩個位元組
bind(SocketSrv,(SOCKADDR *)&AddrSrv,sizeof(SOCKADDR));//step2 綁定到通訊端
listen(SocketSrv,5);//step3 監聽串連請求 5為隊列長度
    SOCKADDR_IN AddrClient;
    int len=sizeof(SOCKADDR);//必須賦初值
//伺服器端程式迴圈運行
while(1)
{
      SOCKET  ScoketConn=accept(SocketSrv, (SOCKADDR*)&AddrClient, &len);//step4 接受請求 AddrClient接收串連請求方的資訊

      char SendBuf[100];
   sprintf(SendBuf,"Wellcome! Connect from %s Success!",inet_ntoa(AddrClient.sin_addr));
   send(ScoketConn,SendBuf,strlen(SendBuf)+1,0);//step5 發送資料用串連的通訊端 不能用處於監聽狀態的通訊端
   char RecvBuf[100];
      recv(ScoketConn,RecvBuf,100,0);//step5 接收資料
      printf("%s\n",RecvBuf);
   closesocket(ScoketConn);
}
//實際上還需做以下操作 但上述是死迴圈因此沒有執行下述操作
    closesocket(SocketSrv);//關閉通訊端
    WSACleanup();//終止庫引用
}
//*************************************************************************************
//TcpClient.cpp
#include <Winsock2.h>
#include <stdio.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD( 1, 1 );

err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
  return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1) {
  WSACleanup( );
  return;
}
SOCKET SocketClient=socket(AF_INET,SOCK_STREAM,0 );//step1 建立通訊端
SOCKADDR_IN AddrSrv;
        AddrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");//本地迴路地址
AddrSrv.sin_family=AF_INET;
AddrSrv.sin_port=htons(6000);
connect(SocketClient,(SOCKADDR *)&AddrSrv,sizeof(SOCKADDR));//step2 串連請求
char RecvBuf[100];
recv(SocketClient,RecvBuf,100,0);//接收伺服器資料
printf("%s\n",RecvBuf);
char sendbuf[100];
sprintf(sendbuf,"this is from %s","liming");
send(SocketClient,sendbuf,strlen(sendbuf)+1,0);//發送資料
closesocket(SocketClient);//關閉通訊端 釋放資源
WSACleanup();//終止通訊端庫的引用
}
//*************************************************************************************
啟動程式後(注意先啟動伺服器程式,後啟動用戶端程式)運行效果:


//*************************************************************************************
8.基於UDP的通訊端編程
UDP服務端和用戶端編寫過程如所示:

 

//實驗代碼如下
//*************************************************************************************
//NetSrv.cpp
#include <Winsock2.h>
#include <stdio.h>
void main()
{  
//載入dll檔案
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
  return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1) {
  WSACleanup( );
  return;
}
SOCKET SockSrv=socket(AF_INET,SOCK_DGRAM,0);//建立基於UDP的通訊端
SOCKADDR_IN AddrSrv;
    AddrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
    AddrSrv.sin_family=AF_INET;
AddrSrv.sin_port=htons(6000);
    bind(SockSrv,(SOCKADDR *)&AddrSrv,sizeof(SOCKADDR));//綁定通訊端
char RecvBuf[100];
char SendBuf[100];
char TempBuf[200];
SOCKADDR_IN AddrClient;
int len=sizeof(SOCKADDR);//監聽請求
while(1)
{
  recvfrom(SockSrv,RecvBuf,100,0,(SOCKADDR *)&AddrClient,&len);//接受資料
     if( 'q'==RecvBuf[0] )//對方是否想要對出聊天
  {
   sendto(SockSrv,"q",strlen("q")+1,0,(SOCKADDR *)&AddrClient,sizeof(SOCKADDR));//回傳一個'q'終止聊天
      printf("chat end!\n");
   break;//終止迴圈
  }
       sprintf(TempBuf,"Message from %s is :%s \n",inet_ntoa(AddrClient.sin_addr),RecvBuf);
    puts(TempBuf);//顯示接受資料
    printf("please reply:\n");
    gets(SendBuf);
    sendto(SockSrv,SendBuf,strlen(SendBuf)+1,0,(SOCKADDR *)&AddrClient,sizeof(SOCKADDR));//發送資料
}
//資源釋放處理
closesocket(SockSrv);
WSACleanup();
}
//*************************************************************************************
//NetClient.cpp
#include <Winsock2.h>
#include <stdio.h>
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD( 1, 1 );

err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
  return;
}

if ( LOBYTE( wsaData.wVersion ) != 1 ||
        HIBYTE( wsaData.wVersion ) != 1) {
  WSACleanup( );
  return;
}
SOCKET SockClient=socket(AF_INET,SOCK_DGRAM,0);//建立基於UDP的通訊端
SOCKADDR_IN AddrClient;
        AddrClient.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
        AddrClient.sin_family=AF_INET;
AddrClient.sin_port=htons(6000);
char RecvBuf[100];
char SendBuf[100];
char TempBuf[200];
SOCKADDR_IN AddrSrv;
int len=sizeof(SOCKADDR);
while(1)
{
    printf("plase input Message\n");
    gets(SendBuf);
    sendto(SockClient,SendBuf,strlen(SendBuf)+1,0,(SOCKADDR *)&AddrClient,sizeof(SOCKADDR));//發送資料
    recvfrom(SockClient,RecvBuf,100,0,(SOCKADDR *)&AddrSrv,&len);
    if('q'==RecvBuf[0])//是否終止聊天
    {
     sendto(SockClient,"q",strlen("q")+1,0,(SOCKADDR *)&AddrClient,sizeof(SOCKADDR));
     printf("chat end!\n");
        break;
    }
    sprintf(TempBuf,"Message From %s is : %s\n",inet_ntoa(AddrSrv.sin_addr),RecvBuf);
    puts(TempBuf);
}
//資源釋放處理
closesocket(SockClient);
WSACleanup();
}
//*************************************************************************************
啟動程式後(注意先啟動伺服器程式,後啟動用戶端程式)運行效果:


//*************************************************************************************
本節小結"
1.認識TCP協議和UDP協議的區別
2.認識Windows sockets通訊端
3.掌握簡單的基於TCP、基於UDP的伺服器端和用戶端的程式編寫
對於這幾個函數中的一些參數特別是如AddrClient.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");這種語句中的資料結構,用起來並不是很方便,還有待理解。

聯繫我們

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