Winsock ‘accept’ creating/returning sockets on the same port as the listener socket

  Kiến thức lập trình

I am creating a listener socket to listen for client connections on a particular port. When accept() is invoked, my understanding is that it should return a newly created socket on a unique port (so the listener can continue accepting clients on the connection/listener port).

However, the behavior I am seeing is that the newly created socket returned by accept() is bound to the same port that the listener socket is on. My below example accepts only one client, but I see the same behavior even if accepting multiple clients – every “handler” socket gets created on the same port.

I doubt this is a bug in the Winsock library, but I’m running out of ideas. My best guess is that I have a misunderstanding about how to properly deal with accepting clients onto new sockets.

Notes:

  • I’m not seeing any errors from the library function calls
  • The sockets returned by accept() all have unique socket IDs (so they appear to be valid new sockets)

Here is a simple program that should demonstrate the issue (at least it does for me). It should build as-is, only linking to ws2_32. Simply run 2 instances of the executable – one server and one client. Then enter the server connection info in the client instance to connect and see the issue.

#include <winsock2.h>
#include <WS2tcpip.h>
#include <iostream>
#include <string>

#pragma comment(lib, "Ws2_32.lib")

int RunServer();
int RunClient();

int main()
{
    // Initialize Winsock (Windows socket library)
    std::cout << "Initializing Winsock..." << std::endl;
    WSACleanup();
    WSADATA wsaData;
    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        std::cout << "Failed to initialize Winsock" << std::endl;
        return 1;
    }

    // Run instance as either the server or the client (ask the user)
    char choice;
    std::cout << std::endl << "Do you want to run as a server (s) or client (c)?: ";
    std::cin >> choice;
    std::cout << std::endl;
    if (choice == 's')
    {
        RunServer();
    }
    else
    {
        RunClient();
    }

    // Cleanup
    WSACleanup();
}

int RunServer()
{
    // Create listener socket
    std::cout << "Creating listener socket..." << std::endl;
    SOCKET listenerSocket = INVALID_SOCKET;
    listenerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (listenerSocket == INVALID_SOCKET)
    {
        std::cerr << "Error creating socket: " << WSAGetLastError() << std::endl;
        return 1;
    }

    // Bind the socket
    std::cout << "Binding the listener socket..." << std::endl << std::endl;
    sockaddr_in service;
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = 0x0189A8C0; // 192.168.137.1
    service.sin_port = htons(12345);
    int iResult = bind(listenerSocket, (SOCKADDR*)&service, sizeof(service));
    if (iResult == SOCKET_ERROR)
    {
        std::cerr << "bind failed: " << WSAGetLastError() << std::endl;
        return 1;
    }

    // Get & print listener socket info
    sockaddr_in listenerSocketAddress{};
    int listenerSocketAddressLength = sizeof(listenerSocketAddress);
    getsockname(listenerSocket, reinterpret_cast<struct sockaddr*>(&listenerSocketAddress), &listenerSocketAddressLength);
    std::cout << "Listener IP Address: " << inet_ntoa(listenerSocketAddress.sin_addr) << std::endl;
    std::cout << "** Listener Port: " << ntohs(listenerSocketAddress.sin_port) << " **" << std::endl << std::endl;

    // Listen
    std::cout << "Listening for clients..." << std::endl;
    iResult = listen(listenerSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR)
    {
        std::cerr << "listen failed: " << WSAGetLastError() << std::endl;
        return 1;
    }

    // Accept
    std::cout << "Waiting to accept client..." << std::endl << std::endl;
    sockaddr_in clientSocketAddress{};
    int clientSocketAddressLength = sizeof(clientSocketAddress);
    SOCKET clientHandlerSocket = INVALID_SOCKET;
    clientHandlerSocket = accept(listenerSocket, reinterpret_cast<struct sockaddr*>(&clientSocketAddress), &clientSocketAddressLength);
    if (clientHandlerSocket == INVALID_SOCKET)
    {
        std::cerr << "accept failed: " << WSAGetLastError() << std::endl;
        return 1;
    }
    std::cout << "Accepted a client!" << std::endl;
    std::cout << "Client's IP Address: " << inet_ntoa(clientSocketAddress.sin_addr) << std::endl;
    std::cout << "Client's Port: " << ntohs(clientSocketAddress.sin_port) << std::endl << std::endl;

    // Get & print the weird client handler socket info shenanigans
    sockaddr_in clientHandlerSocketAddress{};
    int clientHandlerSocketAddressLength = sizeof(clientHandlerSocketAddress);
    if (getsockname(clientHandlerSocket, reinterpret_cast<struct sockaddr*>(&clientHandlerSocketAddress), &clientHandlerSocketAddressLength) == SOCKET_ERROR)
    {
        std::cerr << "getsockname failed: " << WSAGetLastError() << std::endl;
        return 1;
    }
    std::cout << "Connected to Client via IP Address: " << inet_ntoa(clientHandlerSocketAddress.sin_addr) << std::endl;
    std::cout << "** Connected to Client via Port: " << ntohs(clientHandlerSocketAddress.sin_port) << " **" << std::endl << "^^^ This should have been created on a different port than the listener socket, right??" << std::endl << std::endl;

    // Double check that the new socket (returned by 'accept') is actually connected to the expected client
    //sockaddr_in clientSocketAddress2{};
    //int clientSocketAddress2Length = sizeof(clientSocketAddress2);
    //if (getpeername(clientHandlerSocket, reinterpret_cast<struct sockaddr*>(&clientSocketAddress2), &clientSocketAddress2Length) == SOCKET_ERROR)
    //{
    //    std::cerr << "getpeername failed: " << WSAGetLastError() << std::endl;
    //    return 1;
    //}
    //std::cout << "Double checking the connected client... (this should match the info that 'accept' returned)" << std::endl;
    //std::cout << "Client's IP Address: " << inet_ntoa(clientSocketAddress2.sin_addr) << std::endl;
    //std::cout << "Client's Port: " << ntohs(clientSocketAddress2.sin_port) << std::endl << std::endl;

    system("PAUSE");
    return 0;
}

int RunClient()
{
    // Create client socket
    std::cout << "Creating client socket..." << std::endl;
    SOCKET clientSocket = INVALID_SOCKET;
    clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (clientSocket == INVALID_SOCKET)
    {
        std::cerr << "Error creating socket: " << WSAGetLastError() << std::endl;
        return 1;
    }

    // Request server socket info from the user
    std::string serverIP;
    int serverPort;
    std::cin.ignore();
    std::cout << std::endl << "Enter the server's IP address: ";
    std::getline(std::cin, serverIP);
    std::cout << "Enter the server's port number: ";
    std::cin >> serverPort;
    std::cin.ignore();
    if (serverIP.empty())
    {
        // If IP is omitted, I guess try localhost?
        serverIP = "127.0.0.1";
    }

    // Connect to the server
    sockaddr_in serverAddr{};
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(serverPort);
    struct in_addr address {};
    if (inet_pton(AF_INET, serverIP.c_str(), &address) != 1)
    {
        // Error handling: Invalid IP address format
        return 1;
    }
    serverAddr.sin_addr.s_addr = address.s_addr;
    int status = connect(clientSocket, reinterpret_cast<struct sockaddr*>(&serverAddr), sizeof(serverAddr));
    if (status != 0)
    {
        std::cerr << "connect failed: " << WSAGetLastError() << std::endl;
        return 1;
    }

    std::cout << std::endl << "Connected to server!" << std::endl << std::endl;

    // Double check that the new socket (returned by 'accept') is actually connected to the expected client
    sockaddr_in serverSocketAddress{};
    int serverSocketAddressLength = sizeof(serverSocketAddress);
    if (getpeername(clientSocket, reinterpret_cast<struct sockaddr*>(&serverSocketAddress), &serverSocketAddressLength) == SOCKET_ERROR)
    {
        std::cerr << "getpeername failed: " << WSAGetLastError() << std::endl;
        return 1;
    }
    std::cout << "Server's comm socket IP Address: " << inet_ntoa(serverSocketAddress.sin_addr) << std::endl;
    std::cout << "** Server's comm socket Port: " << ntohs(serverSocketAddress.sin_port) << " **" << std::endl;
    std::cout << "^^^ The server should be communicating on a different port than the connection port, right??" << std::endl << std::endl;

    system("PAUSE");

    return 0;
}

New contributor

DuhBulYew is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website

LEAVE A COMMENT