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.