UDP-сервер привязан к неправильному номеру порта - PullRequest
1 голос
/ 04 июля 2019

Я пытаюсь выполнить очень простую настройку клиент / сервер 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, но он привязывается к другим портам.

1 Ответ

0 голосов
/ 04 июля 2019

Самый простой способ - это создать sockaddr_in самостоятельно, так как вы хотите связать все адреса.

// Create IPv6 UDP socket
X = socket(AF_INET6,SOCK_DGRAM,IPPROTO_UDP);

// Allow both ipv6/ipv4 bind  
DWORD ag = 0;  
setsockopt(X,IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ag, sizeof(DWORD));

sockaddr_in s = {0};  
s.sin_port = htons(SERVER_PORT);  
s.sin_family= AF_INET6;
if (bind(X,(sockaddr*)&s,sizeof(s)) == 0) { ... }
...