• 通信编程: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 网络与通信编程》,陈香凝 王烨阳 陈婷婷 张铮 编著,人民邮电出版社

  • 相关阅读:
    markdown with vim
    递归
    类 sizeof
    cppcheck工具
    c++ explicit的含义和用法
    pca主成分分析
    string的使用
    linux的shell进化简史
    adb shell 无法启动 (insufficient permissions for device)
    c++ 四种转换的意思
  • 原文地址:https://www.cnblogs.com/linfangnan/p/15383673.html
Copyright © 2020-2023  润新知