Я пытаюсь выполнить очень простую настройку клиент / сервер UDP с неблокирующими сокетами. Я изолировал свою проблему, чтобы сервер не привязывался к запрошенному номеру порта и вместо этого произвольно связывался с одним (хотя, похоже, он постоянно совпадает с одним и тем же портом).
Можно ли запросить определенный номер порта?
Я пытаюсь не указывать IPv4 или IPv6. Поэтому я не уверен, нужно ли мне использовать getaddrinfo
, если мне наплевать на IP-адрес, но я подумал, что таким образом он найдет то, что было доступно, или еще лучше, прослушать все IP-адреса, приходящие на этот адрес. порт? Но он сообщает ::
, что вызывает у меня проблему, если я позже пытаюсь сообщить своим клиентам, к какому IP-адресу подключаться.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Ws2tcpip.h>
#include <cstring>
#include <iostream>
#include <chrono>
#include <iomanip>
#include <sstream>
static uint16_t SERVER_PORT = 56000;
std::wstring get_error_string(const DWORD error)
{
std::wstring returnResult = L"";
LPWSTR errorText = NULL;
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&errorText,
0,
NULL);
if(NULL != errorText)
{
returnResult = errorText;
LocalFree(errorText);
errorText = NULL;
}
std::time_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm tm = *std::localtime(&t);
std::wstringstream sstr;
sstr << std::put_time<wchar_t>(&tm, L"%d%m%y %H:%M:%S");
return sstr.str() + L" ERROR (" + std::to_wstring(error) + L"): " + returnResult;
}
std::string convert_to_string(const sockaddr* addr)
{
char* str;
if(addr->sa_family == AF_INET)
{
str = new char[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(reinterpret_cast<const sockaddr_in*>(addr)->sin_addr), str, INET_ADDRSTRLEN);
}
else
{
str = new char[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_addr), str, INET6_ADDRSTRLEN);
}
std::string returnVal = str;
if(addr->sa_family == AF_INET)
returnVal += ":" + std::to_string(reinterpret_cast<const sockaddr_in*>(addr)->sin_port);
else
returnVal = "[" + returnVal + "]:" + std::to_string(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port);
return returnVal;
}
int main()
{
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2,2), &wsaData);
if(result != 0)
{
std::wcerr << L"Network init failed: ";
switch(result)
{
case WSASYSNOTREADY:
std::wcerr << L"WSASYSNOTREADY";
break;
case WSAVERNOTSUPPORTED:
std::wcerr << L"WSAVERNOTSUPPORTED";
break;
case WSAEINPROGRESS:
std::wcerr << L"WSAEINPROGRESS";
break;
case WSAEPROCLIM:
std::wcerr << L"WSAEPROCLIM";
break;
case WSAEFAULT:
std::wcerr << L"WSAEFAULT";
break;
default:
std::wcerr << L"Unknown error code " << std::to_wstring(result);
}
std::wcerr << std::endl;
return 0;
}
addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
addrinfo* serverInfoList;
int status = getaddrinfo(nullptr, std::to_string(SERVER_PORT).c_str(), &hints, &serverInfoList);
if(status != 0)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR getaddrinfo: " << get_error_string(lastError) << std::endl;
}
addrinfo* addr = nullptr;
SOCKET sock;
for(addr = serverInfoList; addr != nullptr; addr = addr->ai_next)
{
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if(sock == INVALID_SOCKET)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR socket: " << get_error_string(lastError) << std::endl;
continue; // we'll try the next option
}
u_long blocking = 1;
ioctlsocket(sock, FIONBIO, &blocking);
BOOL conrest = FALSE;
DWORD dwBytesReturned = 0;
WSAIoctl(sock, _WSAIOW(IOC_VENDOR, 12), &conrest, sizeof(conrest), NULL, 0, &dwBytesReturned, NULL, NULL);
int flag = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&flag), (socklen_t)sizeof(flag));
if(bind(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR bind: " << get_error_string(lastError) << std::endl;
if(closesocket(sock) == SOCKET_ERROR)
{
lastError = WSAGetLastError();
std::wcerr << L"ERROR closesocket: " << get_error_string(lastError) << std::endl;
}
continue;
}
sockaddr_storage connectedDetails;
std::memset(&connectedDetails, 0, sizeof(connectedDetails));
int addrLen = sizeof(connectedDetails);
if(getsockname(sock, reinterpret_cast<sockaddr*>(&connectedDetails), &addrLen) == SOCKET_ERROR)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR getsockname: " << get_error_string(lastError) << std::endl;
}
std::cout << "Connected to " << convert_to_string(reinterpret_cast<sockaddr*>(&connectedDetails)) << std::endl;
break; // we've bound to one of them
}
if(addr == nullptr)
{
std::wcerr << L"ERROR: Could not bind to any addr for localhost port \"" << std::to_wstring(SERVER_PORT) << "\"" << std::endl;
return false;
}
freeaddrinfo(serverInfoList);
while(!(GetAsyncKeyState(VK_ESCAPE) & 0x8000))
{
}
result = WSACleanup();
if(result != 0)
{
std::wcerr << L"Network cleanup failed: ";
switch(result)
{
case WSANOTINITIALISED:
std::wcerr << L"WSANOTINITIALISED";
break;
case WSAENETDOWN:
std::wcerr << L"WSAENETDOWN";
break;
case WSAEINPROGRESS:
std::wcerr << L"WSAEINPROGRESS";
break;
default:
std::wcerr << L"Unknown error code " << std::to_wstring(result);
}
std::wcerr << std::endl;
}
return 0;
}
Я ожидаю, что он привязывается к порту 56000
, но он привязывается к другим портам.