2013-03-11 3 views
1

Я пишу класс для связи с сетевым клиентом TCP/IP. В заголовке класса я создаю член SOCKET. Класс также содержит метод вызова WSAStartup и проверки версии. Метод для соединения сначала вызывает WSAStartup, а затем инициализирует член SOCKET, вызывающий функцию socket(). См. Код ниже. Мне интересно, правильно ли это, или если есть лучший способ.Могу ли я создать экземпляр сокета перед вызовом WSAStartup()?

Заголовочный файл:

/*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 */ 
    } 

} 
+0

Это 'socket (...)' фактически создающий сокет, а не предполагаемый конструктор типа SOCKET. Так что ты в порядке. –

ответ

4

Вы беспокоитесь об определении переменной типа SOCKET, а когда его конструктор работает?

Это не проблема, так как SOCKET является совместимым с C-совместимым стандартным типом данных, который содержит идентификатор сокета. Это не объект. Нет никакой нетривиальной конструкции или разрушения, связанных с самой переменной.

+0

Спасибо, это то, о чем я беспокоился. – meltnot

1

Хотя я согласен с @Ben в том, что то, что вы делаете, хорошо, я лично попытаюсь получить это уродство из основной линии вашего кода. ИМО, это один из редких случаев, когда это целесообразно создать глобальную переменную, в заголовке что-то вроде этого:

#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 

Include это в вашем main.cpp (или любое другое имя дать файлу, содержащему main), и он автоматизирует инициализацию/очистку сокетов (а также связывание с правой библиотекой).

+0

Спасибо, это действительно помогло с лучшим дизайном кода! – meltnot

+0

Безопасен ли он для использования в библиотеке, которая «конфиденциально» использует winsockets, и основное приложение может не знать об этом? {Подумайте о DLL COM-компонента inprocess COM – sehe

+0

@sehe: Да, для такой ситуации это немного сложнее - вам нужно определить экземпляр объекта 'winsock' как члена (например) класса' socket' , а не как глобальная переменная. Например: http://codereview.stackexchange.com/a/46354/489 –

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 */ 
    ... 
} 

Хотя обычно я бы назвал их внутри отдельного одноэлементного класса.

+0

+1 для вызова WSAStartup() в конструкторе, а затем WSACleanup() в деструкторе. –

Смежные вопросы