程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> windows下的C++ socket服務器(3)

windows下的C++ socket服務器(3)

編輯:C++入門知識

 make_server_socket(
    WSAStartup(MAKEWORD(), &inet_WsaData);

    if (LOBYTE(inet_WsaData.wVersion) != 2 || HIBYTE(inet_WsaData.wVersion) != 0)//2
    {
        WSACleanup();
        return -1;
    }

     tcp_socket = socket(AF_INET, SOCK_STREAM, );
 sockaddr_in saddr;
    saddr.sin_family === (::bind(tcp_socket, (  sockaddr*)&saddr, (saddr)) == -)
<<  << - (::listen(tcp_socket, ) == -)
<<  << -

1 WSADATA inet_WsaData;SAStartup(MAKEWORD(1, 1), &inet_WsaData);

在windows下使用socket的相關函數前,必須通過WSAStartup函數完成對Winsock服務的初始化。

int  WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData);

該函數的第一個參數指明程序請求使用的Socket版本,其中高位字節指明副版本、低位字節指明主版本;第二個參數可以用來返回請求的Socket的版本信息。當一個應用程序調用WSAStartup函數時,操作系統根據請求的Socket版本來搜索相應的Socket庫,然後綁定找到的Socket庫到該應用程序中。以後應用程序就可以調用所請求的Socket庫中的其它Socket函數了。

以前大家使用的都是socket1.1版本,但socket2.0版本已經出來了,所以我這裡使用的是socket2.0版本(MAKEWORD(2.0))

 

1.1版和2.0版的區別:

兩者的最重要區別是1.1版只支持TCP/IP協議,而2.0版可以支持多協議。2.0版有良好的向後兼容性,任何使用1.1版的源代碼,二進制文件,應用程序都可以不加修改地在2.0規范下使用。

 

MAKEWORD的定義如下

#define MAKEWORD(a, b)      ((WORD)(((BYTE)(((DWORD_PTR)(a)) & 0xff)) | ((WORD)((BYTE)(((DWORD_PTR)(b)) & 0xff))) << 8))

 

2 if (LOBYTE(inet_WsaData.wVersion) != 2 || HIBYTE(inet_WsaData.wVersion) != 0)用於檢測當前的Socket是否為2.0

LOBYTE和HIBYTE是兩個宏,在vs2013裡定義如下

#define LOBYTE(w)           ((BYTE)(((DWORD_PTR)(w)) & 0xff))
#define HIBYTE(w)           ((BYTE)((((DWORD_PTR)(w)) >> 8) & 0xff))

 

WSACleanup();用於解除與Socket庫的綁定並釋放Socket庫所占用的系統資源。

 

3 int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);

socket函數用於建立一個socket,函數原型如下

SOCKET socket(int af, int type, int protocol);

第一個參數af指定應用程序使用的通信協議的協議族,af一般置為AF_INET(表示internetwork: UDP, TCP等);

第二個參數type為協議的Socket類型,常用的有3種:SOCK_STREAM、SOCK_DGRAM和SOCK_RAW。

SOCK_STREAM對應於TCP。

SOCK_DGRAM對應於UDP。

SOCK_RAW稱為原始Socket,可以讀寫ICMP、IGMP、IP報文。前兩種類型使用得最多。

第三個參數protocol指定所使用的協議。對於SOCK_STREAM、SOCK_DGRAM兩種類型的Socket,該參

數為0,對於原始Socket才需要指定具體的協議。

 

4 struct sockaddr_in saddr;

sockaddr_in是定義了socket發送和接收數據包的地址的結構體,有四個字段,含義如下

第一個參數short sin_family,指定應用程序使用的通信協議的協議族,af一般置為AF_INET(表示internetwork: UDP, TCP, etc.Internetwork Version 4);

第二個參數u_short sin_port,代表程序使用的IP地址端口,由程序員指定;

第三個參數struct in_addr sin_addr中的s_addr,用於設置IP地址;

第四個參數char sin_zero[8],是為了保證sockaddr_in與SOCKADDR類型的長度相等而填充進來的字段。

例如

struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);//使用的端口號
saddr.sin_addr.s_addr = INADDR_ANY;//任意地址均可以,這樣任意客戶端都可以訪問到服務器

 

5 ::bind(tcp_socket, (const struct sockaddr*)&saddr, sizeof(saddr))

這裡使用::表示的位於全局作用域下的bind,由於我之前使用了using namespace std;所以如果沒有使用::,它會使用std下的bind,出現一系列的錯誤

bind函數用來將一個socket套接字綁定到一個地址,很多函數會隱式的調用bind函數。

bind的函數原型如下

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

第一個參數指定待綁定的Socket描述符;

第二個參數指綁定到的地址結構,即一個sockaddr類型的數據;

第三個參數指對應的是地址的大小;

如果bind錯誤,返回-1,

例如

if (::bind(tcp_socket, (const struct sockaddr*)&saddr, sizeof(saddr)) == -1)//綁定到tcp_socket,使用saddr的地址結構,該地址的大小為sizeof(saddr),

{
    cerr << "bind error" << endl;   
    return -1;
}

 

6 ::listen(tcp_socket, 1)

如果作為一個服務器,在調用socket()、bind()之後需要調用listen()來監聽這個socket,如果客戶端這時調用connect()發出連接請求,服務器端就會接收到這個請求。

listen的函數原型如下

int  listen(SOCKET s,int backlog);

第一個參數為要監聽的socket描述字;

第二個參數為相應socket可以排隊的最大連接個數。()(當客戶鏈接請求大於這數時(即緩沖池滿),其它的未進入鏈接緩沖池的客戶端在tcp層上tcp模塊會自動重新鏈接,直到超時(大約57秒後))

如果listen錯誤,返回-1,

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved