Home > database >  C Socket Programming Server to Client using User Input
C Socket Programming Server to Client using User Input

Time:02-03

I am trying to modify some client and server c code so that it takes a user input on the client, send it to the server and the server sends back the corresponding value. I am not very familiar with c programming; therefore, my error is most likely syntax related. Server Code:

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

int main(int argc, char *argv[])
{
    int listenfd, connfd;
    struct sockaddr_in serv_addr; 

    char sendBuff[1025];
    char from_client[1025];
    time_t ticks; 

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&serv_addr, '0', sizeof(serv_addr));
    memset(sendBuff, '0', sizeof(sendBuff)); 

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(5000); 

    bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 

    listen(listenfd, 10); 

    while(1)
    {
        connfd = accept(listenfd, (struct sockaddr*)NULL, NULL); 

    recv(connfd, from_client, sizeof(from_client), 0);
    printf("%s", "Letter Recieved\n");
    if(from_client == 't')
    {
        ticks = time(NULL);
            snprintf(sendBuff, sizeof(sendBuff), "%.24s\r\n", ctime(&ticks));
            write(connfd, sendBuff, strlen(sendBuff));
    }
    if(from_client == 'n')
    {
        sendBuff == "Marcus Baker";
        send(connfd, sendBuff, sizeof(sendBuff), 0);
    }
    if(from_client == 'i')
    {
        sendBuff == "201604543";
        send(connfd, sendBuff, sizeof(sendBuff), 0);
    }
    if(from_client == 'q')
    {
        return 0;
    }

        close(connfd);
        sleep(1);
     }
}

Client Code:

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

int main(int argc, char *argv[])
{
    int sockfd, n = 0;
    char letter[1024];
    printf("Enter a Character (t, n, i, q): ");
    scanf("%c",&letter);
    char recvBuff[1024];
    struct sockaddr_in serv_addr; 

    if(argc != 2)
    {
        printf("\n Usage: %s <ip of server> \n",argv[0]);
        return 1;
    } 

    memset(recvBuff, '0',sizeof(recvBuff));
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("\n Error : Could not create socket \n");
        return 1;
    } 

    memset(&serv_addr, '0', sizeof(serv_addr)); 

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(5000); 

    if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
    {
        printf("\n inet_pton error occured\n");
        return 1;
    } 

    if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
       printf("\n Error : Connect Failed \n");
       return 1;
    } 

    while ( (n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0)
    {
        recvBuff[n] = 0;
        if(fputs(recvBuff, stdout) == EOF)
        {
            printf("\n Error : Fputs error\n");
        }
    } 

    if(n < 0)
    {
        printf("\n Read error \n");
    } 

    int toServer = send(sockfd, letter, sizeof(letter), 0);
    printf("%s", "Letter Sent\n");
    recv(sockfd, recvBuff, sizeof(recvBuff), 0);
    printf("%c", recvBuff);
    
    return 0;
}

Any information would be greatly appreciated.

I have tried several different iteration following different stack overflow guides but none seem to work. When I try to compile both pieces of code, I get warnings about the user input potion of the code, something about scanf expects a char*.

CodePudding user response:

I suggest you use a known good client, netcat or socat, then fix the server side first:

  1. server: You need to deference from_client to compare against the first letter:
if(*from_client == 't') {

and the server returns the right ting:

$ socat - tcp-connect:localhost:5000
t
Wed Feb  1 00:04:36 2023
  1. server: sendBuff == "Marcus Baker" is a comparison and always false, and you just send send as many bytes as needed instead of the whole buffer:
            strcpy(sendBuff, "Marcus Baker");
            send(connfd, sendBuff, strlen(sendBuff), 0);

and testing it:

n
Marcus Baker
  1. server: sendBuff == "201604543" is a comparison. You have to decide if you want to send it as a string or binary, and if binary if you want a particular encoding (little or big indian). Using a binary and assume the same encoding on both client and server:
            memcpy(sendBuff, &(int) { 201604543 }, sizeof(int));
            send(connfd, sendBuff, sizeof(int), 0);

and the result is (note the use of od -t d4 to decode the integer value):

socat - tcp-connect:localhost:5000 | od -t d4
i
0000000   201604543
0000004

Now we know the server works (better) so let's start working on the client.

  1. client: After the client connects it does a read() but server is doing a recv(). Client is expected to write to server so let's just remove this block:
    while ( (n = read(sockfd, recvBuff, sizeof(recvBuff)-1)) > 0) {
        recvBuff[n] = 0;
        if(fputs(recvBuff, stdout) == EOF)
        {
            printf("\n Error : Fputs error\n");
        }
    }

    if(n < 0) {
        printf("\n Read error \n");
    }
  1. server: Now when we send 't' the server responds with time but with a bunch of 0. Going back to server we see it sends the whole buffer so let's fix that:
            int n = snprintf(sendBuff, sizeof(sendBuff), "%.24s\r\n", ctime(&ticks));
            write(connfd, sendBuff, n);
  1. client: We still get a bunch of 0 after the time so let's record how much data we get from server and only print that out:
    printf("%s", "Letter Sent\n");
    ssize_t recvLen = recv(sockfd, recvBuff, sizeof(recvBuff), 0);
    printf("%.*s", (int) recvLen, recvBuff);

and now the client reports:

Enter a Character (t, n, i, q): t
Letter Sent
Wed Feb  1 00:26:13 2023
  1. client: The data we get for 'i' looks weird:
Enter a Character (t, n, i, q): i
Letter Sent
=
 

This is because it's binary data so we need to decode it:

    if(*letter = 'i') {
        printf("%d", *(int *) recvBuff);
    } else {
        // ...

and the output is now as expected:

Enter a Character (t, n, i, q): i
Letter Sent
201604543
  1. client: 'q' prints doesn't look right:
Letter Sent
808464432

That's because we don't actually expect to get any more data:

    printf("%s", "Letter Sent\n");
    if(*letter == 'q')
        return 0;
  1. server: When client sends a 'q' we return but this leaves the socket in a timeout state. It's better to close it first:
        if(*from_client == 'q') {
            close(connfd);
            return 0;
        }

Here is the programs as changed:

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

int main(int argc, char *argv[]) {
    int sockfd, n = 0;
    char letter[1024];
    printf("Enter a Character (t, n, i, q): ");
    scanf("%c",&letter);
    char recvBuff[1024];
    struct sockaddr_in serv_addr;

    if(argc != 2) {
        printf("\n Usage: %s <ip of server> \n",argv[0]);
        return 1;
    }

    memset(recvBuff, '0',sizeof(recvBuff));
    if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
        printf("\n Error : Could not create socket \n");
        return 1;
    }

    memset(&serv_addr, '0', sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(5000);

    if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0) {
        printf("\n inet_pton error occured\n");
        return 1;
    }

    if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\n Error : Connect Failed \n");
        return 1;
    }

    int toServer = send(sockfd, letter, sizeof(letter), 0);
    printf("%s", "Letter Sent\n");
    if(*letter == 'q')
        return 0;
    ssize_t recvLen = recv(sockfd, recvBuff, sizeof(recvBuff), 0);
    if(*letter = 'i') {
        printf("%d", *(int *) recvBuff);
    } else {
        printf("%.*s", (int) recvLen, recvBuff);
    }
}

server.c:

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

int main(int argc, char *argv[]) {
    int listenfd, connfd;
    struct sockaddr_in serv_addr;

    char sendBuff[1025];
    char from_client[1025];
    time_t ticks;

    listenfd = socket(AF_INET, SOCK_STREAM, 0);
    memset(&serv_addr, '0', sizeof(serv_addr));
    memset(sendBuff, '0', sizeof(sendBuff));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(5000);

    bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    listen(listenfd, 10);

    for(;;) {
        connfd = accept(listenfd, NULL, NULL);

        recv(connfd, from_client, sizeof(from_client), 0);
        printf("%s", "Letter Recieved\n");
        if(*from_client == 't') {
            ticks = time(NULL);
            int n = snprintf(sendBuff, sizeof(sendBuff), "%.24s\r\n", ctime(&ticks));
            write(connfd, sendBuff, n);
        }
        if(*from_client == 'n') {
            strcpy(sendBuff, "Marcus Baker");
            send(connfd, sendBuff, strlen(sendBuff), 0);
        }
        if(*from_client == 'i') {
            memcpy(sendBuff, &(int) { 201604543 }, sizeof(int));
            send(connfd, sendBuff, sizeof(int), 0);
        }
        if(*from_client == 'q') {
            return 0;
        }
        close(connfd);
        sleep(1);
    }
}
  1. (not fixed) I suggest you extract the shared port number and store in a header file that you can include in both client and server. For example:
#define SERVER_PORT 5000

Then use that constant instead of hard-coding the magic value 5000.

  1. (not fixed) Audit all calls to ensure you check the return value and handle errors. Failing to do so will waste time.
  • Related