1

I am writing a client-server solution. The goal is to connect to a camera remotely and receive its camera feed in the client. I am using TCP protocol for this. Yes, this is the first time I'm doing this.

The client is written in Java. This means it uses BufferedReader.readLine(). Yes, the problem is that it results in null and I am aware that this means that the server shuts down the connection. I have also had this confirmed by a senior coder.

The server is written in C. My problem is that I cannot find an obvious reason to why its code results in shutting down the connection seemingly immediately. I have found multiple code-examples that supports the method I'm already using.

I wonder:

  1. Could BufferedReader resulting in null immediately indicate something else in this case?
  2. What have I missed in my server-code, why does it shut down the connection?

Here is my original server code (see update below it):


#define _GNU_SOURCE -D

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <syslog.h>
#include <capture.h>
#include <sys/sendfile.h>
#include <pthread.h>
//define where the network camera is located
#define PORT_NUMBER 80
#define SERVER_ADDRESS "localhost"

void *msg_handler(void *);

//step 1
/* The server is supposed to send a list of available resolutions and frames per second, so the client can choose*/

//we create a handler to each client

void *client_handler(int server_socket)

{
    int serverSocket = *(int *)server_socket; // this is a pointer to the server socket
    char *message, cli_message[40000];
    message = capture_get_resolutions_list(0);     //list of available image resolutions;
    write(serverSocket, message, strlen(message)); //send to client the list of resolutions
    write(serverSocket, "\n", strlen("\n"));       //breaking line so the client can see the list
    memset(message, 0, strlen(message));           //resetting the message variable

    //variables related to the image
    media_stream *imageStream;

    recv(serverSocket, cli_message, 40000, 0); // receive information from client regarding resolution
    media_frame *imageFrame;
    void *data;
    size_t imageSize;
    int arrayRow = 0; //related to the data array - we need it if we want to loop later through the array

    //opening the stream in order to capture image
    imageStream = capture_open_stream(IMAGE_JPEG, cli_message);

    //send image to client with chosen resolution
    while (1) //condition always true, i.e. infinite loop
    {
        imageFrame = capture_get_frame(imageStream);
        data = capture_frame_data(imageFrame);

        imageSize = capture_frame_size(imageFrame);
        sprintf(message, "%zu\n", imageSize);          //converting the size to a char , and send to client
        write(serverSocket, message, strlen(message)); //sending the size to the client

        unsigned char arrayRow_data[imageSize];

        for (arrayRow = 0; arrayRow < imageSize; arrayRow++)
        {

            arrayRow_data[arrayRow] = ((unsigned char *)data)[arrayRow];
        }

        //try to send data to client ---> see the possible errors
        int sendDataError = write(serverSocket, arrayRow_data, sizeof(arrayRow_data));

        if (sendDataError < 0)
        {
            syslog(LOG_INFO, "oops, connection with client lost");
            break;
        }

        //restoring variables to ZERO
        memset(data, 0, sizeof(data));
        memset(arrayRow_data, 0, sizeof(arrayRow_data));

        capture_frame_free(imageFrame);
    }
    syslog(LOG_INFO, "ERROR:CAN NOT SEND IMAGE");
    capture_close_stream(imageStream);
    close(serverSocket);
    return 0;
}

//main function

//creating a socket
int main(void)
{
    //char server_message[256]= "Congrats, you have reached the server";
    int server_socket;
    int client_socket;
    int *new_socket;
    int conn;
    struct sockaddr_in server, client;

    server_socket = socket(AF_INET, SOCK_STREAM, 0);

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(1025);

    //bind the socket to the specified IP and port
    bind(server_socket, (struct sockaddr *)&server, sizeof(server));
    conn = sizeof(struct sockaddr_in);
    listen(server_socket, 5);

    //accept the connection from the client and send the message

    while ((client_socket = accept(server_socket, (struct sockaddr *)&client, (socklen_t *)&conn)))
    {

        pthread_t client_thread;
        new_socket = malloc(sizeof *new_socket);
        new_socket = client_socket;

        //establishing a message thread and possible errors
        if (pthread_create(&client_thread, NULL, client_handler, (void *)new_socket) < 0)
        {
            syslog(LOG_INFO, "ERROR. MESSAGE THREAD NOT ESTABLISHED");
            return 1;
        }
    }

    if (client_socket < 0)
    {
        syslog(LOG_INFO, "Sorry, client socket declined");
        free(new_socket);
        return 1;
    }

    return 0;
}

Here's an UPDATED server code. It has a lot of comments because we want to keep track of what we've done for now. Still the same problem:

#define _GNU_SOURCE -D

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <syslog.h>
#include <capture.h>
#include <sys/sendfile.h>
#include <pthread.h>
//define where the network camera is located
#define PORT_NUMBER 443
#define SERVER_ADDRESS "localhost"

//store recieved messages from client
//char cli_message;


//This is a buffer to store a welcome message to client in.
char buffer[1024];

char *message; //declared like this instead.


void * client_handler(void *server_socket) //This probably had the wrong input variable.

{
    //int serverSocket = (int) (intptr_t)server_socket; 
    int new_socket = (int)(intptr_t) (server_socket);   // this is a pointer to the server socket.
    //char *message, cli_message[2000];


    //HERE: get the media_stream BEFORE trying to fetch message
    media_stream *imageStream;
    media_frame *imageFrame;

    //ADDED: storing the image data.
    char *imageData;
    //ADDED: storing the size of the image data array.
    size_t imgSize;

    //MOVED and changed from server_socket
    recv(new_socket, message, 2000, 0); // receive information from client regarding resolution



    //MOVED. NOW we get the available resolutions.
    message = capture_get_resolutions_list(0);     //list of available image resolutions;

    //ADDED: Fetching the length of message in an int to make CERTAIN it's right and using a different method later in write to send it.
    int first_message_size = strlen(message);

    
    //write(serverSocket, message, strlen(message)); 
    write(new_socket, message, first_message_size); //send to client the list of resolutions
    write(new_socket, "\n", strlen("\n"));       //breaking line so the client can see the list


    memset(message, 0, strlen(message));           //resetting the message variable

    //Store the recieved resolution and FPS from client picked by user.
    recv(new_socket, message, 2000, 0);

    //opening the stream in order to capture image
    imageStream = capture_open_stream(IMAGE_JPEG, message);

    //send image to client with chosen resolution
    while (1) //condition always true, i.e. infinite loop
    {
        imageFrame = capture_get_frame(imageStream);
        imageData = capture_frame_data(imageFrame);

        imgSize = capture_frame_size(imageFrame);

        sprintf(message, "%zu\n", imgSize);          //converting the size to a char , and send to client
    
        write(new_socket, message, strlen(message)); //sending the size to the client
    
    //EDITED: sends image data to client, we also check if this write fails. We send imgSize, not sizeof(data).
    int sending = write(new_socket, imageData, imgSize);

    //Here we check that it's sending. If it isn't, the problem might be
    //that the CLIENT disconnected.
    if (sending < 0)
    {
        syslog(LOG_INFO, "ERROR! Failed to send. Client disconnected?");
    break;
    }

    //EDITED: Another reset of variables cause we DO NOT want to store this.
    memset(imageData, 0, sizeof(imageData));
    //memset(arrayRow_data, 0, sizeof(arrayRow_data));
    capture_frame_free(imageFrame);

        //unsigned char arrayRow_data[imgSize];
int arrayRow;


        for (arrayRow = 0; arrayRow < imgSize; arrayRow++)
        {

            imageData[imgSize] = imageData[arrayRow];

        }
    }//End of while

    
    syslog(LOG_INFO, "Exiting...");
    capture_close_stream(imageStream);
    close(new_socket);
    //ADDED
    pthread_exit(NULL);
    return 0;
}

    int start(void)
    {
        //(might solve our log issues)
        openlog ("server", LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
        //message onto server log
        syslog(LOG_INFO, "Server running...");


        //char server_message[256]= "Congrats, you have reached the server";
        int server_socket;
        int new_socket;

        //int *new_socket;
        //int conn; //wat is dis?
        //struct sockaddr_in server, client;
        struct sockaddr_in server;
        struct sockaddr_storage storage; //This is new. Testing.
        socklen_t addressLength;

        server_socket = socket(PF_INET, SOCK_STREAM, 0); //Changed to PF
        server.sin_family = AF_INET;
        server.sin_port = htons(443);
        server.sin_addr.s_addr = htonl(INADDR_ANY);//we added htons & htonl

        //Deal with eventual padding field
        memset(server.sin_zero, '\0', sizeof server.sin_zero);

        //bind the socket to the specified IP and port
        bind(server_socket, (struct sockaddr *) &server, sizeof(server));
        //conn = sizeof(struct sockaddr_in); ----> wat is?

        //Gave the listener a few more helpers. Longer time and max 40 con
        //requests.
        if(listen(server_socket, 50)==0)
        {
        syslog(LOG_INFO, "Listening\n");
        }
        else
        {
        syslog(LOG_INFO, "Not listening, lalalala.\n");
        }

        pthread_t client_thread[60]; //Move to outside of loop and given size.
        int i = 0; //used in the if-statement in the below while-loop.
        int end = 1; //used to end the while-loop.
        //accept the connection from the client and send the message
        while(end)
        {
        addressLength = sizeof storage;
           //new_socket = malloc(sizeof *new_socket);
        //new_socket = accept(server_socket, (struct sockaddr *) &client, (socklen_t *)&conn);
        *new_socket = accept(server_socket, (struct sockaddr *) &storage, &addressLength); //New addition

        //establishing a message thread and possible errors
        //if(pthread_create(&client_thread, NULL, client_handler, (void *)((intptr_t) new_socket))< 0)

        //Trying this if-statement instead..
        if(pthread_create(&client_thread[i], NULL,client_handler,(void *)((intptr_t) new_socket)) !=  0)

        {
            syslog(LOG_INFO, "ERROR. MESSAGE THREAD NOT ESTABLISHED!");
        }
        i++;

        if( i > 50)
        {
            end = 0;
        }
    }
    return 0;
}

//end

Zarkaylia
  • 61
  • 4
  • 13
  • 1
    What is the capture lib (`capture.h`) ? – Ôrel Jan 06 '21 at 12:45
  • 2
    `recv(serverSocket, cli_message, 40000, 0)`? And just how do you know how long that message actually is? You're not treating it as a string, are you? `recv()` does ***not*** terminate strings properly. – Andrew Henle Jan 06 '21 at 14:45
  • @Ôrel I am using an Axis camera, it's from their library. – Zarkaylia Jan 06 '21 at 15:09
  • Thsi `new_socket =accept(...` ought to be `*new_socket =accept(....`. Also the code still does not check calls to `write()` and `recv()` for errors. – alk Jan 08 '21 at 10:10
  • @alk We have since gone through the code entirely and based it off of new finds and feedback. Since we're still having problems we will look into the client-side next. But the new server-code is in this post as an updated version and feedback is still appreciated. I'm looking into checking recv(), just trying to find a good resource on how to best do so. – Zarkaylia Jan 10 '21 at 13:15

3 Answers3

2

Given

pthread_create(&client_thread, NULL, client_handler, (void *)new_socket)

you are passing the new_socket as a void * by value and not address.

so this is wrong:

int serverSocket = *(int *)server_socket;

Since you're passing by value, you should write

pthread_create(&client_thread, NULL, client_handler, (void *)((intptr_t) new_socket) )

to send the value and retrieve the value with

int serverSocket = (int)(intptr_t)server_socket;

And this is always wrong:

void *client_handler(int server_socket)

pthread_create() passes the argument as a void *. Trying to pass that to a function that takes an int argument simply will not work on platforms where void * is a different size than int. And it's likely undefined behavior in all cases, but I don't have the time to look that up. So it needs to be

void *client_handler(void *server_socket)
Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
1

This

  new_socket = client_socket;

should issue a warning during compilation. Do not ignore compiler warnings!

It ought to be:

  *new_socket = client_socket;

Aside of this many important calls to system functions lack error checking (recv(), write()).

Error checking is debugging for free (nd mandatory for code going into production).

alk
  • 69,737
  • 10
  • 105
  • 255
  • Thank you for telling me, my partner in this has now admitted she ignored a few compiling errors and we're looking into it. – Zarkaylia Jan 06 '21 at 15:28
  • The compilation errors are fixed! The question remains relevant – Zarkaylia Jan 06 '21 at 18:05
  • @Zarkaylia: Did you add error checking (and logging) to all relevant system-calls as recommended as well? – alk Jan 06 '21 at 18:07
  • @Zarkaylia: If you significantly changed your code, *append* it as update (with deleting the original code) to your question. – alk Jan 06 '21 at 18:08
  • @Zarkaylia: Deleting the original code might render answers and comments given to it ununderstandable. – alk Jan 06 '21 at 18:09
  • I'm trying to make my partner do this. It's a long story, but she handles the c-compilations and package-building cause she managed to make her setup work to do it, I didn't. I gave up after attempting about 10 different virtual machines for it. Should she not have the time, I will write it and send to her. EDIT: I will fix a code update, good point. – Zarkaylia Jan 06 '21 at 18:11
  • I have updated the post with the changes we've made based on feedback. We are still experiencing the same problems and looking into solving it. – Zarkaylia Jan 06 '21 at 20:52
0

This turned out to be a case of the cameras used not being properly configured for remote access (something not under my control)!

I'm still thankfull for all the tips and pointers given. But for the future, if anyone encounters the same issues and it feels impossible to solve, here's a few tips:

  1. Check access to the ports of your camera! This can be done using for example Postman (keep above port 1024 or try 80 or 8080).
  2. Before getting your hands dirtied with a more complicated project, run a simple Hello World-solution to make sure the setup is even functional from start.
Zarkaylia
  • 61
  • 4
  • 13