C Server prints garbage characters only in a specific situation

Viewed 62

I've read many threads about problems similar to mine, i've studied from 'Advanced programming in Unix environment', but can't solve this problem. I'm sure it is a simple error in my thinking, and I need a look from someone more advanced than me.

I'm creating a chatroom in C, for a university project. The idea is that the server sends every client a list of rooms, than a client chooses the room he wants to join, and he can chat when someone else joins. Every client, when chatting, can type 'exit' to quit the program, or 'menu' to go back to the room selection. When a client does this, the other client receives a message saying 'other client disconnected, going back to menu in 3, 2, 1', then goes back.

Here comes the problem. Everything works, but the client who automatically disconnected (because the other client did) prints a lot of garbage characters, thus everything breaks (for him).

I tried everything. I think it's something regarding the buffer's sizes and the amount of bytes I send, but it works in every case except this. I changed my code so many times that i'm losing control, that's why i'm asking here.

Here's the code.

#ifndef PROTO
#define PROTO

#define NAME_LENGTH 31
#define LENGTH_MSG 200
#define LENGTH_SEND 300
#define ROOM_CHOICE 5
#define MAX_CLIENTS 50
#define MAX_ROOM_WAITLIST 5

#endif // PROTO

Server

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "proto.h"
#include "server.h"


// Variabili Globali
int server_sockfd = 0, client_sockfd = 0;
int leave_flag = 0; // flag to make a client disconnect
Client* clientList[MAX_CLIENTS] = {}; // clients connected to the server
char chosenRoom[ROOM_CHOICE] = {}; // contains the room chosen by the client
char recv_buffer[LENGTH_MSG] = {};
char send_buffer[LENGTH_SEND] = {};

Room* room1;
Room* room2;
Room* room3;
Room* room4;
Room* room5; // rooms

int clientsInRoom[ROOM_CHOICE] = {0}; // how many people are in rooms

pthread_mutex_t globalMutex = PTHREAD_MUTEX_INITIALIZER; // mutex to manage the connection to clients
// Metodi

void catch_ctrl_c_and_exit(int sig) {

        for (int i = 0; i<MAX_CLIENTS; i++){
          if (clientList[i]){
            printf("\nClose socketfd: %d\n", clientList[i]->socket);
            close(clientList[i]->socket);
            free(clientList[i]);
          }
        }
    printf("Bye\n");
    exit(EXIT_SUCCESS);
}


void send_to_other_client(Client* client, char tmp_buffer[]) {
      printf("Sockfd: %d sends to Sockfd %d: \"%s\" \n", client->socket, client->pairSock, tmp_buffer);
      send(client->pairSock, tmp_buffer, LENGTH_SEND, 0);
      memset(send_buffer, '\0', LENGTH_SEND);
}


void pairing_with_client(Client *client){
    for (int i = 0; i < MAX_ROOM_WAITLIST; i++){
      if (client->room->waitList[i]){
        if (client->socket != client->room->waitList[i]->socket && client->room->waitList[i]->paired == 0 && client->room->waitList[i]->socket != client->pairSock && client->room->waitList[i]->pairSock != client->socket){
          client->pairSock = client->room->waitList[i]->socket;
          client->room->waitList[i]->pairSock = client->socket;
          client->paired = 1;
          client->room->waitList[i]->paired = 1;
          strncpy(client->pairName, client->room->waitList[i]->name, NAME_LENGTH);
          strncpy(client->room->waitList[i]->pairName, client->name, NAME_LENGTH);
          pthread_cond_broadcast(&client->room->cond);
          printf("The socket: %d woke up the waiting socket: %d.\n", client->socket, client->pairSock);
          memset(send_buffer, '\0', LENGTH_SEND);
          sprintf(send_buffer, "User found!\nBegin conversation with %s:\n", client->pairName);
          send(client->socket, send_buffer, LENGTH_SEND, 0);
          memset(send_buffer, '\0', LENGTH_SEND);

          return;
        }
    }
  }
  // Didn't find anyone, putting the client in wait

  add_to_waiting_list(client);

  memset(send_buffer, '\0', LENGTH_SEND);
  sprintf(send_buffer, "No user found. Waiting...\n");
  send(client->socket, send_buffer, LENGTH_SEND, 0);
  memset(send_buffer, '\0', LENGTH_SEND);

  pthread_cond_wait(&client->room->cond, &client->room->mutex);

  memset(send_buffer, '\0', LENGTH_SEND);
  sprintf(send_buffer, "User foubd!\nBegin conversation with %s:\n", client->pairName);
  send(client->socket, send_buffer, LENGTH_SEND, 0);
  memset(send_buffer, '\0', LENGTH_SEND);
  remove_from_waiting_list(client);

  return;

}



void client_handler(void *p_client) {

    char nickname[NAME_LENGTH] = {};
    Client *client = (Client *)p_client;

    // Naming
    memset(nickname, '\0', NAME_LENGTH);
    if (recv(client->socket, nickname, NAME_LENGTH, 0) <= 0) {
        printf("%s didn't insert a nickname.\n", client->ip);
        leave_flag = 1;
    } else {
        strncpy(client->name, nickname, NAME_LENGTH);
        printf("%s(%s)(%d) joins the chatroom.\n", client->name, client->ip, client->socket);
        sprintf(send_buffer, "%s joins the chatroom.", client->name);
    }


while (1){

    memset(send_buffer, '\0', LENGTH_SEND);
    sprintf(send_buffer, "\nWelcome to RandomChat!\nChoose your room.\n\n1 - Politics [%d/10]\n2 - Computers [%d/10]\n", room1->howmany, room2->howmany);
    send(client->socket, send_buffer, LENGTH_SEND, 0);
    memset(send_buffer, '\0', LENGTH_SEND);

    while (1){
    // Choosing room

      memset(chosenRoom, '\0', ROOM_CHOICE);
      int receive = recv(client->socket, chosenRoom, ROOM_CHOICE, 0);
      if (receive <= 0) {
          printf("%s didn't choice a room.\n", client->ip);
          leave_flag = 1;
      } else {
        if (strcmp(chosenRoom, "1") == 0){
          client->room = room1;
          pthread_mutex_lock(&client->room->mutex);
            client->room->howmany++;
          pthread_mutex_unlock(&client->room->mutex);
        } else if (strcmp(chosenRoom, "2") == 0){
          client->room = room2;
          pthread_mutex_lock(&client->room->mutex);
            client->room->howmany++;
          pthread_mutex_unlock(&client->room->mutex);
        } else {
          printf("Received %d byte\n", receive);
          printf("Wrong choice. You chose: %s\n", chosenRoom);
          memset(send_buffer, '\0', LENGTH_SEND);
          sprintf(send_buffer, "Wrong choice. Try again\n");
          send(client->socket, send_buffer, LENGTH_SEND, 0);
          memset(send_buffer, '\0', LENGTH_SEND);
          continue;
        }
      }
      printf("%s(%s)(%d) chose room %s.\n", client->name, client->ip, client->socket, chosenRoom);
      memset(send_buffer, '\0', LENGTH_SEND);
      sprintf(send_buffer,"\nYou chose room %s\n", chosenRoom);
      send(client->socket, send_buffer, LENGTH_SEND, 0);
      memset(send_buffer, '\0', LENGTH_SEND);
      break;
  }


    // Pairing

    pairing_with_client(client);

    // Conversation
    printf("Conversation begins between socket: %d and socket:%d\n", client->socket, client->pairSock);

    while (1) {
        if (leave_flag == 1 || leave_flag == 2) {
            break;
          }

        memset(recv_buffer, '\0', LENGTH_MSG);
        int receive = recv(client->socket, recv_buffer, LENGTH_MSG, 0);
        if (receive > 0) {
            if (strlen(recv_buffer) == 0) {
                continue;
            }
            if (strcmp(recv_buffer, "menu") == 0){
              memset(send_buffer, '\0', LENGTH_SEND);
              sprintf(send_buffer, "backToMenu");
              leave_flag = 2;

            } else if (strcmp(recv_buffer, "makeMeGoBack") == 0){
              memset(send_buffer, '\0', LENGTH_SEND);
              sprintf(send_buffer, "\nThe user you were talking to quitted the chatroom.\n\nGoing back to menu in...");
              send(client->socket, send_buffer, LENGTH_SEND, 0);
              memset(send_buffer, '\0', LENGTH_SEND);
              for (int i = 3; i>0; i--){
                memset(send_buffer, '\0', LENGTH_SEND);
                sprintf(send_buffer, "%d", i);
                send(client->socket, send_buffer, LENGTH_SEND, 0);
                sleep(1);
              }
              memset(send_buffer, '\0', LENGTH_SEND);
              sprintf(send_buffer, "ricomincia");
              send(client->socket, send_buffer, LENGTH_SEND, 0);
              memset(send_buffer, '\0', LENGTH_SEND);
              leave_flag = 2;
              break;
            } else {
              memset(send_buffer, '\0', LENGTH_SEND);
              sprintf(send_buffer, "%s:%s", client->name, recv_buffer);
            }
        } else if (receive == 0 || strcmp(recv_buffer, "exit") == 0) {
            printf("%s(%s)(%d) quitted the chatroom.\n", client->name, client->ip, client->socket);
            memset(send_buffer, '\0', LENGTH_SEND);
            sprintf(send_buffer, "backToMenu");
            leave_flag = 1;
        } else {
            printf("Fatal Error: -1\n");
            leave_flag = 1;
        }
        send_to_other_client(client, send_buffer);
  }

    if (leave_flag == 1){ // the client wrote 'exit'. He wants to close the program.
      client->room->howmany--;
      close(client->socket);
      free(client);
      printf("LEAVE FLAG 1\n");
      break;

    } else if (leave_flag == 2){ // The client wrote 'menu'. He wants to tell the other clients he is going back to menu
      client->room->howmany--;
      client->room = NULL;
      client->paired = 0;
      leave_flag = 0;
      printf("LEAVE FLAG 2\n");
    }
  }


}




int main(int argc, char *argv[])
{
    signal(SIGINT, catch_ctrl_c_and_exit);

    // Creating socket
    server_sockfd = socket(AF_INET , SOCK_STREAM , 0);
    if (server_sockfd == -1) {
        printf("Failed socket creation.\n");
        exit(EXIT_FAILURE);
    }

    // Informations
    struct sockaddr_in server_info, client_info;
    int s_addrlen = sizeof(server_info);
    int c_addrlen = sizeof(client_info);
    memset(&server_info, 0, s_addrlen);
    memset(&client_info, 0, c_addrlen);
    server_info.sin_family = PF_INET;
    server_info.sin_addr.s_addr = INADDR_ANY;
    server_info.sin_port = htons(8888);

    // Bind e Listen
    int opt = 1;
    setsockopt(server_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    bind(server_sockfd, (struct sockaddr *)&server_info, s_addrlen);
    listen(server_sockfd, 50);

    // Stampa Server IP
    getsockname(server_sockfd, (struct sockaddr*) &server_info, (socklen_t*) &s_addrlen);
    printf("Server started: %s:%d\n", inet_ntoa(server_info.sin_addr), ntohs(server_info.sin_port));

    // Allocating rooms

    room1 = (Room*) malloc(sizeof(Room));
    room2 = (Room*) malloc(sizeof(Room));
    room3 = (Room*) malloc(sizeof(Room));
    room4 = (Room*) malloc(sizeof(Room));
    room5 = (Room*) malloc(sizeof(Room));

    room1->howmany = 0;
    room2->howmany = 0;
    room3->howmany = 0;
    room4->howmany = 0;
    room5->howmany = 0;

    pthread_mutex_init(&room1->mutex, NULL);
    pthread_mutex_init(&room2->mutex, NULL);
    pthread_mutex_init(&room3->mutex, NULL);
    pthread_mutex_init(&room4->mutex, NULL);
    pthread_mutex_init(&room5->mutex, NULL);

    pthread_cond_init(&room1->cond, NULL);
    pthread_cond_init(&room2->cond, NULL);
    pthread_cond_init(&room3->cond, NULL);
    pthread_cond_init(&room4->cond, NULL);
    pthread_cond_init(&room5->cond, NULL);


  
    while (1) {
        client_sockfd = accept(server_sockfd, (struct sockaddr*) &client_info, (socklen_t*) &c_addrlen);

        // Print Client IP
        getpeername(client_sockfd, (struct sockaddr*) &client_info, (socklen_t*) &c_addrlen);
        printf("Client %s:%d come in.\n", inet_ntoa(client_info.sin_addr), ntohs(client_info.sin_port));

        // Append nexted list for clients
        Client *c = newNode(client_sockfd, inet_ntoa(client_info.sin_addr));

        int i = 0;
        pthread_mutex_lock(&globalMutex);
        while (i < MAX_CLIENTS){
          if (!clientList[i]){
            clientList[i] = c;
            break;
          } else {
            i++;
          }
        }
        pthread_mutex_unlock(&globalMutex);


        pthread_t id;
        if (pthread_create(&id, NULL, (void *)client_handler, (void *)c) != 0) {
            perror("Create pthread error!\n");
            exit(EXIT_FAILURE);
        }
    }

    return 0;
}

Server.h

#ifndef LIST
#define LIST

typedef struct Room_Chat {
  struct ClientNode* waitList[MAX_ROOM_WAITLIST];
  pthread_mutex_t mutex;
  pthread_cond_t cond;
  int howmany;
} Room;

typedef struct ClientNode {
    int socket; // client's file descriptor
    char ip[16]; // client's ip
    char name[NAME_LENGTH]; // client's nickname
    Room* room;
    int pairSock;
    char pairName[NAME_LENGTH]; // name of the client you're paired with
    int paired; // if you're paired with someone or not
} Client;


Client *newNode(int sockfd, char* ip) {
    Client *np = (Client *)malloc( sizeof(Client) );
    np->socket = sockfd;
    strncpy(np->ip, ip, 16);
    strncpy(np->name, "NULL", 5);
    strncpy(np->pairName, "NULL", 5);
    np->paired = 0;
    np->pairSock = 0;
    return np;
}


Client *copyNode(Client* np){
  Client* node = (Client* )malloc(sizeof(Client));
  node->socket = np->socket;
  strncpy(node->ip, np->ip, 16);
  strncpy(node->name, np->ip, NAME_LENGTH);
  strncpy(node->pairName, np->ip, 5);
  node->paired = np->paired;
  node->pairSock = np->pairSock;
  return node;
}

void add_to_waiting_list(Client* client){
  for (int i = 0; i < MAX_ROOM_WAITLIST; i++){
    if (!client->room->waitList[i]){
      client->room->waitList[i] = client;
      break;
    }
  }
  return;
}

void remove_from_waiting_list(Client* client){
  for (int i = 0; i < MAX_ROOM_WAITLIST; i++){
    if (client->room->waitList[i] && client->room->waitList[i]->socket == client->socket){
      client->room->waitList[i] = NULL;
      break;
    }
  }
  return;
}



#endif // LIST

Client:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include "proto.h"


// Variabili globali
int sockfd = 0;
char nickname[NAME_LENGTH] = "";
char choiceRoom[ROOM_CHOICE] = ""; // here i save the room i chose, so i can send it to the server
char choiceRoom_buffer[LENGTH_SEND] = {}; // buffer where i receive the list of rooms from the server
char okChoice[LENGTH_SEND] = {}; // a flag buffer. If the server tells me my choice was invalid, i save here the message
volatile sig_atomic_t flag = 0;


void string_trim(char* arr, int length) {
// This function trims the nickname, so if i write 'Alex       ', it becomes 'Alex'
    int i;
    for (i = 0; i < length; i++) {
        if (arr[i] == '\n') {
            arr[i] = '\0';
            break;
        }
    }
}

void overwrite_stdout() {
// This function puts '>' before every message i write
    printf("\r%s", "> ");
    fflush(stdout);
}

void exit_from_client(int sig) {
  flag = 1;
}

void back_to_menu(int sig){
  flag = 2;
}

void send_msg_handler() {
  // Function for the sender thread
    char message[LENGTH_MSG] = {};
    while (1) {
        overwrite_stdout();
        memset(message, '\0', LENGTH_MSG);
        while (fgets(message, LENGTH_MSG, stdin) != NULL) {
            string_trim(message, LENGTH_MSG);
            if (strlen(message) == 0) {
                overwrite_stdout();
            } else {
                break;
            }
        }
        send(sockfd, message, LENGTH_MSG, 0);
        memset(message, '\0', LENGTH_MSG);
        if (strcmp(message, "exit") == 0) {
          exit_from_client(2);
          break;
        }
        if (strcmp(message, "menu") == 0){
          back_to_menu(2);
          break;
        }
      }
}


void recv_msg_handler() {
  // Function for receiver thread
    char receiveMessage[LENGTH_SEND] = {};
    while (1) {
        memset(receiveMessage, '\0', LENGTH_SEND);
        int receive = recv(sockfd, receiveMessage, LENGTH_SEND, 0);
        if (receive > 0) {
          if (strcmp(receiveMessage, "backToMenu") == 0){
            send(sockfd, "makeMeGoBack", LENGTH_SEND, 0);
            memset(receiveMessage, '\0', LENGTH_SEND);
          } else if (strcmp(receiveMessage, "ricomincia") == 0) {
            back_to_menu(2);
            break;
          } else {
            printf("\r%s\n", receiveMessage);
            overwrite_stdout();
          }
        } else if (receive == 0) {
            break;
        } else {
            // -1
        }
    }
}




int main(int argc, char *argv[]){

  signal(SIGINT, exit_from_client);


  // Creating socket
  sockfd = socket(AF_INET , SOCK_STREAM , 0);
  if (sockfd == -1) {
      printf("Failed socket creation.");
      exit(EXIT_FAILURE);
  }

  // Socket info
  struct sockaddr_in server_info, client_info;
  int s_addrlen = sizeof(server_info);
  int c_addrlen = sizeof(client_info);
  memset(&server_info, 0, s_addrlen);
  memset(&client_info, 0, c_addrlen);
  server_info.sin_family = PF_INET;
  server_info.sin_addr.s_addr = inet_addr("127.0.0.1");
  server_info.sin_port = htons(8888);

  // Connection to server
  int err = connect(sockfd, (struct sockaddr *)&server_info, s_addrlen);
  if (err == -1) {
      printf("Connessione al server fallita.\n");
      exit(EXIT_FAILURE);
  }

  // Getting the info to print
  getsockname(sockfd, (struct sockaddr*) &client_info, (socklen_t*) &c_addrlen);
  getpeername(sockfd, (struct sockaddr*) &server_info, (socklen_t*) &s_addrlen);


  printf("Connected to Server: %s:%d\n", inet_ntoa(server_info.sin_addr), ntohs(server_info.sin_port));
  printf("Your IP address is: %s:%d\n", inet_ntoa(client_info.sin_addr), ntohs(client_info.sin_port));

  // Choose Nickname

 while (strlen(nickname) == 0 || strlen(nickname) >= NAME_LENGTH-1){
    printf("\nInsert your nickname: ");

    if (fgets(nickname, NAME_LENGTH, stdin) != NULL) {
      string_trim(nickname, NAME_LENGTH);
    }
    if (strlen(nickname) == 0){
      printf("\nCannot insert an empty nickname. Try again.\n");
    } else if (strlen(nickname) >= NAME_LENGTH-1){
      printf("\nCannot insert a nickname that long. Try again.\n");
    }
  }

  // Send the nickname to server
  send(sockfd, nickname, NAME_LENGTH, 0);
  memset(nickname, '\0', NAME_LENGTH);



// The server sends me the rooms

  memset(choiceRoom_buffer, '\0', LENGTH_SEND);
  if (recv(sockfd, choiceRoom_buffer, LENGTH_SEND, 0) <= 0) {
    printf("***Error. Cannot receive rooms.***");
    exit(EXIT_FAILURE);
  }


while (1){


  // Choosing room
    while (1){
      printf("%s\n", choiceRoom_buffer);
      memset(choiceRoom, '\0', ROOM_CHOICE);
      scanf("%s", choiceRoom);
      send(sockfd, choiceRoom, ROOM_CHOICE, 0);
      memset(choiceRoom, '\0', ROOM_CHOICE);

      memset(okChoice, '\0', LENGTH_SEND);
      if (recv(sockfd, okChoice, LENGTH_SEND, 0) <=0) {
        printf("**Errore**\n");
        continue;
      } else {
        if (strcmp(okChoice, "Invalid choice.\n") == 0){
          printf("%s\n", okChoice);
          continue;
        } else {
          printf("%s\n", okChoice);
          break;
        }
      }
   }

   pthread_t send_msg_thread;
   if (pthread_create(&send_msg_thread, NULL, (void *) send_msg_handler, NULL) != 0) {
       printf ("Create pthread error!\n");
       exit(EXIT_FAILURE);
   }

   pthread_t recv_msg_thread;
   if (pthread_create(&recv_msg_thread, NULL, (void *) recv_msg_handler, NULL) != 0) {
       printf ("Create pthread error!\n");
       exit(EXIT_FAILURE);
   }





   while (1) {
      if(flag == 1) { // exit
          printf("\nBye\n");
          close(sockfd);
          return 0;
      } else if (flag == 2){ // menu
        // if (pthread_cancel(tid_sender) != 0){
        //   printf("Failure closing thread sendern");
        // }
        // if (pthread_cancel(tid_receiver) != 0){
        //   printf("Failure closing thread receiver\n");
        // }

        // This part is a work in progress. I want to close these threads, so when i choose a room, they start again.
        break;
      }
    }


}

    return 0;

}

This is what my output looks like, server and client.

As soon as the second client goes automatically back to menu, everything hangs. If a client selects a room, nothing happens.

Any help? Sorry for the long post and for all of this code, but i tried to give you every possible helpful information.

0 Answers
Related