Потокобезопасность функций WinAPI
Пишу функцию, реализующую функционал утилиты ping. Функция вызывается внутри цикла для каждого значения IP-адреса, который она проверяет, каждый вызов происходит в отдельном потоке. Пишу на Qt с использованием классов: QTreadPool, QMutex и QMutexLocker. Являются ли функции gethostbyname, getprotobyname, memcpy, sendto, socket, setsockopt, recvfrom и WSAStartup потокобезопасными? Первый раз работаю с многопоточностью. При запуске в разных потоках получаю неопределённое поведение. При запуске в одном потоке всё работает верно.
#include "ips_socket.h"
#include "include/log.h"
#include "classes/binary_defines.h"
#include "include/error_defines.h"
#include "include/error_messages.h"
#include "include/network/unix_socket.h"
#include "include/network/win_socket.h"
#include <QMutexLocker>
#ifdef Q_OS_UNIX
#include <fcntl.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/ip_icmp.h>
#endif
IPSSocket::IPSSocket(QTcpSocket* parent)
: inherited(parent)
{
}
void IPSSocket::createConnectionToHost(QTcpSocket* socket, QString const& host, std::uint16_t port) noexcept
{
QMutexLocker locker(&m_mutex);
return socket->connectToHost(host, port);
}
#ifdef Q_OS_WIN
bool IPSSocket::preparingData(const char* host) noexcept
{
QMutexLocker locker(&m_mutex);
if ((m_sock.lpHostEntry = gethostbyname(host)) == nullptr)
return false;
m_sock.sockAddrLocal.sin_family = AF_INET;
m_sock.sockAddrLocal.sin_addr = *(reinterpret_cast<LPIN_ADDR>(*m_sock.lpHostEntry->h_addr_list));
if ((m_sock.lpProtocolEntry = getprotobyname("icmp")) == nullptr)
m_sock.nProtocol = IPPROTO_ICMP;
else
m_sock.nProtocol = m_sock.lpProtocolEntry->p_proto;
return true;
}
void IPSSocket::sendData(win_socket::icmp icmp) noexcept
{
QMutexLocker locker(&m_mutex);
memcpy(&m_sock.IcmpSendPacket, &icmp, sizeof(win_socket::icmp));
m_sock.iSentBytes = sendto(m_sock.hsocket, reinterpret_cast<LPSTR>(m_sock.IcmpSendPacket), m_sock.iPacketSize,
NO_FLAGS, reinterpret_cast<LPSOCKADDR>(&m_sock.sockAddrLocal), sizeof(m_sock.sockAddrLocal));
}
bool IPSSocket::createSocket() noexcept
{
QMutexLocker locker(&m_mutex);
if ((m_sock.hsocket = socket(PF_INET, SOCK_RAW, m_sock.nProtocol)) == INVALID_SOCKET) {
return false;
}
return true;
}
unsigned short IPSSocket::checksum(void *b, int len)
{
QMutexLocker locker(&m_mutex);
unsigned short *buf = static_cast<unsigned short *>(b);
unsigned int sum=0;
unsigned short result;
for (sum = 0; len > 1; len -= 2)
sum += *buf++;
if (len == 1)
sum += *(reinterpret_cast<unsigned char*>(buf));
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
result = static_cast<unsigned short>(~sum);
return result;
}
bool IPSSocket::setOptionsSocket() noexcept
{
QMutexLocker locker(&m_mutex);
DWORD timeOut = static_cast<unsigned long>(SOCKET_TIMEOUT);
return !setsockopt(m_sock.hsocket, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<const char*>(&timeOut), sizeof(timeOut));
}
win_socket::icmp IPSSocket::setICMPHeader() noexcept
{
QMutexLocker locker(&m_mutex);
win_socket::icmp pkg;
pkg.icmp_type = ICMP_ECHO;
pkg.icmp_cksum = 0;
pkg.icmp_code = 0;
pkg.icmp_seq = 0;
pkg.icmp_id = HINSTANCE(1);
pkg.icmp_cksum = checksum(reinterpret_cast<unsigned short *>(&pkg), sizeof(pkg));
m_sock.iPacketSize = sizeof(pkg);
return pkg;
}
void IPSSocket::reciveData() noexcept
{
QMutexLocker locker(&m_mutex);
m_sock.iHostAddrLength = sizeof(m_sock.sockAddrHost);
m_sock.iReceivedBytes = recvfrom(m_sock.hsocket, reinterpret_cast<LPSTR>(m_sock.IcmpRecvPacket),
sizeof(m_sock.IcmpRecvPacket), NO_FLAGS, reinterpret_cast<LPSOCKADDR>(&m_sock.sockAddrHost), &m_sock.iHostAddrLength);
}
bool IPSSocket::controlData() noexcept
{
QMutexLocker locker(&m_mutex);
m_pIpHeader = reinterpret_cast<struct win_socket::ip*>(m_sock.IcmpRecvPacket);
m_sock.iIPHeadLength = (m_pIpHeader->ip_verlen >> 4) << 2;
if (m_sock.iReceivedBytes < m_sock.iIPHeadLength + ICMP_HEADERSIZE)
return false;
return true;
}
bool IPSSocket::pingICMPWin(const char* host)
{
QMutexLocker locker(&m_mutex);
WSADATA wsaData;
WSAStartup(WINSOCK_VERSION, &wsaData);
if(!preparingData(host))
return false;
if(!createSocket())
return false;
if(!setOptionsSocket())
return false;
sendData(setICMPHeader());
reciveData();
closesocket(m_sock.hsocket);
if(!controlData())
return false;
WSACleanup();
return true;
}
#endif