简体   繁体   中英

Why does my server for my client-server solution shut down connection immediately?

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

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)

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).

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM