Пишу простой UDP сервер под Windows, который принимает сообщения от клиентов. По заданию сокеты должны работать в неблокирующем режиме и обязательно нужно использовать WSAPoll для параллельного обслуживания клиентов. Написал небольшую обертку для сокета:
class _socket
{
private:
SOCKET sock;
sockaddr_in addr;
public:
_socket(int port);
_socket(const _socket & other);
~_socket();
SOCKET GetSocket();
bool Generate();
bool Bind();
};
_socket::~_socket()
{
if (sock != INVALID_SOCKET)
{
closesocket(sock);
}
}
_socket::_socket(int port) : sock(INVALID_SOCKET)
{
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
}
_socket::_socket(const _socket & other)
{
this->sock = other.sock;
this->addr = other.addr;
}
SOCKET _socket::GetSocket()
{
return (this->sock);
}
bool _socket::Generate()
{
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0)
{
return false;
}
else
{
unsigned long mode = 1;
return (ioctlsocket(sock, FIONBIO, &mode) != SOCKET_ERROR);
}
}
bool _socket::Bind()
{
return (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) >= 0);
}
Вот так работаю с ним в main (через argv передаю диапазон портов):
int main(int argc, const char *argv[])
{
std::vector<_socket> sockets;
std::map
if (argc < 3)
{
return ErrorPrint("Too low arguments");
}
if (!Init())
{
return ErrorPrint("Error starting WSA");
}
for (int i = atoi(argv[1]); i <= atoi(argv[2]); i++)
{
_socket s(i);
if (!s.Generate())
{
return ErrorPrint("Cannot create socket");
}
if (!s.Bind())
{
return ErrorPrint("Cannot bind socket");
}
sockets.push_back(s);
}
WSAPOLLFD *pfd = (WSAPOLLFD*)malloc(sockets.size() * sizeof(WSAPOLLFD));
for (int i = 0; i < sockets.size(); i++)
{
pfd[i].fd = sockets[i].GetSocket();
pfd[i].events = POLLIN | POLLOUT;
pfd[i].revents = 0;
}
while (true)
{
int ev_cnt = WSAPoll(pfd, sockets.size(), 1000);
if (ev_cnt > 0)
{
for (int i = 0; i < sockets.size(); i++)
{
if (pfd[i].revents & POLLERR)
{
}
if (pfd[i].revents & POLLIN)
{
}
if (pfd[i].revents & POLLOUT)
{
}
}
}
else if (ev_cnt == SOCKET_ERROR)
{
std::cout << "Error WSAPoll: " << WSAGetLastError() << std::endl; // Постоянно попадаю сюда
}
else
{
std::cout << "waiting..." << std::endl;
}
}
return (EXIT_SUCCESS);
}
При вызове WSAPoll постоянно получаю ошибку WSAENOTSOCK (10038), которая говорит мне, что я пытаюсь выполнить операцию, предназначенную для сокета на чем-то, что им не является. Прочитал спецификации к WSAPoll - вроде все делаю правильно, никак не могу понять, что сделал не так.
Ответ
У вас реализован конструктор копирования после выполнения которого в двух экземплярах класса будет хранится один и тот же дескриптор сокета. После разрушения одного из экземпляров второй останется с невалидным дескриптором. Следовало реализовать конструктор перемещения (и перемещающий оператор присваивания):
_socket(const _socket & other) = delete;
_socket(_socket && other)
: sock{other.sock}
, addr{other.addr}
{
other.sock = INVALID_SOCKET;
::std::memset(::std::addressof(other.addr), 0, sizeof(other.addr));
}
Сокеты на самом деле можно дублировать используя WSADuplicateSocket, но обычно это не нужно.
Комментариев нет:
Отправить комментарий