Search code examples
cwindowssocketsnetworkingwinsock2

winsock2 socket connection works within local pc, but not from remote pcs


I'm trying to learn low level networking in c, and have gotten as far as making TCP client and server programs that make, bind, connect, accept, etc. sockets using winsock.

They work just fine, and my simple chat console works as intended when both programs are running on the same machine, but connecting from a client on another pc over my local network, or another network, fails to communicate over the socket connection.

Here are my server:

#include "sockets.h"

int main(){

    // SETUP AND BIND SERVER SOCKET

    SOCKET server_socket = socket_setup();
    SOCKADDR_IN address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr("192.168.86.179"); //using LAN IP, not local (127.0.0.1)
    address.sin_port = htons(6502);

    if(bind(server_socket, (SOCKADDR*)&address, sizeof(address)) == SOCKET_ERROR)
        WSA_ERR("Socket Bind Error");

    if(listen(server_socket, 5) == SOCKET_ERROR)
        WSA_ERR("Listening Error");

    SOCKET accept_socket = SOCKET_ERROR;
    do{
        accept_socket = accept(server_socket, NULL, NULL);
    }
    while(accept_socket == SOCKET_ERROR);

    // SEND AND RECEIVE BYTES

    char sendmsg[100] = "Hello Client";
    char recvmsg[100] = "";

    int bytes_sent = send(accept_socket, sendmsg, strlen(sendmsg), 0);
    if(bytes_sent == SOCKET_ERROR){
        WSA_ERR("Send Error");
    }else{
        printf("Send OK\n");
        printf("Bytes Sent: %ld\n", bytes_sent);
        printf("%s\n", sendmsg);
    }

    int bytes_rcvd = blocking_recv(accept_socket, recvmsg);
    if(bytes_rcvd == SOCKET_ERROR){
        WSA_ERR("Recv Error");
    }else{
        printf("Recv OK\n");
        printf("Bytes Rcvd: %ld\n", bytes_rcvd);
        printf("%s\n", recvmsg);
    }

    chat_loop(recvmsg, sendmsg, accept_socket);
    return 0;
}

and client:

#include "sockets.h"

int main(){

    // SETUP AND CONNECT CLIENT SOCKET

    SOCKET client_socket = socket_setup();
    SOCKADDR_IN address;
    address.sin_family = AF_INET;
    printf("Enter IP address:\n");
    char ip[15];
    gets_s(ip, 15);
    address.sin_addr.s_addr = inet_addr(ip);
    address.sin_port = htons(6502);

    if(connect(client_socket, (SOCKADDR*)&address, sizeof(address)) == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK){
        WSA_ERR("Connection Error");
    }

    // SEND AND RECIEVE BYTES

    char sendmsg[100] = "Hello Server";
    char recvmsg[100] = "";

    int bytes_rcvd = blocking_recv(client_socket, recvmsg);
    if(bytes_rcvd == SOCKET_ERROR){
        WSA_ERR("Recv Error"); //<-- WSAGetLastError() from inside macro gives error 10057 here
    }else{
        printf("Recv OK\n");
        printf("Bytes Rcvd: %ld\n", bytes_rcvd);
        printf("%s\n", recvmsg);
    }

    int bytes_sent = send(client_socket, sendmsg, strlen(sendmsg), 0);
    if(bytes_sent == SOCKET_ERROR){
        WSA_ERR("Send Error");
    }else{
        printf("Send OK\n");
        printf("Bytes Sent: %ld\n", bytes_sent);
        printf("%s\n", sendmsg);
    }

    chat_loop(recvmsg, sendmsg, client_socket);

    system("pause");
    return 0;
}

This function is part of "sockets.h":

int blocking_recv(SOCKET sock, char buf[]){
    int result = 0;
    do{
        result = recv(sock, buf, 100, 0);
    }while(result == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK);
    return result;
}

I can elaborate on excluded code and macros if needed, but I think the problem must be in here somewhere since everything works fine except for the actual connections.

I've already disabled all firewalls, forwarded port, etc. I'm using non-blocking sockets.


Solution

  • The server needs to bind() to a local IP that belongs to the machine which the server is running on. Otherwise, you can bind() the server to INADDR_ANY (0.0.0.0) instead to bind to all local IPs on the machine.

    The client can connect() to any IP that the server is bound to and listening on:

    • If the client and server are running on the same machine, you can use any local IP, or INADDR_LOOPBACK (127.0.0.1).

    • If the client and server are running on the same LAN network, you can bind() the server to the machine's LAN IP or INADDR_ANY, and then connect() the client to the server's LAN IP.

    • If the client and server are running on different networks, the client must connect() to a public WAN IP that belongs to the server's network, AND the WAN/LAN router on that network must be configured with port forwarding enabled to pass incoming connections from a WAN IP/Port to the LAN IP/Port that the server is listening on. Depending on the router's configuration, its port forwarding rules MAY be configurable dynamically directly from the server's code via the uPNP protocol, otherwise the rules will have to be configured manually by the network admin.


    Now, that being said, something I do notice in your code - you say you are using non-blocking sockets. That means connect() will fail with a WSAEWOULDBLOCK (10035) error if the connection is successfully started in the background and in progress, however you are not waiting for that connection to actually finish being established before you then call send()/recv() on the socket. That would account for the WSAENOTCONN (10057) error you are seeing.

    You need to use select() or equivalent to make sure a non-blocking connection is fully established before you start exchanging data over it, eg:

    if (connect(client_socket, (SOCKADDR*)&address, sizeof(address)) == 0) {
        printf("Connected\n");
    }
    else if (WSAGetLastError() != WSAEWOULDBLOCK) {
        WSA_ERR("Connection Error");
    }
    else {
        fd_set wfds;
        FD_ZERO(&wfds);
        FD_SET(client_socket, &wfds);
    
        fd_set efds;
        FD_ZERO(&efds);
        FD_SET(client_socket, &efds);
    
        timeval timeout;
        timeout.tv_sec = 10;
        timeout.tv_usec = 0;
    
        int res = select(0, NULL, &wfds, &efds, &timeout);
        if (res == SOCKET_ERROR) {
            WSA_ERR("Select Error");
        }
        else if (res == 0) {
            WSASetLastError(WSAETIMEDOUT);
            WSA_ERR("Connection Error");
        }
        else if (FD_ISSET(client_socket, &efds)) {
            int err = 0;
            if (getsockopt(client_socket, SOL_SOCKET, SO_ERROR, &err)) == 0) {
                WSASetLastError(err);
            }
            WSA_ERR("Connection Error");
        }
        else {
            printf("Connected\n");
        }
    }