1

我正在为 TCP/IP 网络客户端通信编写一个类。在类头中,我创建了一个 SOCKET 成员。该类还包含一个调用 WSAStartup 和检查版本的方法。建立连接的方法首先调用 WSAStartup,然后初始化调用 socket() 函数的 SOCKET 成员。请参阅下面的代码。我想知道这是否“正确”,或者是否有更好的方法。

头文件:

/*network.h*/
public class IPnetwork
{
private:
    WSADATA wsaData ;
    SOCKET hSocket ;
    sockaddr_in socketAddress ;
    static const int SERVER_PORT = 502 ;
    unsigned long int = serverIP ;

public:
    IPnetwork(char* serverIPaddress) ;
    bool Connect() ;
    bool Disconnect() ;
    ~IPnetwork() ;

private:
    bool startWinSock() ;
} ;

源代码:

/*network.cpp*/
IPnetwork::IPnetwork(char* serverIPaddress)
{
    serverIP = inet_addr(serverIPaddress) ;
}

bool IPnetwork::Connect()
{
     /* start winsock */
    if(!startWinSock())
    {
        return false ; /* winsock problem */
    }

    /* Create socket */
    if ((hSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        return false ; /* could not create socket */
    }

    /* fill socket address structure */
    socketAddress.sin_family = AFINET ;
    socketAddress.sin_port = htons(SERVER_PORT) ;
    socketAddress.sin_addr.S_un.S_addr = serverIP ;

    /* connect */
    if(connect(hSocket,reinterpret_cast<sockaddr*>(&socketAddress), sizeof(sockAddr))!=0)
    {
        return false ; /* could not connect*/
    }

    return true ;

}

bool IPnetwork::startWinSock()
{
    if(WSAStartup(MAKEWORD(REQ_WINSOCK_VER,0),&wsaData)==0)
    {
        /* Check if major version is at least REQ_WINSOCK_VER */
        if (LOBYTE(wsaData.wVersion) < REQ_WINSOCK_VER)
        {
            return false ; /* winsock started but version is too low */
        }
        else
        {
            return true ; /* winsock started with right version*/
        }
    }
    else
    {
        return false ; /* winsock not started */
    }

}
4

3 回答 3

4

您担心定义一个 type 的变量SOCKET,以及它的构造函数何时运行?

这不是问题,因为SOCKET它是一个 C 兼容的普通旧数据整数类型,它包含一个套接字标识符。它不是一个对象。没有与变量本身相关的重要构造或破坏。

于 2013-03-11T16:33:58.713 回答
2

WSAStartup()必须在调用之前socket()调用,否则会返回WSANOTINITIALISED错误。

您可以WSAStartup()多次调用。最好WSAStartup()只调用一次,但 WinSock 是引用计数的,因此如果需要可以多次调用。您必须确保WSACleanup()每次WSAStartup()成功调用一次,否则引用计数将不平衡。例如,您可以调用WSAStartup()构造函数并调用WSACleanup()析构函数:

public class IPnetwork
{
private:
    WSADATA wsaData ;
    bool wsaInit;
    ...

public:
    IPnetwork(char* serverIPaddress) ;
    ~IPnetwork() ;
} ;

.

IPnetwork::IPnetwork(char* serverIPaddress)
{
    wsaInit = (WSAStartup(MAKEWORD(REQ_WINSOCK_VER,0), &wsaData) == 0);
    ...
}

IPnetwork::~IPnetwork()
{
    if (wsaInit) WSACleanup();
}

bool IPnetwork::Connect()
{
    if (!wsaInit)
        return false ; /* winsock problem */
    ...
}

虽然我通常会在单独的单例类中调用它们。

于 2013-03-11T18:40:58.693 回答
1

虽然我同意@Ben 的观点,即您正在做的事情很好,但我个人会尝试从您的代码主线中消除这种丑陋。IMO,这是在这样的标题中创建全局变量的罕见情况之一:

#ifndef WS_INITIATOR_INC_
#define WS_INITIATOR_INC_

#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")

#include <stdexcept>

struct bad_version : public std::logic_error {
    bad_version(std::string const &s) : logic_error(s) {}
};

struct winsock { 
    static const int version = 2;
    WSADATA wsaData;

    winsock() { 
        WSAStartup(MAKEWORD(2, 2),&wsaData);
        if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
            throw bad_version("Could not initialize WinSock 2.2");
    }
    ~winsock() { 
        WSACleanup();
    }
} ws_initiator;

#endif

将此包含在您的main.cpp(或您为包含 的文件提供的任何名称main)中,它会自动初始化/清理套接字(以及与正确的库链接)。

于 2013-03-11T16:56:56.310 回答