posts - 495,comments - 227,trackbacks - 0
          <2008年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          常用鏈接

          留言簿(46)

          隨筆分類(476)

          隨筆檔案(495)

          最新隨筆

          搜索

          •  

          積分與排名

          • 積分 - 1394814
          • 排名 - 16

          最新評論

          閱讀排行榜

          評論排行榜

          服務端套接字是編程最簡單的一個部分,甚至在各種環境下都可以用類似的代碼。對于一個服務端套接字而言,他的基本工作包括:

          1. 初始化
          2. 創建套接字
          3. 給套接字捆綁一個本地地址
          4. 【可選】設置套接字屬性
          5. 調用listen函數
          6. 進入accept循環,接受來自客戶端的請求
          7. 對PCS進行管理
          8. 釋放服務端套接字所占用的資源

          3.1 初始化

          對于每個需要處理套接字的程序,在調用任何其他套接字函數之前,必須首先調用WSAStartup函數。如果不調用它,其他函數調用會失敗,并返回WSANOTINITIALISED錯誤碼,表示WinSock庫還沒有初始化。對于除了其他操作系統,初始化的方法會不一樣,請參見對應的文檔找到初始化方法。

          WSAStartup的函數原型為:

          int WSAStartup(
          WORD wVersionRequested,
          LPWSADATA lpWSAData
          );

          其中,各個參數的含義如下:
          wVersionRequested: 本程序所需要的最低版本號。請注意,MSDN中所說的是本程序可能用到的最高版本的函數,其含義和我所說的一樣。
          lpWSAData 返回WSADATA結構,說明WinSock庫當前的實現細節

          該函數成功時返回0,否則可能返回如下一些錯誤碼:
          WSASYSNOTREADY 底層網絡系統未準備好
          WSAVERNOTSUPPORTED 所要求版本號本實現不支持
          WSAEINPROGRESS 一個阻塞性套接字操作未完成
          WSAEPROCLIM 達到當前實現所支持的最大任務數
          WSAEFAULT lpWSAData是一個非法指針

          一般用戶并不關心實現細節,只要當前實現庫滿足最低版本需求即可。常見代碼為:

           WORD wVersion = MAKEWORD(2,2);//最低版本2.2
          WSADATA WSAData;
          int nResult;
          if(ERROR_SUCCESS != (nResult = WSAStartup(wVersion,&WSAData)))
          {
          ReportError("WSAStartup", nResult);
          return -1;
          }

          3.2 創建套接字

          每個套接字任務都從創建套接字開始。我們可以用socket函數來創建套接字,該函數的原型為:

          SOCKET socket(
          int af,
          int type,
          int protocol
          )
          其中,各個參數的含義為: 協議號,說明該套接字所處理的協議。他的可選值隨前面兩個參數不同而不同。似乎在RAW協議中用的比較多,大家可以在ROUTPROT.h中找到類似定義。
          af 地址族,說明該socket支持的地址類型。我們可以在winsock2.h中找到所支持的地址族。不過一般來說,對于TCP/IP編程,我們都會設置為AF_INET
          type 協議類型,Winsock2.h中列出了5種類型,我們一般會使用其中的三種,SOCK_STREAM表示流協議,SOCK_DGRAM表示數據報協議,SOCK_RAW表示原始套接字。我會在數據傳輸部分詳細解釋這些內容
          protocol

          用于偵聽的套接字需要是流套接字,下面代碼會創建這樣的套接字:

              SOCKET hSocket;
          int a = PROTO_ICMP;
          hSocket = socket(AF_INET,SOCK_STREAM,0);
          if(ERRORHANDLE(hSocket == INVALID_SOCKET))
          {
          WSACleanup();
          return -1;
          }

          3.3 捆綁本地地址

          每個套接字必須有一個地址才能和對方通訊。bind函數用于捆綁地址,它的函數原型為:

          int bind(
          SOCKET s,
          const struct sockaddr* name,
          int namelen
          );

          各個參數含義如下:
          s 套接字號
          name 地址信息:需要注意的是,套接字接口是給多種協議共享的,sockaddr結構只是一個占位符,不同協議使用不同的地址結構,例如,TCP/IP編程使用的結構是sockaddr_in
          namelen 地址結構的長度,加入這個參數的原因也就是因為不同協議有不同的結構

          需要注意的,bind函數只能給socket綁定本機的IP地址,如果你給出的地址信息是其他機器的,則必然會失敗。此時該函數返回SOCKET_ERROR,WSAGetLastError則返回WSAEADDRNOTAVAIL。

          如果沒有特殊需求,應該設置該結構的IP地址為INADDR_ANY。對于端口,服務端套接字需要指定一個端口,而客戶端端口最好設置為0,讓系統選擇一個可用端口。

          下面代碼初始化一個地址結構:

          void InitializeAddress(DWORD ip, UINT port, sockaddr_in & addr)
          {
          memset(&addr,0,sizeof(addr));
          addr.sin_family = AF_INET;
          addr.sin_addr.s_addr= ip;
          addr.sin_port = htons(port);
          }

          下面代碼則把一個套接字綁定到本機2000端口上:

          if(ERRORHANDLE(SOCKET_ERROR == bind(hSocket, (const sockaddr*) & addr, sizeof(addr)))) 
          {
          closesocket(hSocket);
          WSACleanup();
          return -1;
          }

          3.4 進入偵聽狀態

          當我們創建一個套接字,并綁定了地址后,我們需要設置這個套接字進入偵聽狀態,進入偵聽狀態后,該套接字就可以處理來自客戶端的鏈接請求。

          listen函數設置套接字進入偵聽狀態,其函數原型為:

          int listen(
          SOCKET s,
          int backlog
          );

          對于剛開始編程的人,最容易誤解的是backlog參數。許多人以為這就是該套接字最多能接收的鏈接的數目。實際上,一個套接字能接受的鏈接的數目不受這個參數控制,它只受系統資源的限制。例如對于linux,套接字用文件句柄實現,那么它可能受最大文件句柄數的限制。

          這個參數的含義是最多未決連接的數目,也就是連接請求已經到了服務端套接字,但是用戶還沒有調用accept的套接字數目。對于WinSock2,這個參數最大值為5。

          下面示例代碼說明了如何調用listen:

          if(ERRORHANDLE(SOCKET_ERROR == listen(hSocket,5))) 
          {
          closesocket(hSocket);
          WSACleanup();
          return -1;
          }

          3.5 accept循環

          當一個套接字處于偵聽狀態以后,我們就可以循環調用accept來接受新連接。accept函數的原型如下:

          SOCKET accept(
          SOCKET s,
          struct sockaddr* addr,
          int* addrlen
          );

          這個函數的參數說明和前面bind的一樣。需要說明的是,在MSDN中說后面兩個參數都是out參數,經過我的測試,結論并不一樣。對于 addrlen參數,應該是一個in/out參數,也就是說,如果第二個參數是一個結構指針,則第三個參數必須是一個整型變量的指針,該整型變量還必須被 設置為該結構的長度。

          3.6 PCS管理

          由于一個一個服務端套接字可能接收無數個PCS,如何管理這些PCS就成為一個問題。不同的程序員有自己不同的管理方式,在此我就不準備細講了。

          posted on 2008-07-03 15:17 SIMONE 閱讀(653) 評論(0)  編輯  收藏 所屬分類: C++
          主站蜘蛛池模板: 昌吉市| 秦皇岛市| 镇雄县| 长葛市| 和林格尔县| 衢州市| 九台市| 余姚市| 兴安盟| 石渠县| 勃利县| 屯留县| 蕲春县| 邯郸县| 曲阜市| 澜沧| 分宜县| 聂拉木县| 松滋市| 阳西县| 平顶山市| 渝中区| 铜川市| 台北市| 朔州市| 武清区| 乃东县| 无极县| 扎囊县| 江陵县| 静海县| 黄大仙区| 阿拉尔市| 平塘县| 永新县| 奉节县| 东城区| 德化县| 衡阳市| 高雄县| 富阳市|