Страницы

Поиск по вопросам

четверг, 16 мая 2019 г.

Ошибка при вызове WSAPoll()

Пишу простой 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 clients;
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, но обычно это не нужно.

Комментариев нет:

Отправить комментарий