1

I have been trying to learn sockets programming, and came across this website: http://beej.us/guide/bgnet/output/html/multipage/index.html which is really good. I was able to understand, but after writing a test program myself I ended up with a problem which I cannot figure after 3 hours. Have very limited time, so thought asking experts help here.

This is my server program:

/**
 * Program showing how sockets can be used
 */

#include <stdio.h>          // For printf
#include <strings.h>        // For bzero, memset
#include <stdlib.h>         // For exit, atoi
#include <unistd.h>         // For close

#include <sys/socket.h>     // For socket
#include <sys/types.h>      // For types

#include <arpa/inet.h>      // For inet_addr
#include <netdb.h>          // Import module network database

#include <errno.h>          // To access the global errno that holds last system call error
#include <assert.h>         // For asserting

#define ADDRESSINFO_GET_SUCCESS 0
const int   kSuccess            = 0;
const char *kMyPort             = "3490"; // Can be either port num or name of the service
const int   kMaxListenConn      = 10;     // How many connections queue will hold

// Utility function to get socket address, IPv4 or IPv6:
void* getSocketAddress(const struct sockaddr *sa)
{
    // Cast socketaddr to sockaddr_in to get address and port values from data

    if (sa->sa_family == PF_INET) {
        return &(((struct sockaddr_in *)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6 *)sa)->sin6_addr);
}

// Utility function to get socket address string
void getSocketAddressString(const struct sockaddr *sd, char *ipAddr)
{
    inet_ntop(sd->sa_family, getSocketAddress(sd), ipAddr, INET6_ADDRSTRLEN);
}

int createAndBindSocket(const struct addrinfo *addrList)
{
    int status = -1;            // Invalid status
    int socketFileDesc = -1;    // Invalid descriptor

    const struct addrinfo *addrIt;

    /*
     * STEP 2.1: Loop through all the addrinfo nodes and bind to the one we can
     */

    for (addrIt = addrList; addrIt != NULL; addrIt = addrIt->ai_next)
    {
        char ipAddr[INET6_ADDRSTRLEN];
        inet_ntop(addrIt->ai_family, getSocketAddress(addrIt->ai_addr), ipAddr, sizeof ipAddr);
        printf("IP: %s\n", ipAddr);

        /*
         * STEP 2.2: Crete the socket file descriptor for our IP address
         */

        socketFileDesc = socket(addrIt->ai_family, addrIt->ai_socktype, addrIt->ai_protocol);

        if (socketFileDesc == -1) {
            fprintf(stderr, "Failed to create socket with error: %d\n", errno);
            perror("socket");   // Get error desc
            continue;           // Try next address
        }

        /*
         * STEP 2.3: Set socket behaviour by making ip address to be re-used if used already
         */

        int yes=1;

        if (setsockopt(socketFileDesc, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
        {
            perror("setsockopt");
            exit(1);
        }

        printf("Port %d\n",((struct sockaddr_in *)addrIt->ai_addr)->sin_port);

        /*
         * STEP 2.4: Bind our socket with ip address and port number to listen
         */

        status = bind(socketFileDesc, addrIt->ai_addr, addrIt->ai_addrlen);
        if (status != kSuccess) {
            fprintf(stderr, "Failed to bind socket with error:%d\n", errno);
            perror("bind");  // Get error desc

            // Clear socket
            close(socketFileDesc);
            socketFileDesc = -1;

            continue;       // Try next address
        }
    }

    return socketFileDesc;
}


int main()
{
    int status = -1; // Status is invalid

    struct addrinfo  hints;         // Holds our hints to get address info
    struct addrinfo *addrList;      // Contains our address info

    /*
     * STEP 1: Setup service details
     */

    // Make sure struct is empty
    bzero(&hints, sizeof hints); // memset(&hints, 0, sizeof(hints));

    hints.ai_family     = AF_UNSPEC;    // Don't care IPv4 or v6
    hints.ai_socktype   = SOCK_STREAM;  // Use TCP stream sockets
    hints.ai_flags      = AI_PASSIVE;   // Use my local IPs or you igore this and provide IP manually in first arg


    status = getaddrinfo(NULL, kMyPort, &hints, &addrList);
    if (status != kSuccess) {
        fprintf(stderr, "Failed to get address info with error: %s\n", gai_strerror(status));
        exit(1);
    }

    /*
     * STEP 2: Create a socket and bind it
     */

    int socketFileDesc;
    socketFileDesc = createAndBindSocket(addrList);

    freeaddrinfo(addrList);   // Done with list

    if (socketFileDesc == -1) {
        exit(1);
    }

    /*
     * STEP 3: Listen to the port for incoming connections
     */

    status = listen(socketFileDesc, kMaxListenConn);     // Second arg is number of incoming connections in queue
    if (status != kSuccess) {
        fprintf(stderr, "Failed to listen to the port\n");
        perror("listen");
        goto exit;
    }

    printf("Server is listening at the port %s\n", kMyPort);

    struct sockaddr_storage inConnAddr;     // Big enough to hold both IPv4 and v6
    socklen_t inConnAddrLen = sizeof inConnAddr;

    const size_t kMaxBufferSize = 50;
    char buff[kMaxBufferSize] = {0};

    long bytesReceived = -1;
    long bytesSent = 0;
    int clientSockfd;

    while (1) {

        /*
         * STEP 4: Accept incoming connections
         */

        inConnAddrLen = sizeof inConnAddr;

        clientSockfd = accept(socketFileDesc, (struct sockaddr *)&inConnAddr, &inConnAddrLen);

        // Got new connection ?
        if (clientSockfd == -1) {
            perror("accept");   // Print error description
            continue;           // Continue to look for new connections
        }

        //
        // Got connection, create child process to handle request
        //

        if (!fork()) {
            close(socketFileDesc); // No need with child

            char ipAddr[INET6_ADDRSTRLEN];
            getSocketAddressString((struct sockaddr *)&inConnAddr, ipAddr);
            printf("Child process created for hanlding request from %s\n", ipAddr);

            /*
             * STEP 5: Receive and Send data to requests
             */

            bytesReceived = recv(clientSockfd, &buff, kMaxBufferSize - 1, 0);
            if (bytesReceived > 0)
            {
                printf("Data from client %s\n", buff);
                while (bytesSent < bytesReceived) {
                    bytesSent = send(clientSockfd, buff, bytesReceived, 0);
                    printf("Bytes sent %ld\n", bytesSent);
                }
            }

            if (bytesReceived < 0) {
                perror("recv");
            }
            else if (bytesReceived == 0) {
                printf("Connection closed by server\n");
            }

            close(clientSockfd); // Close socket
            exit(0);
        }
    }

exit:
    /*
     * STEP 5: Close the socket
     */
    close(socketFileDesc);


    return 0;
}

This is my client program:

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <strings.h>
#include <arpa/inet.h>

const char  kSuccess = 0;

// Utility function to get socket address
void* getSocketAddress(const struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &( ((struct sockaddr_in *)sa)->sin_addr );
    }

    return &( ((struct sockaddr_in6 *)sa)->sin6_addr );
}

// Utility function to get socket address string
void getSocketAddressString(const struct sockaddr *sd, char *ipAddr)
{
    inet_ntop(sd->sa_family, getSocketAddress(sd), ipAddr, INET6_ADDRSTRLEN);
}

int createAndConnectSocket(const struct addrinfo *addrList)
{
    int socketFileDesc = -1;    // Invalid descriptor

    const struct addrinfo *addrIt;

    /*
     * STEP 2.1: Loop through all the addrinfo nodes and bind to the one we can
     */

    for (addrIt = addrList; addrIt != NULL; addrIt = addrIt->ai_next)
    {
        /*
         * STEP 2.2: Crete the socket file descriptor for our IP address
         */

        socketFileDesc = socket(addrIt->ai_family, addrIt->ai_socktype, addrIt->ai_protocol);

        if (socketFileDesc == -1) {
            fprintf(stderr, "Failed to create socket with error: %d\n", errno);
            perror("socket");   // Get error desc
            continue;           // Try next address
        }

        /*
         * STEP 2.3: Set socket behaviour by making ip address to be re-used if used already
         */

        int yes=1;

        if (setsockopt(socketFileDesc, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
        {
            perror("setsockopt");
            exit(1);
        }

        char ipAdd[INET6_ADDRSTRLEN];
        getSocketAddressString(addrIt->ai_addr, ipAdd);
        ((struct sockaddr_in *)addrIt->ai_addr)->sin_port = atoi("3490");

        printf("IP is %s::%d\n", ipAdd,((struct sockaddr_in *)addrIt->ai_addr)->sin_port);

        // Connect to the socket
        int status;
        status = connect(socketFileDesc, addrIt->ai_addr, addrIt->ai_addrlen);
        if (status != kSuccess) {
            perror("connect");
            close(socketFileDesc);
            socketFileDesc = -1;
            continue;
        }
    }

    return socketFileDesc;
}

int main(int argc, char* argv[])
{
    // Check we have data from arguments
    if (argc < 3 || argc > 3) {
        perror("Invalid command");
        printf("Usage: %s hostname portnumber\n", argv[0]);
        printf("       %s 192.168.1.2 3490\n", argv[0]);
    }

    // Setup server address info
    struct addrinfo *serverInfo;
    struct addrinfo hints;

    int status = -1;

    memset(&hints, 0, sizeof hints);  // Make sure it is empty

    hints.ai_family     = AF_INET;      // IPv4
    hints.ai_socktype   = SOCK_STREAM;  // Use socket stream

    ((struct sockaddr_in *)&hints.ai_addr)->sin_port = atoi(argv[2]);

    status = getaddrinfo(argv[1], "3490", &hints, &serverInfo);
    if (status != kSuccess) {
        fprintf(stderr, "Failed to get address info %s\n", gai_strerror(status));
        exit(1);
    }

    printf("Connecting to %s::%s...\n", argv[1], argv[2]);

    // Create and bind socket
    int sockfd = createAndConnectSocket(serverInfo); 

    freeaddrinfo(serverInfo);   // We are done with serverinfo

    if (sockfd == -1) {
        exit(1);
    }

    // Send and receive data from server

    long bytesReceived = -1;
    long bytesSent  = 0;

    const size_t kMaxBufferSize = 50;
    char buff[kMaxBufferSize];

    const char *msg = "Hi! I am client!";
    size_t msgLen = strlen(msg);

    printf("Connected to server\n");

    // Loop to send and receive data
    while (1) {

        while (bytesSent < msgLen) {
            bytesSent = send(sockfd, msg, msgLen, 0);
            printf("Bytes sent %ld\n", bytesSent);
        }

        bytesReceived = recv(sockfd, &buff, kMaxBufferSize - 1, 0);
        if (bytesReceived > 0)
        {
            printf("Data received from server: %s\n", buff);
        }
        else if (bytesReceived < 0) {
            perror("Read error");
            break;
        }
        else if (bytesReceived == 0) {
            printf("Connection closed by server\n");
            break;
        }
    }

    // Close socket
    close(sockfd);

    return 0;
}

My problem is: Even though I have set the port and IP in getaddrinfo() call, but when it is binding or connecting the port number found in sockaddr struct is wrong as it is different value. I do know what is happing here, and I get connection refused message when I do that because of that. Can anyone please take a look at my program and tell me why I am getting connection refused ? I would really appreciate if someone can suggest any improvements in my code.

Thanks

Dinesh G
  • 129
  • 7

1 Answers1

3

You're not connecting to the port you think you're connecting to.

((struct sockaddr_in *)addrIt->ai_addr)->sin_port = atoi("3490");

The value of sin_port must be in network byte order, i.e. big endian. You're instead assigning the value 3490 (via atoi) directly, so the value is in host byte order. Your platform is most likely using little endian byte ordering.

As a result instead of connecting to port 3490 (0DA2 hex) you're connecting to port 41485 (A2 0D hex).

You need to use the htons function, which converts a 16 bit value (since sin_port is a 16 bit field) from host byte order to network byte order. Also, there's no need to use atoi here. Just use a numeric constant instead.

((struct sockaddr_in *)addrIt->ai_addr)->sin_port = htons(3490);
dbush
  • 205,898
  • 23
  • 218
  • 273
  • Thanks for your answer. I forgot about this, and I have corrected it but I still have problem. Now, I am getting operation timed out with connect call. Do you have any idea ? Also would you mind looking at my code and suggest any code improvements ? Thanks PS: If I manually input sockaddr, family and other values instead of using getaddrinfo() to get structure then it connects and works fine, but does not work if I use getaddrinfo() to get the structure and use it. – Dinesh G Aug 03 '17 at 16:51
  • @DineshG It works fine for me with server and client on the same machine. Check your firewall settings. – dbush Aug 03 '17 at 16:57