Я пишу класс для общения с клиентами по 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 */
}
}
Вы беспокоитесь об определении переменной типа SOCKET
, а когда его конструктор запускается?
Это не проблема, так как SOCKET
является C-совместимым типом простых старых данных, который содержит идентификатор сокета. Это не объект. Там нет нетривиальной конструкции или разрушения, связанных с самой переменной.
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 */
...
}
Хотя я обычно называю их внутри отдельного синглтон-класса.
Хотя я согласен с @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
), и он автоматизирует инициализацию / очистку сокетов (а также связывание с нужной библиотекой).