通信編程:Winsock 接口載入


Winsock 編程接口

Winsock 是 Windows 下網絡編程的規范,該規范是 Windows 下得到廣泛應用的、開放的、支持多種協議的網絡編程接口。從 1991 年的 1.0 版到 1995 年的 2.0.8 版,經過不斷完善並在 Intel、Microsoft、Sun、SGI、Informix、Novell 等公司的全力支持下,已成為 Windows 網絡編程的事實上的標准。——百度百科

通過 Winsock 編程接口就可以令多個應用程序通過網絡來進行通信,Winsock 編程接口有 Winsock1 和 Winsock2 兩個版本,目前主要使用 Winsock2 來進行開發。想要使用 Winsock2 庫,就需要包含頭文件來使用相關的 socket 函數和結構體,同時還要添加到 WS2_32.lib 的鏈接。

#include <winsock2.h>
#pragma comment(lib, "WS2_32")  // 鏈接到 WS2_32.lib

Winsock 的載入和釋放

載入與釋放操作

每個基於 Winsock 開發的程序都需要載入對應版本的 Winsock DLL,這樣才能使用 Winsock 提供的工具包。 想要載入 Winsock 庫,需要使用 WSAStartup() 函數:

int
WSAAPI
WSAStartup(
    _In_ WORD wVersionRequested,
    _Out_ LPWSADATA lpWSAData
    );
參數 類型 數據類型 說明
wVersionRequested 輸入 WORD 指定要加載的 Winsock 版本
lpWSAData 返回值 LPWSADATA 一個指向 WSADATA 結構的指針

其中 wVersionRequested 參數有 2 個字節,高字節指定次版本號,低字節指定主版本號,一般來說使用 Winsock2 時高字節和低字節都是 2。建立這個參數時,可以使用 MAKEWORD(a, b) 宏。函數的返回值時 LPWSADATA 結構,里面存儲了加載的庫的版本相關信息。

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

想要釋放 Winksock 庫,可以使用 WSACleanup() 函數。

int
WSAAPI
WSACleanup(
    void
    );

CInitSock 類

由於每次使用 Winksock 程序都需要載入 Winksock 庫,因為從封裝性的角度來考慮,可以封裝一個工具類來專門載入和釋放 Winksock 庫。首先先簡單介紹一下 C++ 面向對象編程的構造器和析構器,注意和 Java 不同的是 Java 的類不需要寫析構器。

函數 函數名 返回值 功能
構造器 和類名相同 不需要用戶顯式調用,而是在創建對象時自動執行
析構器 在類名前面加一個 “~” 符號 不需要程序員顯式調用,而是在銷毀對象時自動執行

其實這個工具類只需要寫構造器和析構器即可,其中構造器需要使用 MAKEWORD(a, b) 宏給一個 WORD 指定版本號,然后調用 WSAStartup() 函數載入 Winsock2 庫。析構器則只需要調用 WSACleanup() 方法,目的就是在不需要使用 Winsock2 時自動把它釋放掉。

#include <winsock2.h>
#pragma comment(lib, "WS2_32")  // 鏈接到 WS2_32.lib

class CInitSock
{
public:
    /*CInitSock 的構造器*/
    CInitSock(BYTE minorVer = 2, BYTE majorVer = 2)
    {
        // 初始化WS2_32.dll
        WSADATA wsaData;
        WORD sockVersion = MAKEWORD(minorVer, majorVer);
        if (::WSAStartup(sockVersion, &wsaData) != 0)
        {
            exit(0);
        }
    }

    /*CInitSock 的析構器*/
    ~CInitSock()
    {
        ::WSACleanup();
    }
};

為了以后調用方便,這個工具類可以寫在 initsock.h 頭文件中。

Winsock 尋址方式

sockaddr_in 結構

Winsock 是 Windows 下網絡編程的規范,是支持多種協議的網絡編程接口,因此編址也需要顧及不同的協議棧。Winsock 的第一個版本使用 sockaddr 結構來編址,里面的 sa_family 成員制定了使用的編址方式。而對於 TCP/ IP 協議棧,可以直接使用 sockaddr_in 結構。

typedef struct sockaddr_in {

#if(_WIN32_WINNT < 0x0600)
    short   sin_family;
#else //(_WIN32_WINNT < 0x0600)
    ADDRESS_FAMILY sin_family;
#endif //(_WIN32_WINNT < 0x0600)

    USHORT sin_port;
    IN_ADDR sin_addr;
    CHAR sin_zero[8];
} SOCKADDR_IN, *PSOCKADDR_IN;

其中有幾個重要的成員:

成員變量 說明
sin_family 地址家族
sin_port 端口號
sin_addr IPv4 地址
sin_zero[8] 占位,用於和 sockaddr 結構大小對齊

其中對於 sin_family 變量必須使用 AF_INET 作為地址家族,表示使用 IP 編址。in_addr 結構用來存儲 IP 地址,底層是使用一個共用體 union 來實現,可以用 4 個 uchar 或 2 個 ushort 或 1 個 ulong 來存儲。

typedef struct in_addr {
        union {
                struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b;
                struct { USHORT s_w1,s_w2; } S_un_w;
                ULONG S_addr;
        } S_un;

sockaddr_in 結構初始化

因此對於 sockaddr_in 結構的初始化,實際上就是分別指定地址家族,綁定端口號和 IP 地址。

sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(4567);
sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

參考資料

《Windows 網絡與通信編程》,陳香凝 王燁陽 陳婷婷 張錚 編著,人民郵電出版社


免責聲明!

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



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