孫鑫VC++視頻學習筆記之14:網絡編程

轉自:http://webbery.tianyablog.com

  閱讀本文前,我們假設您已經:
   1,知道如何創建一個單文檔的App Wizard
   2,知道C++ 類、函數重載等簡單知識
   3,知道如何給View類或者Doc文檔添加成員變量
   4,會用MFC的IDE調試工具最好,那麼本文的程序您可以copy去調試
   5,知道如何爲某個框架類添加虛函數或消息處理函數
  
  網絡編程的內容和MFC關聯並不大,並不是MFC架構的主要內容,所以我記得比較簡略。
  
  一、ISO/OSI七層參考模型
  
  
  
  二、套接字(socket)的一些文字描述
  
   套接字存在於通信區域中。通信區域也叫地址族,它是一個抽象的概念,主要用於將通過套接字通信的進程的共有特性綜合在一起。套接字通常只與同一區域的套 接字交換數據(也有可能跨區域通信,但這支在執行了某種轉換進程後才能實現)。Windows Sockets只支持一個通信區域:網際域(AF_INET),這個域被使用網際協議簇通信的進程使用。
  
  網絡字節順序不同的計算 機存放多字節值的順序不同,有的機器在起始地址存放低字節(低位先存),有的機器在起始地址存放高字節(高位先存)。基於Intel的CPU,即我們通常 使用的PC機採用的是低位先存。爲保證數據的正確性,在網絡協議中需要制定網絡字節順序。TCP/IP協議使用16位整數和32位整數的高位先存格式。
  
  網間進程通信完全是異步的,相互通信的進程間既不存在父子關係,又不共享內存緩衝區,因此需要一種機制爲希望通信的進程間建立聯繫,爲二者的數據交換提供同步,這就是基於客戶機/服務器模式的TCP/IP。
  
  套接字的類型
  
  流式套接字(SOCK_STREAM)
  
  提供面向連接,可靠的數據傳輸服務,數據無差錯,無重複的發送,且按發送順序接收
  
  數據報套接字(SOCK_DGRAM)
  
  提供無連接服務。數據報以獨立包形式發送,不提供錯誤保證,數據可能丟失或重複,並且接收順序混亂。基於UDP原始套接字(SOCK_RAW)。
  
  基於TCP的socket編程服務器和客戶端進行通信都使用send/recv
  
  基於UDP的socket編程服務器端爲接收端,客戶端爲發送端。發送數據爲sendto,接收數據爲recvfrom
  
  
  
  三、一些結構定義和函數
  
  (一) 、3個結構定義:
  
  1, SOCKET socket (
   int af, //指定地址族,對於TCP/IP只能是AF_INET(PF_INET)
   int type, //SOCK_STREAM,SOCK_DGRAM
   int protocol //推薦爲零,可自動選擇協議
  );
  
  2, struct sockaddr_in{
  
  short sin_family;
  
  unsigned short sin_port;
  
  struct in_addr sin_addr;
  
  char sin_zero[8];
  
  };
  
  3, struct in_addr {
  
  union {
  
  struct{unsigned char s_b1,s_b2,s_b3,s_b4;} S_un_b;
  
  struct {unsigned short s_w1,s_w2;} S_un_w; unsigned long S_addr;
  
  } S_un;
  
  };
  
  4, struct sockaddr {u_short sa_family;char sa_data[14];};
  
  (二) 、4個函數定義
  
  1, SOCKET accept (
   SOCKET s,
   struct sockaddr FAR* addr,
   int FAR* addrlen //必須在傳入一個addrlen之前爲它賦初始值,否則調用失敗
  ); //int len=sizeof(SOCKADDR);
  
  2, unsigned long inet_addr ( const char FAR * cp );//用來把IP地址轉化爲ULONG類型,用於IN_ADDR結構
  
  3, char FAR * inet_ntoa ( struct in_addr in );//返回一個點分十進制地址值
  
  4, int bind ( SOCKET s, const struct sockaddr FAR* name, int namelen);
  
  
  
  
  
  在爲我們的網絡程序指定端口號時,我們要用1024以上的端口.
  
  兩個類型轉換函數:
  
  (1) htonl把一個u_long類型從主機字節序轉換爲網絡字節序
  
  (2) htons把一個u_short類型從主機字節序轉換爲網絡字節序
  
  四、TCP聊天程序服務器端程序
  
  #include
  #include
  
  main(){
  
   WORD wVersionRequested;
  
   WSADATA wsaData;
  
   wVersionRequested = MAKEWORD( 1, 1);
  
   int 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_STREAM,0); //第三個參數爲零表示自動選擇協議
  
   SOCKADDR_IN addrSrv; //定義一個地址結構體的變量
  
   addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
  
   addrSrv.sin_family=AF_INET;
  
   addrSrv.sin_port=htons(6000);//htons把一個u_short類型從主機字節序轉換爲網絡字節序
  
   bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
  
   listen(sockSrv,5);
  
   SOCKADDR_IN addrClient;
  
   int len=sizeof(SOCKADDR);
  
   while(1) {
  
   SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
  
   char sendBuf[100];
  
   sprintf(sendBuf,"Welcome %s to here",inet_ntoa(addrClient.sin_addr));
  
   send(sockConn,sendBuf,strlen(sendBuf)+1,0);
  
   char recvBuf[100];
  
   recv(sockConn,recvBuf,100,0);
  
   printf("%s/n",recvBuf);
  
   closesocket(sockConn);
  
   }
  
  }
  
  要在控制檯使用套接字,需要加入頭文件 #include 和庫函數ws2_32.lib
  要鏈接一個動態鏈接庫,我們要在VC++菜單欄中選擇Project--->Settings--->Link,在其中的Object/Library modules中先打入一個空格,再添加庫函數ws2_32.lib
  
  
  
  五、TCP聊天程序客戶端程序
  
  #include
  
  #include
  
  main(){
  
  WORD wVersionRequested;
  
   WSADATA wsaData;
  
  wVersionRequested = MAKEWORD( 1, 1);
  
   int 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_STREAM,0);
  
   SOCKADDR_IN addrSrv;
  
   addrSrv.sin_addr.S_un.S_addr="127.0.0.1";
  
  //本地迴路地址,不管本地主機上有沒有網卡,都可以測試網絡
  
  TCP和UDP編程代碼大致相同,不同之處在於,TCP使用send/recv;UDP使用sendto/recvfrom;
  
  sendto(sockClient,"Hello!",strlen("Hello!")+1,0,(SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
  
  recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
  
  六、UDP的聊天程序服務器版:
  
  #include
  
  #include
  
  void main()
  
  {
  
  WORD wVersionRequested;
  
  WSADATA wsaData;
  
  wVersionRequested = MAKEWORD( 1, 1);
  
  int 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);
  
   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 sendBuf[100],recvBuf[100],temp[200];
  
   SOCKADDR_IN addrClent;
  
   int len=sizeof(SOCKADDR);
  
   while(1) {
  
   recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClent,&len);
  
   if('q'==recvBuf[0]) {
  
   sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClent,len);
  
   printf("chat end!/n");
  
   break;
  
   }
  
   sprintf(temp,"%s:%s",inet_ntoa(addrClent.sin_addr),recvBuf);
  
   printf("%s/n",temp);
  
   printf("please input data:/n");
  
   gets(sendBuf);
  
   sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClent,len);
  
   }
  
   closesocket(sockSrv);
  
  WSACleanup();
  
  }
  
  七、UDP聊天程序客戶機版:
  #include
  #include
  
  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);
  
   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);
  
   char sendBuf[100];
   char recvBuf[100];
   char temp[200];
   int len=sizeof(SOCKADDR);
  
   while(1)
   {
   printf("please input data/n");
   gets(sendBuf);
   sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrSrv,
   len);
   recvfrom(sockClient,recvBuf,100,0,(SOCKADDR*)&addrSrv,
   &len);
   if('q'==recvBuf[0])
   {
   sendto(sockClient,"q",strlen("q")+1,0,(SOCKADDR*)&addrSrv,len);
   printf("chat end!/n");
   break;
   }
   sprintf(temp,"%s:%s",inet_ntoa(addrSrv.sin_addr),recvBuf);
   printf("%s/n",temp);
   }
   closesocket(sockClient);
   WSACleanup();
  }
  記着要加載庫函數ws2_32.lib
  啓動順序應遵循先服務器後客戶機,否則容易出錯。
  發送字符時應該多加一個空字符作爲結束字符。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章