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.
Related
I made a chat application using sockets in c language. When i run server and client on same device it works fine. But, when i run client and server on different device client shows connect error. is this problem related to ip address.
server side 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 <pthread.h>
#include <sys/types.h>
#include <signal.h>
#define MAX_CLIENTS 100
#define BUFFER_SZ 2048
static _Atomic unsigned int cli_count = 0;
static int uid = 10;
/* Client structure */
typedef struct{
struct sockaddr_in address;
int sockfd;
int uid;
char name[32];
} client_t;
client_t *clients[MAX_CLIENTS];
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
void str_overwrite_stdout() {
printf("\r%s", "> ");
fflush(stdout);
}
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
void print_client_addr(struct sockaddr_in addr){
printf("%d.%d.%d.%d",
addr.sin_addr.s_addr & 0xff,
(addr.sin_addr.s_addr & 0xff00) >> 8,
(addr.sin_addr.s_addr & 0xff0000) >> 16,
(addr.sin_addr.s_addr & 0xff000000) >> 24);
}
/* Add clients to queue */
void queue_add(client_t *cl){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(!clients[i]){
clients[i] = cl;
break;
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Remove clients to queue */
void queue_remove(int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i < MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid == uid){
clients[i] = NULL;
break;
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Send message to all clients except sender */
void send_message(char *s, int uid){
pthread_mutex_lock(&clients_mutex);
for(int i=0; i<MAX_CLIENTS; ++i){
if(clients[i]){
if(clients[i]->uid != uid){
if(write(clients[i]->sockfd, s, strlen(s)) < 0){
perror("ERROR: write to descriptor failed");
break;
}
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Handle all communication with the client */
void *handle_client(void *arg){
char buff_out[BUFFER_SZ];
char name[32];
int leave_flag = 0;
cli_count++;
client_t *cli = (client_t *)arg;
// Name
if(recv(cli->sockfd, name, 32, 0) <= 0 || strlen(name) < 2 || strlen(name) >= 32-1){
printf("Didn't enter the name.\n");
leave_flag = 1;
} else{
strcpy(cli->name, name);
sprintf(buff_out, "%s has joined\n", cli->name);
printf("%s", buff_out);
send_message(buff_out, cli->uid);
}
bzero(buff_out, BUFFER_SZ);
while(1){
if (leave_flag) {
break;
}
int receive = recv(cli->sockfd, buff_out, BUFFER_SZ, 0);
if (receive > 0){
if(strlen(buff_out) > 0){
send_message(buff_out, cli->uid);
str_trim_lf(buff_out, strlen(buff_out));
printf("%s -> %s\n", buff_out, cli->name);
}
} else if (receive == 0 || strcmp(buff_out, "exit") == 0){
sprintf(buff_out, "%s has left\n", cli->name);
printf("%s", buff_out);
send_message(buff_out, cli->uid);
leave_flag = 1;
} else {
printf("ERROR: -1\n");
leave_flag = 1;
}
bzero(buff_out, BUFFER_SZ);
}
/* Delete client from queue and yield thread */
close(cli->sockfd);
queue_remove(cli->uid);
free(cli);
cli_count--;
pthread_detach(pthread_self());
return NULL;
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "127.0.0.1";
int port = atoi(argv[1]);
int option = 1;
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
pthread_t tid;
/* Socket settings */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = inet_addr(ip);
serv_addr.sin_port = htons(port);
/* Ignore pipe signals */
signal(SIGPIPE, SIG_IGN);
if(setsockopt(listenfd, SOL_SOCKET,(SO_REUSEPORT | SO_REUSEADDR),(char*)&option,sizeof(option)) < 0){
perror("ERROR: setsockopt failed");
return EXIT_FAILURE;
}
/* Bind */
if(bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("ERROR: Socket binding failed");
return EXIT_FAILURE;
}
/* Listen */
if (listen(listenfd, 10) < 0) {
perror("ERROR: Socket listening failed");
return EXIT_FAILURE;
}
printf("=== WELCOME TO THE CHATROOM ===\n");
while(1){
socklen_t clilen = sizeof(cli_addr);
connfd = accept(listenfd, (struct sockaddr*)&cli_addr, &clilen);
/* Check if max clients is reached */
if((cli_count + 1) == MAX_CLIENTS){
printf("Max clients reached. Rejected: ");
print_client_addr(cli_addr);
printf(":%d\n", cli_addr.sin_port);
close(connfd);
continue;
}
/* Client settings */
client_t *cli = (client_t *)malloc(sizeof(client_t));
cli->address = cli_addr;
cli->sockfd = connfd;
cli->uid = uid++;
/* Add client to the queue and fork thread */
queue_add(cli);
pthread_create(&tid, NULL, &handle_client, (void*)cli);
/* Reduce CPU usage */
sleep(1);
}
return EXIT_SUCCESS;
}
client side code:
#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>
#define LENGTH 2048
// Global variables
volatile sig_atomic_t flag = 0;
int sockfd = 0;
unsigned char name[32];
void str_overwrite_stdout() {
printf("%s", "> ");
fflush(stdout);
}
void str_trim_lf (char* arr, int length) {
int i;
for (i = 0; i < length; i++) { // trim \n
if (arr[i] == '\n') {
arr[i] = '\0';
break;
}
}
}
void catch_ctrl_c_and_exit(int sig) {
flag = 1;
}
void send_msg_handler() {
unsigned char message[LENGTH] = {};
unsigned char buffer[3000] = {};
while(1) {
str_overwrite_stdout();
fgets(message, LENGTH, stdin);
str_trim_lf(message, LENGTH);
if (strcmp(message, "exit") == 0) {
break;
} else {
sprintf(buffer, "%s: %s\n", name, message);
send(sockfd, buffer, strlen(buffer), 0);
}
bzero(message, LENGTH);
bzero(buffer, LENGTH + 32);
}
catch_ctrl_c_and_exit(2);
}
void recv_msg_handler() {
char message[LENGTH] = {};
while (1) {
int receive = recv(sockfd, message, LENGTH, 0);
if (receive > 0) {
printf("%s", message);
str_overwrite_stdout();
} else if (receive == 0) {
break;
} else {
// -1
}
memset(message, 0, sizeof(message));
}
}
int main(int argc, char **argv){
if(argc != 2){
printf("Usage: %s <port>\n", argv[0]);
return EXIT_FAILURE;
}
char *ip = "152.168.0.100";
int port = atoi(argv[1]);
signal(SIGINT, catch_ctrl_c_and_exit);
printf("Please enter your name: ");
fgets(name, 32, stdin);
str_trim_lf(name, strlen(name));
if (strlen(name) > 32 || strlen(name) < 2){
printf("Name must be less than 30 and more than 2 characters.\n");
return EXIT_FAILURE;
}
struct sockaddr_in server_addr;
/* Socket settings */
sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(ip);
server_addr.sin_port = htons(port);
// Connect to Server
int err = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (err == -1) {
printf("ERROR: connect\n");
return EXIT_FAILURE;
}
// Send name
send(sockfd, name, 32, 0);
printf("=== WELCOME TO THE CHATROOM ===\n");
pthread_t send_msg_thread;
if(pthread_create(&send_msg_thread, NULL, (void *) send_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
pthread_t recv_msg_thread;
if(pthread_create(&recv_msg_thread, NULL, (void *) recv_msg_handler, NULL) != 0){
printf("ERROR: pthread\n");
return EXIT_FAILURE;
}
while (1){
if(flag){
printf("\nBye\n");
break;
}
}
close(sockfd);
return EXIT_SUCCESS;
}
it is showing connect error when i run it on different machines.
So as far as I can tell, the codes that you posted are incomplete, but let's assume that, as you say, everything is working fine, it work in the local machine, but when you run the two programs in two different devices, the client fails to connect to the server.
Then I would ask you the question, as I also asked it in the comment: are the two devices in the same subnet? You have a home router, and both devices are connected to this router?
If not, then that will be the problem. I am not sure how deep you understand IP addresses and subnets, but here is a simple summary. When you look at the IP address on your PC, you see your private IP address that was given you by the router. Let's say, that you have two machines connected to the same router, one has the IP address of 192.168.0.101, the other 192.168.0.102. If 192.168.0.101 is the client and wants to connect to the server that runs on 192.168.0.102, it should work without problems.
But let's say that you send the server code to me. I run the server on my computer, which has the IP address of 192.168.0.103, you still run the client at home on 192.168.0.101. In this case, you won't be able to connect to my machine, because the 192.168.0.103 is the private IP address given out by my own router. For that to work, I would have to at least configure my router to open up a port and forward the packets coming to that port to my PC. Then I would have to give you the public IP and the port. And so if the firewall is configured so that it allows your packets into my network, then it might work.
Of course it is a bit more complicated than this, but this is the general idea.
If this is not the case for you, and the devices are on the same subnet, then we would simply need more information and the comlete code.
I have a server-client program written in C. My problem is that I have a while loop in which I receive and send data. it happens a lot when i need my server to do other things but it just stops at recv() function and waits for data from client. How can I overcome it? I've tried this:
int waitForAnswer =1;
if((childpid = fork()) == 0){
close(socketfd);
close(fd[0]);
while(1){
if(waitForAnswer) {
receive(newSocket, buff) == 0;
parseRecvMess(buff);
}
waitForAnswer =0;
but it doesn't work. For some reason the program finishes with an exit code 1, at accepting socket with a "No Socket" error.
Here is my program ;
nt main(){
int size = 256;
char buff[size];
char sbuff[size];
int n;
int reader;
int socketfd, ret;
struct sockaddr_in serverAddr;
int newSocket = -2;
struct sockaddr_in newAddr;
socklen_t addr_size;
pid_t childpid;
pocet_hracu = 0;
srand(time(NULL));
initializeLobby();
memset(buff, 0, size);
memset(sbuff, 0, size);
socketfd = socket(AF_INET,SOCK_STREAM,0);
if(socketfd < 0){
printf("\n error in socket creation");
return -1;
}
printf("\n Server socket is created\n");
memset(&serverAddr, '\0', sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(PORT);
serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
ret = bind(socketfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
if(ret < 0){
printf("Error in binding\n");
return -1;
}
printf("[*]Bind to port %d\n", PORT);
if(listen(socketfd, 10) == 0){
printf("Listening...\n");
}else{
printf("Error in binding\n");
}
// pipe pro komunikaci
int readpipe;
int writepipe;
int fd[2];
pipe(fd);
for (;;) {
newSocket = accept(socketfd, (struct sockaddr *) &newAddr, &addr_size);
if (newSocket < 0) {
printf("No socket\n");
exit(1);
}
// struct timeval tv;
// tv.tv_sec = 1;
// tv.tv_usec = 0;
// setsockopt(socketfd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
// read the message from client and copy it in buffer
receive(newSocket, buff);
parseRecvMess(buff);
printf("\n%s sent: %s", user, command);
if (strncmp(" new", command, 4) == 0) {
player newPlayer;
newPlayer.name = (char *) malloc(sizeof(char) * 10); // *login must contain up to 10 characters
strcpy(newPlayer.name, user);
newPlayer.gameScore = 0;
newPlayer.roundScore = 0;
newPlayer.socket = newSocket;
// TODO vztvorit pipe pro kounikaci
// newPlayer.readpipe
// newPlayer.writepipe
addToPlayersArray(&newPlayer); //TODO player index is not increasing
addToLobby(&newPlayer);
printf("Server : %s uspesne prihlasen.\n", newPlayer.name);
}
bzero(buff, size);
printf("To client : OK\n");
strcpy(sbuff, "OK\n" );
sendMess(newSocket, sbuff, sizeof(sbuff));
int waitForAnswer =1;
if((childpid = fork()) == 0){
close(socketfd);
close(fd[0]);
while(1){
printf("MAIN->while(1)\n");
// poslouchame zpravy
//if(waitForAnswer) {
receive(newSocket, buff) == 0;
parseRecvMess(buff);
//}
waitForAnswer =0;
if(strcmp(buff,":exit") == 0){
printf("Disconnected from %s:%d\n", inet_ntoa(newAddr.sin_addr), ntohs(newAddr.sin_port));
break;
} else{
// send buff to parent
int i = findPlayerIndex();
memset(sbuff, '\0', size) ;
str str;
strcat(sbuff, "//:");
sprintf(str, "%d", i);
strcat(sbuff, str);
strcat(sbuff, ":");
strcat(sbuff, command);
strcat(sbuff, "\n");
write(fd[1], sbuff , strlen(sbuff)+1);
bzero(buff, sizeof(buff));
if (strncmp("roll", command, 4) == 0) {
//TODO
hod();
}
if (strncmp("none", command, 4) == 0) {
//TODO
}
strcpy(sbuff, "OK\n" );
write(newSocket, sbuff, strlen(sbuff));
bzero(buff, sizeof(buff));
bzero(sbuff, sizeof(sbuff));
}
}
}
close(fd[1]);
int nbytes = read(fd[0], buff, sizeof(buff));
printf("PARENT: buffer : %s\n",buff);
parseRecvMess(buff);
char *index1= (char *)malloc(sizeof(char)*10);
strcpy(index1, user);
int index = atoi(index1);
char *command1= (char *)malloc(sizeof(char)*10);
strcpy(command1, command);
//printf("PARENT - index:%d, command:%s\n", index, command );
printf("PARENT - index:%d, jmeno:%s, command:%s\n", index, players_array[index].name, command1 );
bzero(buff, sizeof(buff));
if (strncmp("room", command1, 4) == 0) {
// vlozit do mistnosti
printf("command = room\n");
command = strtok(command1," ");
command = strtok(NULL," "); // volba
printf("command(volba): + %s \n", command);
//int i = findPlayerIndex(); // find player in array based on user name
printf("player index: = %d \n", index);
//printPlayer(&players_array[i]);
volbaHry(&players_array[index], command, lobby);
//players_array[i].mist->pocet_hracu++;
printf("PARENT: array pocet hracu %d\n",players_array[index].mist->pocet_hracu);
printf("PARENT: lobby pocet hracu %d\n",lobby[0].pocet_hracu);
str str;
memset(sbuff, '\0', size);
//pokud v mistnosti jsou 2 hraci => zacina hra
if( players_array[index].mist->pocet_hracu == 2){
printf("Hra zacina ...\n");
hra(players_array[index].mist);
}
printMistnost(players_array[index].mist);
}
//players_array[index].mist = lobby[volba - 1];
// }
}
close(newSocket);
return 0;
}
and this is my receive() function:
int receive(int socket, char *buff){
int size = 256;
int reader = recv(socket, buff, size * sizeof(char), 0);
if (reader == -1) {
printf("BREAK reader == -1\n");
perror("recv()");
return(-1);
} else if (reader == 0) {
printf("BREAK reader == 0\n");
return(-2);
} else if(checkMessage(buff) == 0){
printf("ERROR: recieved message is not valid\n");
return(-3);
}
return 0;
}
if I'm not using waitForAnswer variable the program connects without any errors with the client, but at some points stops at the beginning of while waiting for another message.
You can also use ioctl() to check for data if the socket is set to non blocking
#include <sys/ioctl.h>
int status;
do{
ioctl(socketfd, FIONREAD, &status);
if( status > 0 ){
//packet waiting to be read
recv(socketfd, buff, size * sizeof(char), 0);
}
else{
// no data .. so do something else
}
} while (!must_stop);
Call recv with the nonblocking flag:
recv(socket, buff, size, MSG_DONTWAIT);
And the function will immediately return if there was no data to read.
I want to build a chat program, but the problem is that once client sends a message to server, all other client will receive this message from server, but client don't know when the message will come. In the client's main loop it will block on fgets() and wait for user to input a command or message. I need the client program to receive the message and print it while waiting for input from user. How can I do that ?
here's the code:
I haven't write recv for message since I don't know where to put it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <time.h>
#define LOGIN 1
#define LO_ACK 2
#define LO_NACK 3
#define EXIT 4
#define JOIN 5
#define JN_ACK 6
#define JN_NACK 7
#define LEAVE_SESS 8
#define NEW_SESS 9
#define NS_ACK 10
#define MESSAGE 11
#define QUERY 12
#define QU_ACK 13
struct packet {
unsigned int type;
unsigned int size;
char source[20];
char data[500];
};
int encode(struct packet temp, char *data) {
sprintf(data, "%d:%d:%s:", temp.type, temp.size,
temp.source);
int length = strlen(data);
int i;
for (i = 0; i < temp.size; i++) {
data[length + i] = temp.data[i];
}
data[length + i] = '\0';
return length;
}
struct packet decode(char *data) {
int i, j;
struct packet message;
char temp[100];
char source[20];
sscanf(data, "%d:%d", &message.type, &message.size);
sprintf(temp, "%d:%d", message.type, message.size);
int length = strlen(temp);
for (i = length + 1; data[i] != ':'; i++) {
message.source[i - length - 1] = data[i];
}
for (j = 0; j < message.size; j++) {
message.data[j] = data[j + i + 1];
}
return message;
}
int main(void) {
int sockfd, numbytes;
struct addrinfo hints, *servinfo, *p;
int rv;
int login = 0;
char me[20];
while (1) {
char buf[500];
char input[100];
char *command;
char arg1[20], arg2[20], arg3[20], arg4[20], arg5[20];
int i, j, k, l, m;
fgets(input, 100, stdin);
if (strlen(input) < 3)
continue;
if (input[0] == '/') {//command
command = &input[1];
//get first argument
for (i = 0; command[i] != '\0' && command[i] != ' '; i++) {
arg1[i] = command[i];
}
//arg1[i] = '\0';
if (strcmp(arg1, "login") == 0) {//login
//get id,password,ip,port
if (login == 1) {
printf("error: already login\n");
continue;
}
for (j = i + 1; command[j] != '\0' && command[j] != ' '; j++) {//id
arg2[j - i - 1] = command[j];
}
//arg1[j-i+1] = '\0';
for (k = j + 1; command[k] != '\0' && command[k] != ' '; k++) {//password
arg3[k - j - 1] = command[k];
}
for (l = k + 1; command[l] != '\0' && command[l] != ' '; l++) {//ip
arg4[l - k - 1] = command[l];
}
for (m = l + 1; command[m] != '\0'; m++) {//port
arg5[m - l - 1] = command[m];
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(arg4, arg5, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "client: failed to connect\n");
return 2;
}
freeaddrinfo(servinfo);
struct packet tosend;
tosend.type = LOGIN;
sprintf(tosend.data, "%s %s", arg2, arg3);
strcpy(tosend.source, arg2);
tosend.size = strlen(tosend.data);
char message[500];
encode(tosend, message);
send(sockfd, message, strlen(message), 0);
usleep(100);
recv(sockfd, buf, strlen(buf), 0);
struct packet reply;
reply = decode(buf);
if (reply.type == LO_ACK) {
printf("login successful\n");
strcpy(me, arg2);
login = 1;
continue;
} else if (reply.type == LO_NACK) {
printf("login failed: %s\n", reply.data);
continue;
}
} else if (strcmp(arg1, "createsession") == 0) {
if (login == 0) {
printf("error: not login\n");
continue;
}
for (j = i + 1; command[j] != '\0'; j++) {//session name
arg2[j - i - 1] = command[j];
}
struct packet tosend;
tosend.type = NEW_SESS;
strcpy(tosend.data, arg2);
strcpy(tosend.source, me);
tosend.size = strlen(tosend.data);
char message[500];
encode(tosend, message);
send(sockfd, message, strlen(message), 0);
usleep(100);
recv(sockfd, buf, strlen(buf), 0);
struct packet reply;
reply = decode(buf);
if (reply.type == NS_ACK) {
printf("create session successful\n");
continue;
} else if (reply.type == JN_ACK) {
printf("session already exist, join session successful\n");
continue;
}
} else if (strcmp(arg1, "joinsession") == 0) {
if (login == 0) {
printf("error: not login\n");
continue;
}
for (j = i + 1; command[j] != '\0'; j++) {//session name
arg2[j - i - 1] = command[j];
}
struct packet tosend;
tosend.type = JOIN;
strcpy(tosend.data, arg2);
strcpy(tosend.source, me);
tosend.size = strlen(tosend.data);
char message[500];
encode(tosend, message);
send(sockfd, message, strlen(message), 0);
usleep(100);
recv(sockfd, buf, strlen(buf), 0);
struct packet reply;
reply = decode(buf);
if (reply.type == JN_ACK) {
printf("join session successful\n");
continue;
}
} else if (strcmp(arg1, "leavesession") == 0) {
if (login == 0) {
printf("error: not login\n");
continue;
}
struct packet tosend;
tosend.type = LEAVE_SESS;
strcpy(tosend.data, "none");
strcpy(tosend.source, me);
tosend.size = strlen(tosend.data);
char message[500];
encode(tosend, message);
send(sockfd, message, strlen(message), 0);
printf("leave session successful\n");
continue;
} else if (strcmp(arg1, "list") == 0) {
if (login == 0) {
printf("error: not login\n");
continue;
}
struct packet tosend;
tosend.type = QUERY;
strcpy(tosend.data, "none");
strcpy(tosend.source, me);
tosend.size = strlen(tosend.data);
char message[500];
encode(tosend, message);
send(sockfd, message, strlen(message), 0);
usleep(100);
recv(sockfd, buf, strlen(buf), 0);
struct packet reply;
reply = decode(buf);
printf("%s", reply.data);
continue;
} else {
printf("invalid command\n");
continue;
}
} else {//message
if (login == 0) {
printf("error: not login\n");
continue;
}
struct packet tosend;
tosend.type = MESSAGE;
strcpy(tosend.data, input);
strcpy(tosend.source, me);
tosend.size = strlen(tosend.data);
char message[500];
encode(tosend, message);
send(sockfd, message, strlen(message), 0);
continue;
}
}
}
'client don't know when to recv' that's easy: all the time.
There are multiple options:
1) select() on fd's, including stdin (Google for details).
2) poll/epoll() - as above, but improved performance.
3) Have a thread that waits on input and fires messages onto a producer-consumer queue to another 'state-machine' thread that handles those messages, and messages from other threads that handle client recv() data from the server, and performs the required actions.
4) Any other mechanism that might be available from your particular OS.
Check this code here:
#include <stdio.h>
#include <sys/select.h>
#include <unistd.h>
int main()
{
fd_set fds;
while(1){
FD_ZERO(&fds); // you NEED to zero this every loop
FD_SET(STDIN_FILENO, &fds);
// add your socket here
// first argument here will be your socket + 1
select(STDIN_FILENO + 1, &fds, NULL, NULL, NULL);
if (FD_ISSET(STDIN_FILENO, &fds)){
char buffer[128];
fgets(buffer, sizeof(buffer), stdin);
printf("User input - stdin: %s", buffer);
}
// here check if your socket is set and act accordingly
}
return 0;
}
It will read from stdin using select (http://man7.org/linux/man-pages/man2/select.2.html), you can use it as a base to study how select works and expand it for your needs.
You will need to add your socket in the fds so it will wait on the select until either there is an user input to process or there is data on your socket (use FD_ISSET to check).
Good luck.
PS: I used the code from this question Using stdin with select() in C as a base to produce the one I posted.
I am trying to build a server and client for a chat server using sockets. I know that I am supposed to use select() to get input from multiple sockets, but I am not sure how to do it and still read from them properly. As my code sits, it reads perfectly fine from one client, but when two are open it ignores everything that the second client does, until the first is closed.
I am wondering how to properly implement select to make sure that I can take input from multiple sockets. Thanks in advance.
#include "../../include/my.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void interrupt();
void interrupt()
{
my_str("Server exiting.\n");
exit(0);
}
int str_to_num(char* str)
{
int i = 0;
int numDigits = 0;
int ret = 0;
while (str[numDigits] != '\0')
{
numDigits++;
}
while (i < numDigits)
{
int digit = str[i];
if (digit < 48 && digit > 57)
return -1;
digit -= 48;
ret += digit * my_pow(10, numDigits - i - 1);
i++;
}
return ret;
}
char* add_null_term(char* str)
{
int i = 0;
while (str[i] != '\0')
i++;
str[i] = '\0';
return str;
}
int main(int argc, char **argv)
{
int sockfd, newsockfd, portnum;
int len;
char buffer[256];
/*char *username = (char*)malloc(256*sizeof(char));*/
socklen_t clilen;
struct sockaddr_in serv_addr, cli_addr;
/*check args*/
if (argc < 2)
{
my_str("Usage: ./server <port_number>\n");
exit(0);
}
portnum = str_to_num(argv[1]);
if (portnum < 1024)
{
perror("Ports below 1024 are reserved for root access.\n");
exit(0);
}
if (portnum < 1 || portnum > 65535)
{
my_str("Port must be between 1 and 65535\n");
exit(0);
}
signal(SIGINT, interrupt);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
bzero((char*)&serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portnum);
serv_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr))<0)
{
perror("bind");
exit(0);
}
listen(sockfd, 5);
clilen = sizeof(cli_addr);
while(1)
{
if ((newsockfd=accept(sockfd, (struct sockaddr*)&cli_addr, &clilen)) < 0)
{
perror("accept");
exit(0);
}
usleep(2000);
my_str("Server received: ");
while ((len = read(newsockfd, &buffer, 256)) > 0)
{
buffer[my_strlen(buffer)] = '\0';
if (my_strcmp(buffer, "/") == 0)
{
my_str("Error: command ");
my_str(buffer);
my_str("not found.\n");
bzero((char*)&buffer, sizeof(buffer));
}
/*if (count == 0)
{
my_strcpy(username, buffer);
my_str("setting nickname to: ");
my_str(username);
bzero((char*)&buffer, sizeof(buffer));
my_str(buffer);
count++;
my_str("\n");
}*/
/*else if (my_strncmp(buffer, "/me", 3) == 0)
{
my_str(username);
my_str(" ");
my_str(&buffer[4]);
bzero((char*)&buffer, sizeof(buffer));
my_str("\n");
}
else if (my_strncmp(buffer, "/nick", 5) == 0)
{
my_str("Changing nickname of ");
my_str(username);
my_strcpy(username, &buffer[6]);
my_str(" to ");
my_str(username);
bzero((char*)&buffer, sizeof(buffer));
my_str("\n");
}*/
/*else
{*/
/*my_str(username);
my_str(": ");*/
my_str(buffer);
bzero((char*)&buffer, sizeof(buffer));
my_str(buffer);
my_str("\n");
/*}*/
}
my_str("\nServer: Message end. Waiting for next connection.\n");
}
return 0;
}
If your question is HOW to use select() in a server...
Here is a snippet off the top of my head of one technique
to get you started... bear in mind all error checking is not
present, and it is not optimized to function in a round robin
basis...
listen(sockfd, SOMAXCONN)
fd_set my_set;
fd_set wk_set;
FD_ZERO(&my_set); /* initialize fd_set */
FD_SET(sock_fd, &my_set) /* put listener into fd_set */
max_fd = sockfd;
while( TRUE )
{
memcpy(&wk_set, &my_set, sizeof(my_set));
rc = select(max_fd + 1, &wk_set, NULL, NULL, NULL )
if ( rc == -1 )
{
if ( errno == EINTR )
continue;
printf("select failed: errno = %d\n", errno);
exit(1);
}
for(i = 0; i < max_fd; i++) /* check everything in wk_set */
{
if ( FD_ISSET(i, &wk_set)
{
if ( i == sockfd ) /* is it the listener socket?? */
{
wk_sock=accept(sockfd, (struct sockaddr*)&cli_addr, &clilen);
if ( wk_sock == -1 )
{
printf("accept failed: errno=%d\n", errno);
exit(1);
}
FD_SET(wk_sock, &my_set); /* put into fd_set */
if ( wk_sock > max_sock )
max_sock = wk_sock;
}else{
/* ready to read from this socket */
recv_len = recv(i, .... )
if ( recv_len == 0 ) /* client disconnected */
{
close(i);
FD_CLR(i, &my_set);
if (i == max_fd )
{
for(x=0;x<max_fd;x++) /* find new max_fd */
{
if ( FD_ISSET(x, &my_set )
max_fd = x;
}
}
} else {
handle message from client
}
}
}
}
}
I have to build a quiz application.
Details about the application:
1. Each client has to register to server before participating in Quiz. The server will ask
username from each user and generate temporary id for each user.
2. After Registration process clients, who are successfully connected to server will get
Question from server.
3. The client will reply with answer.
4. Server will receive answer from different clients with time stamps, and it will calculate
time difference of each client which is called ∆t.
Define such as:
∆t = (Time Question sent - Time answer received) - RTT
Where RTT is Round Trip Time
Server will select client, whose ∆t is minimum to all and reply with whatever score client will gain remains will not gain any score.
After sending Question server will wait Answer for a particular time periods called (T). If client did not reply within ‘T’ time period Server will skip that Question and goes to next Question.
Pseudocode of main loop in my server code
A. A main while loop which runs once for each question.
B. Inside this first I am accepting login for 10 seconds.
Here I am assigning user Id and all other initialization stuff.
C. Then using `select` to check which are available for writing.
To available connections I am checking `RTT` and then sending question to each user.
D. Then I am waiting for some time to get answers.
Here I am using `select` to determine where the answer is available to read.
E. Then I am repeating steps C. and D.
Problem:
When I connect only to a single client my code works fine for any number of question.
But when I test this code on multiple client with the same client code:
Login for everyone is OK.
Sending First Question to everyone works fine.
Then while waiting for the answer I only received answer from one client. Each client shows that answer is been sent. For second client the second select function doesn't return with readable data availability.
Why for multi-client my code is not working. (According to me the error is somewhere in getting answer).
My Code:
The structure of the packets send can be understand easily from the variable names.
Server.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <error.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include <time.h>
#define PORT "3490" //the port user will be connecting to
#define BACKLOG 10 //how many pending connection queue will hold
#define maxUser 10
#define LOGIN_OK "OK"
#define LOGIN_WrongPassword "NP"
#define LOGIN_WrongUsername "NU"
#define MAX_USERS 10
#define MAX_ANSWER_TIME 10
#define LOGIN_WAIT 10
#define TOTAL_QUES "3"
int users[MAX_USERS][3] = {}; //index is userID, 0 is no user
void sigchld_handler(int s)
{
while(waitpid(-1, NULL, WNOHANG) > 0);
}
//get sockaddr, IPv4 or IPv6
int timer;
void alarm_handler(int s) {
timer = 0;
}
wrongRecv(ssize_t recvd, ssize_t expctd)
{
if(recvd != expctd)
{
printf("Recvd(%zd) bytes not equal to expected(%zd) bytes\n",recvd,expctd);
//getchar();
}
}
//void nextQues(char* quesMsg, char* ques, char* optA, char* optB, char* optC, char* optD)
int nextQues(char* quesMsg, int QID)
{
char ques[40], optA[10], optB[10], optC[10], optD[10], quesId[5];
sprintf(quesId,"%d",QID);
strncpy(ques, "This is the question?",22);
strncpy(optA, "OptionA", 7); strncpy(optB, "OptionB", 7); strncpy(optC, "OptionC", 7); strncpy(optD, "OptionD", 7);
strncpy(quesMsg,quesId,5);
strncpy(quesMsg + 05,ques,40);
strncpy(quesMsg + 45,optA,10);
strncpy(quesMsg + 55,optB,10);
strncpy(quesMsg + 65,optC,10);
strncpy(quesMsg + 75,optD,10);
return 0;
}
//void answerCheck(char* ques, char* optA, char* optB, char* optC, char* optD, char* usrResponse, int rtt, int timeTaken)
void answerCheck(int fd, char usrResponse[6], int rtt, int timeTaken)
{
int responseTime, i;
char actualAnswer[1];
char quesId[5];
printf("fd(%d) quesid(%s) response(%c) rtt(%d) timeTaken(%d)\n", fd, usrResponse, usrResponse[5], rtt, timeTaken );
strncpy(quesId, usrResponse, 5);
actualAnswer[0] = 'B';//we have quesId we can find actual answer on basis of it
if(actualAnswer[0] == usrResponse[5])
{
//printf("%s\n","+++++" );
responseTime = timeTaken - rtt;
//printf("Response Time(%d)\n",responseTime);
//save it with user id
//finding userid
for(i = 0; i < MAX_USERS; i++) {
if(users[i][1] == fd) {
users[i][2] = responseTime;//saving it
//printf("%d\n",i );
}
}
}
}
int compareAnswer() {
int i, min = 2 * MAX_ANSWER_TIME, userIndex;
for(i = 0; i < MAX_USERS; i++) {
if(users[i][2] < min) {
min = users[i][2];
userIndex = i;
}
}
//Increasing Score
users[userIndex][0]++;
//returning fd
return users[userIndex][1];
}
void users_deleteFd(int fd) {
int i;
for (i = 0; i < MAX_USERS; ++i)
{
if(users[i][1] == fd) {
users[i][1] =0;
return;
}
}
}
int rtt_check(int new_fd)
{
ssize_t send_ret, recv_ret;
char rtt_check[1];
time_t rtt1, rtt2;
rtt1 = time(NULL);
send_ret = send(new_fd, "r", 1, 0);
if(send_ret == 0)
{
return -2;
}
wrongRecv(send_ret, 1);
//printf("%s\n","Between two phase of rttCheck" );
recv_ret = recv(new_fd, rtt_check, 1,0);
rtt2 = time(NULL);
if(recv_ret == 0)
{
return -2;
}
wrongRecv(recv_ret,1);
//printf("diff(%d)\n",(int) difftime(rtt2,rtt1));
return (int) difftime(rtt2,rtt1);
}
int login(char user[], char pass[])
{
//for user
static int Id = 0; //when have function getUserID, make it not static and also remove Id++;
if(!strcmp(user,"abhishek") && !strcmp(pass,"abhishek")) {
//Id = getUserID(user);
return ++Id;
}else if(!strcmp(user,"abhishek")){
return 0; //wrong password
}
return -1; //wrong username
}
int totalQues;
int login_setup(int new_fd)
{
//login inititalizations
char login_det[16];
char username[9],password[9], login_statMsg[7], totalQuesMsg[5] = TOTAL_QUES;
totalQues = atoi(totalQuesMsg);
//for user
int userId;
//for wrongRecv
ssize_t send_ret,recv_ret;
//getting username and password
recv_ret = recv(new_fd,login_det,16,0);
if(recv_ret == 0)
{
return -2;
}
wrongRecv(recv_ret,16);
//extracting username nad password
strncpy(username,login_det,8);
strncpy(password,login_det+8,8);
username[8]='\0'; password[8]='\0';
//printf("username(%s) and password(%s)\n",username,password);
if( (userId = login(username,password)) > 0) {
//printf("%d\n",userId);
//sending status
strncpy(login_statMsg, LOGIN_OK, 2);
strncpy(login_statMsg + 2, totalQuesMsg , 5);
send_ret = send(new_fd, login_statMsg,7,0);
if(send_ret == 0)
{
return -2;
}
wrongRecv(send_ret,7);
//TODO error checking then handling if error
//users[userId][0] = 0; //score
users[userId][1] = new_fd; //file descriptor associated with this user
//users[userId][2] = 0; //answer time
return 1;
}
else if(userId == -1) { //wrong username
strncpy(login_statMsg, LOGIN_WrongUsername, 2);
strncpy(login_statMsg + 2, totalQuesMsg , 5);
send_ret = send(new_fd, login_statMsg,7,0);
if(send_ret == 0)
{
return -2;
}
wrongRecv(send_ret,7);
return 0;
}
else{
strncpy(login_statMsg, LOGIN_WrongPassword, 2);
strncpy(login_statMsg + 2, totalQuesMsg , 5);
send_ret = send(new_fd, login_statMsg,7,0);
if(send_ret == 0)
{
return -2;
}
wrongRecv(send_ret,7);
return 0;
}
//TODO erorr handling of above two case
//TODO make login a loop
}
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void)
{
int listen_fd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr;//connection's address info
socklen_t sin_size;
int yes=1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;//IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if((rv = getaddrinfo(NULL,PORT, &hints, &servinfo)) != 0){ //getting which IPv server supports
fprintf(stderr, "getaddrinfo: %s\n",gai_strerror(rv));
return 1;
}
//loop through all the result and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next){
if((listen_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
perror("server : socket");
continue;
}
if(setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
perror("set sockopt");
exit(1);
}
if(bind(listen_fd, p->ai_addr, p->ai_addrlen) == -1){
close(listen_fd);
perror("server: bind");
continue;
}
break;
}
if(p == NULL) {
fprintf(stderr, "server:failed to bind\n");
return 2;
}
freeaddrinfo(servinfo);//all done with this structure
if(listen(listen_fd, BACKLOG) == -1){
perror("listen");
exit(1);
}
//printf("listen_fd(%d)\n",listen_fd );
// sa.sa_handler = sigchld_handler; // reap all dead processes
// sigemptyset(&sa.sa_mask);
// sa.sa_flags = SA_RESTART;
// if(sigaction(SIGCHLD, &sa, NULL) == -1){
// perror("sigaction");
// exit(1);
// }
printf("server waiting for connections.....\n");
fd_set master; //master file descriptor list
fd_set read_fds; //temp file descriptor list for select()
int fdmax;
FD_ZERO(&master); //clear the master and temp sets
FD_ZERO(&read_fds);
FD_SET(listen_fd, &master);
//keep track of the bigge file descriptor
fdmax = listen_fd; // so far it is this one
ssize_t recv_ret, send_ret;
//for login
int loginStatus;
struct sigaction sa;
sa.sa_handler = alarm_handler;
sigemptyset(&sa.sa_mask);
//sa.sa_flags = SA_RESTART;
if(sigaction(SIGALRM, &sa, NULL) == -1){
perror("sigaction");
exit(1);
}
//login while
alarm(LOGIN_WAIT);//accepting login only for 10 seconds
timer = 1;
printf("\n-----------------------------Waiting for users to login for %d seconds.-----------------------------\n",LOGIN_WAIT);
while(timer) {
sin_size = sizeof their_addr;
new_fd = accept(listen_fd, (struct sockaddr *)&their_addr, &sin_size);
if(new_fd == -1){
//perror("accept");
break;// this break is very important , as we are using alarm(Signals) and accept is a blocking function
//If accept is in blocked sate and our signal comes then accept will exit returning error. So
//if error then we have to break else next satements will run on falsy values.
//In reality we dont need this as I alredy set the SA_RESTART flag in sigaction which means
//after returning from the signal handler restart the activity on which you are previously
//instead of starting execution from next line.
}else {
inet_ntop(their_addr.ss_family, get_in_addr((struct sockaddr *)&their_addr), s, sizeof s);
printf("server : got connection from %s\n", s);
//LOGIN //need to call login function via thread because this
//may stop the function if user doesnot respond
loginStatus = login_setup(new_fd);
//adding to select checkup
if(loginStatus) {
printf("User Loginned Succesfully\n");
}
}
}
printf("-----------------------------Login Closed. Now starting the QUIZ.-----------------------------\n");
//for randome seek
srand(time(NULL));
//for main loop counter
int i, win_fd;
//for questions
int QID = 0;
int maxQues_Len = 40, maxOpt_len = 10, maxQuesId_len = 5;//including '\0' this time
char quesMsg[80], answer[6];//score doesnot include \0
//char ques[40], optA[10], optB[10], optC[10], optD[10];
//for time calculation of each answer
ssize_t time_ques, time_ans;
//getting all avialable participants
fdmax = 0;
FD_ZERO(&master);
for(i = 0; i < MAX_USERS; i++) {
if( (new_fd = users[i][1]) != 0){
FD_SET(new_fd, &master);
if(new_fd > fdmax)
fdmax = new_fd;
//printf("%d\n",new_fd);
}
}
int current_rtt;
//while for main quiz
while(totalQues--) {
//checking who are ready for witing
if(select(fdmax+1, NULL, &master, NULL, NULL) == -1){//here select will return withh all the descriptors which are
//ready to write , all others have to miss this question
perror("select");
exit(1);
}
//setting which question to send
QID++;
//for sending questions to all
for(i = 0; i <= fdmax; i++) {
if(FD_ISSET(i, &master)) {
//rtt check
current_rtt = rtt_check(i);
if(current_rtt == -2) {//connection closed
FD_CLR(i, &master);
users_deleteFd(i);
continue;
}
//setting question
//nextQues(quesMsg, ques, optA, optB, optC, optD);
nextQues(quesMsg, QID);
printf("Sending Question QID(%s) fd(%d)\n",quesMsg,i);
//send a question
time_ques = time(NULL);
send_ret = send(i, quesMsg, maxQues_Len + 4 * maxOpt_len + maxQuesId_len, 0);
if(send_ret == 0) {//connection closed
FD_CLR(i, &master);
users_deleteFd(i);
continue;
}
wrongRecv(send_ret, maxQues_Len + 4 * maxOpt_len + maxQuesId_len);
}
}
//ASSUMING Question is send ot all the users at same time
//receiving and waiting for answers
alarm(MAX_ANSWER_TIME);
timer = 1;
FD_ZERO(&read_fds);
read_fds = master;
// unsigned int qq = read_fds.fd_count;
// for (int ii = 0; ii < qq; ++ii)
// {
// printf("%d\n",read_fds.fd_array[i] );
// }
while(timer) {
//printf("HURRAY\n");
if(select(fdmax+1, &read_fds, NULL, NULL, NULL) <=0){
perror("select");
//exit(4);
break;//break is important. Explained above
}
for(i = 0; i <= fdmax; i++) {
//printf("Recving answer I(%d)\n",i);
if(FD_ISSET(i, &read_fds)) {
//receiving answer
//TODO if we get answer to wrong ques
printf("Recving answer I(%d) fdmax (%d)\n",i,fdmax);
recv_ret = recv(i,answer,6,0);
time_ans = time(NULL);
wrongRecv(recv_ret,6);
printf("%s\n",answer );
if(recv_ret == 0)//connection closed
{
FD_CLR(i, &read_fds);
FD_CLR(i, &master);
users_deleteFd(i);
continue;
}else if(recv_ret > 0){
if(QID == atoi(answer)) { //we have received the answer to this question so remove the user from wait answer loop
FD_CLR(i, &read_fds);
//printf("%s i(%d)\n","#######",i );
answerCheck(i ,answer, current_rtt, (int) difftime(time_ans,time_ques));
//printf("Answer(%c)\n",answer[0]);
}
else{//we have recvd something unexpectable so ignore for NOW
}
}
//time_t cccc = time(NULL);
//printf("%s I(%d)\n",ctime(&cccc),i);
}
}
}
//comparing answers
win_fd = compareAnswer();
//sending score
}
return 0;
}
Client.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define PORT "3490" //the port client will be connecting to
#define MAXDATASIZE 100 // max number of bytes we can get at once
//get sockaddr ,IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if(sa->sa_family ==AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
wrongRecv(ssize_t recvd, ssize_t expctd)
{
if(recvd != expctd)
{
printf("Recvd(%zd) bytes not equal to expected(%zd) bytes\n",recvd,expctd);
getchar();
}
}
void rtt_check(int sockfd)
{
ssize_t send_ret, recv_ret;
char rtt_check[1];
recv_ret = recv(sockfd, rtt_check, 1,0);
wrongRecv(recv_ret,1);
sleep(1);//to check
send_ret = send(sockfd, "r", 1, 0);
wrongRecv(send_ret, 1);
return;
}
int main(int argc, char *argv[])
{
int sockfd, numbytes;
char buf[MAXDATASIZE];
struct addrinfo hints, *servinfo, *p;
int rv;
char s[INET6_ADDRSTRLEN];
if(argc != 2) {
fprintf(stderr,"usage: client hostname\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
fprintf(stderr,"getaddrinfo: %s\n",gai_strerror(rv));
return 1;
}
//lopp through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
perror("client: socket");
continue;
}
if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1){
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if(p ==NULL) {
fprintf(stderr,"client: failed to connect\n");
return 2;
}
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof s);
printf("client : connecting to %s\n", s);
freeaddrinfo(servinfo); // all done with this structure
char login_det[17] = "abhishekabhishek";
char login_retMsg[7], login_stat[3], totalQuesMsg[5];
int totalQues;
//sending login details
ssize_t send_ret,recv_ret;
send_ret = send(sockfd, login_det,16,0);
wrongRecv(send_ret,16);
//receiving login status
recv_ret = recv(sockfd,login_retMsg,7,0);
wrongRecv(recv_ret,7);
strncpy(login_stat, login_retMsg, 2);
login_stat[2] = '\0';
printf("Login Status(%s)\n",login_stat);
strncpy(totalQuesMsg, login_retMsg + 2, 5);
totalQues = atoi(totalQuesMsg);
printf("totalQues(%d)\n",totalQues);
if(!strcmp(login_stat,"OK")) { //login ok
char quesId[5];
int maxQues_Len = 40, maxOpt_len = 10, maxQuesId_len = 5;//including '\0' this time
char quesMsg[80], scoreMsg[1];//score doesnot include \0
char ques[40], optA[10], optB[10], optC[10], optD[10];
char answer[6];
while(totalQues--) {
//checking rtt
rtt_check(sockfd);
//receving question
recv_ret = recv(sockfd, quesMsg, maxQues_Len + 4 * maxOpt_len + maxQuesId_len ,0);
wrongRecv(recv_ret, maxQues_Len + 4 * maxOpt_len + maxQuesId_len);
strncpy(quesId,quesMsg,5);
strncpy(ques, quesMsg + 05, 40);
strncpy(optA, quesMsg + 45, 10);
strncpy(optB, quesMsg + 55, 10);
strncpy(optC, quesMsg + 65, 10);
strncpy(optD, quesMsg + 75, 10);
printf("QUESID(%s) Question(%s), A(%s) , B(%s) , C(%s) , D(%s)\n", quesId, ques, optA, optB, optC, optD);
//choose answer
scoreMsg[0] = 'B';
strncpy(answer,quesId, 5);
answer[5] = scoreMsg[0];
sleep(5);
//sending answer
send_ret = send(sockfd, answer,6,0);
wrongRecv(send_ret,6);
printf("%s\n","Answer Message Sent" );
// if((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
// perror("recv");
// exit(1);
// }
// buf[numbytes] = '\0';
// printf("client: received '%s'\n",buf);
}
}
//TODO wrong login
close(sockfd);
return 0;
}
The problem is that the call to select in the answer getting loop is modifying read_fds to hold just the file descriptor of the first client(s) to respond. Since you don't reset read_fds before calling select again, it will not recognize the other clients' response.