0

I am writing a server application on Raspberry Pi with communication on TCP/IP sockets and I have trouble with showing an IP of socket I am listening on. I have found two solutions and none of them are working.

1st solution:

char hostName[255];
struct hostent *host_entry;
char * szLocalIP;
gethostname(hostName, 255);
host_entry = gethostbyname(hostName);
szLocalIP = inet_ntoa (*(struct in_addr *)host_entry->h_addr);
printf("Server runs on IP: %s, port: %d\n\n", szLocalIP, ntohs(server_address.sin_port));

output: Server runs on IP: 0.0.0.0, port: 5000

2nd solution:

struct sockaddr_in server_address;
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
if (getnameinfo((struct sockaddr *)&server_address, server_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0)
         printf("Server runs on IP: %s, port: %s\n\n", hbuf, sbuf);

output: Server runs on IP: 127.0.1.1, port: 5000

full code:

#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <arpa/inet.h>
#include <netdb.h>

#define PORT 5000
#define MAX_QUEUE 3
#define NAME "Server"

int main()
{
    int server_file_descriptor, new_connection;
    long valread;
    unsigned long counter = 1;
    struct sockaddr_in server_address, client_address;
    socklen_t server_len, client_len;
    int opt = 1;
    char buffer[1024] = {0};

    char hostName[255];
    struct hostent *host_entry;
    char * szLocalIP;

    char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];

    char *hello = "Temperature send";

    if ((server_file_descriptor = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    if (setsockopt(server_file_descriptor, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
    {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = INADDR_ANY;
    server_address.sin_port = htons(PORT);

    server_len = sizeof(server_address);

    if (bind(server_file_descriptor, (struct sockaddr *)&server_address, server_len))
    {
        perror("bind failed");
        exit(EXIT_FAILURE);
    }

    if (listen(server_file_descriptor, MAX_QUEUE))
    {
        perror("listen failed");
        exit(EXIT_FAILURE);
    }

    gethostname(hostName, 255);
    host_entry = gethostbyname(hostName);
    szLocalIP = inet_ntoa (*(struct in_addr *)host_entry->h_addr);

    if (getnameinfo((struct sockaddr *)&server_address, server_len, hbuf, sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0)
         printf("1 - Server runs on IP: %s, port: %s\n\n", hbuf, sbuf);

    printf("2 - Server runs on IP: %s, port: %d\n\n", szLocalIP, ntohs(server_address.sin_port));
    printf("3 - Server runs on IP: %s, port: %d\n\n", inet_ntoa(server_address.sin_addr), ntohs(server_address.sin_port));

    client_len = sizeof(client_address);

    for (;;counter++)
    {
        if ((new_connection = accept(server_file_descriptor, (struct sockaddr *)&client_address, &client_len)) == -1)
        {
            perror("accept failed");
            exit(EXIT_FAILURE);
        }

        printf("%s: got connection from %s on port: %d.\n", NAME, inet_ntoa(client_address.sin_addr), ntohs(client_address.sin_port));

        valread = read( new_connection , buffer, 1024);

        printf("%s sends request: %s\n", inet_ntoa(client_address.sin_addr), buffer);

        if (strcmp(buffer, "get_temp") == 0)
        {
            send(new_connection , hello , strlen(hello) , 0 );
            printf("%s: message \"%s\" send to %s.\n", NAME, hello, inet_ntoa(client_address.sin_addr));
        }
        else
        {
            send(new_connection , "Unknown command\n" , 15 , 0 );
            printf("%s: message \"Unknown command\" send to %s.\n", NAME, inet_ntoa(client_address.sin_addr));
        }
        printf("%s: closing connection with: %s.\n", NAME, inet_ntoa(client_address.sin_addr));
        printf("Handled calls: %ld\n\n", counter);
        close(new_connection);
        memset(buffer, 0, sizeof(buffer));
    }
}

output of 3rd printf is the same as 1st.

  • I don't think DNS is the way to go. Maybe getsockname on the listening socket? But if you're listening on all interfaces that might come back as 0.0.0.0, so you'll need to enumerate all of the known IP addresses and work out which one you want. Some ideas here: https://stackoverflow.com/questions/122208/get-the-ip-address-of-local-computer – Rup Sep 18 '18 at 14:55
  • What is wrong with the second output? That's a potentially valid IP address – Chris Turner Sep 18 '18 at 15:06
  • 1
    my raspberry's local network IP is 192.168.40.21, I am connecting to it via SSH with this IP, so I think this is what has to be printed? – Krzysztof Tarnawski Sep 18 '18 at 17:25

1 Answers1

1

The answer 0.0.0.0 is the correct one.

More precisely, when you open a server socket, it does not have a specific, well-defined address. Therefore if you ask any system calls to "find the local address this socket is listening to" they will return the wildcard address 0.0.0.0. Furthermore, you can connect to the server using many different destination addresses: for example "127.0.0.1", "127.1.2.3", "192.168.40.21" (the address of your network interface), etc.

If you want to listen to a specific address, you need to pass that address to bind(). For example, this code listens to a specific local address 127.1.2.3:

server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("127.1.2.3");
server_address.sin_port = htons(PORT);
if (bind(server_file_descriptor, (struct sockaddr *)&server_address, sizeof(server_address)))
   ...

Output:

3 - Server runs on IP: 127.1.2.3, port: 5000

The problem is that connections to "127.0.0.1", "192.168.40.21" and all of the other addresses are now going to fail, since you have restricted the server to be reachable only on a specific address.

However, there is a thing that does have a well-defined address in your initial example. It is the client socket created after the connection is accepted. If you add this code after the accept() call, you will see the address the client has connected to:

    struct sockaddr_in addr;
    socklen_t len;
    len = sizeof(addr);
    getsockname(new_connection, (struct sockaddr *)&addr, &len);
    printf("Client connected to: %s, port: %d\n\n", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));

This address may be different for different clients: i.e. one client may connect to "127.0.0.1", another to "192.168.40.21".

kfx
  • 8,136
  • 3
  • 28
  • 52
  • Whilst that's correct, it's not useful if you did want to print "Server is now running: connect your clients to 192.168.40.21:5000". (Which I'd expected was what OP is trying to do?) – Rup Sep 19 '18 at 09:31