Related
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.
I'm building an interactive web page so that a user can type a Bash command into the HTML page, click run and the output will display on the same page.
As of now my server can run commands if they are put into the search bar, but it doesn't work for piping or flags, just basic commands.
I'm looking for help understanding why pexec.c below provides no output, only a blank page.
**wrc-server.c**
/*
** server.c -- a stream socket server demo
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.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 <poll.h>
#define PORT "3838" // The port users will be connecting to
#define BACKLOG 10 // How many pending connections queue will hold
void sigchld_handler(int s)
{
(void)s; // Quiet unused variable warning
// waitpid() might overwrite errno, so we save and restore it:
int saved_errno = errno;
while(waitpid(-1, NULL, WNOHANG) > 0);
errno = saved_errno;
}
// 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);
}
char *parse(char *command) {
char * newCommand = (char *) malloc(sizeof(char)*35);
newCommand = strtok(command, " ");
newCommand = strtok(NULL, "/ ");
return newCommand;
}
char * execution(char *command) {
//printf("yo:%s\n", command);
int piper[2];
size_t len = 0;
pipe(piper);
char* output = malloc(1000 * sizeof(char));
memset(output, '\0', 1000* sizeof(char));
pid_t pid = fork();
if(pid != 0) // Parent
{
wait(NULL);
close(piper[1]);
int n = sizeof(output);
// ----Rob Code
struct pollfd * poll_fd = malloc(sizeof(struct pollfd));
poll_fd->fd = piper[0];
poll_fd->events = POLLIN;
//wait(NULL);
//printf("done\n");
//printf("AAA %s", output);
if (poll(poll_fd, 1, 0) == 1) { // Pipe data check
read(piper[0], output, 1000);
}
//printf("the command is %s\n", output);
//read(&output, output, piper[0]);
//printf("%s\n", piper[0]);
//dup2(piper[1], 1);
//close(0)
}
else {
//dup2(piper[1], 1);
//printf("run: %s", command);
close(1);
dup(piper[1]);
//close(0);
execlp(command, command, NULL);
exit(1);
}
// dup2 execute and print it out in parent
// if (*(commands+1) != NULL) // Redirect stdout as long as were not at the last row
// {
// dup2(piper[1], 1);
// close(piper[0]);
// }
return output;
}
int main(void)
{
int sockfd, new_fd; // Listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // Connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP
if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// Loop through all the results and bind 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("server: socket");
continue;
}
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
close(sockfd);
perror("server: bind");
continue;
}
break;
}
//printf("%s\n", servinfo.ai_canonname);
freeaddrinfo(servinfo); // All done with this structure
if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
exit(1);
}
if (listen(sockfd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
printf("server: waiting for connections...\n");
int ptds[2];
char *commands = malloc(sizeof(char)*1212);
while(1) { // Main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1) {
perror("accept");
continue;
}
inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s);
char *buf = malloc(1000*sizeof(char));
memset(buf, '\0', 1000* sizeof(char));
recv(new_fd, buf, 1000*sizeof(char), 0);
//printf("%s\n", parse(buf));
;
if (!fork()) { // This is the child process
close(sockfd); // Child doesn't need the listener
if (send(new_fd, execution(parse(buf)), 1000, 0) == -1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd);
}
return 0;
}
**pexec.c**
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
static char* args[512];
pid_t pid;
int command_pipe[2];
#define READ 0
#define WRITE 1
static int command(int input, int first, int last)
{
int pipettes[2];
pipe(pipettes);
pid = fork();
if (pid == 0) {
if (first == 1 && last == 0 && input == 0) {
// First command
dup2(pipettes[WRITE], STDOUT_FILENO);
} else if (first == 0 && last == 0 && input != 0) {
// Middle command
dup2(input, STDIN_FILENO);
dup2(pipettes[WRITE], STDOUT_FILENO);
} else {
// Last command
dup2(input, STDIN_FILENO);
}
if (execvp( args[0], args) == -1)
_exit(EXIT_FAILURE);
}
if (input != 0)
close(input);
close(pipettes[WRITE]);
if (last == 1)
close(pipettes[READ]);
return pipettes[READ];
}
static void cleanup(int n)
{
int i;
for (i = 0; i < n; ++i)
wait(NULL);
}
static int run(char* cmd, int input, int first, int last);
static char line[1024];
static int n = 0; /* Number of calls to 'command' */
int main(int argc, char *argv[])
{
while (1) {
// for(int i = 1; i < argc; i++) {
// strcat(line, argv[i]);
// strcat(line, " ");
// }
/* Read a command line */
//if (!fgets(line, 1024, stdin))
//return 0;
int InputLength = atoi(getenv("INPUT_LENGTH"));
fread(line, InputLength, 1, stdin);
int input = 0;
int first = 1;
char* cmd = line;
char* next = strchr(cmd, '#'); /* Find first '|' */
while (next != NULL) {
/* 'next' points to '|' */
*next = '\0';
input = run(cmd, input, first, 0);
cmd = next + 1;
next = strchr(cmd, '#'); /* Find next '|' */
first = 0;
}
input = run(cmd, input, first, 1);
cleanup(n);
n = 0;
}
return 0;
}
static void split(char* cmd);
static int run(char* cmd, int input, int first, int last)
{
split(cmd);
if (args[0] != NULL) {
if (strcmp(args[0], "exit") == 0)
exit(0);
n += 1;
return command(input, first, last);
}
return 0;
}
static char* skipwhite(char* s)
{
while (isspace(*s))
++s;
return s;
}
static void split(char* cmd)
{
cmd = skipwhite(cmd);
char* next = strchr(cmd, ' ');
int i = 0;
while (next != NULL) {
next[0] = '\0';
args[i] = cmd;
++i;
cmd = skipwhite(next + 1);
next = strchr(cmd, ' ');
}
if (cmd[0] != '\0') {
args[i] = cmd;
next = strchr(cmd, '\n');
next[0] = '\0';
++i;
}
args[i] = NULL;
}
I have a client and server, the server is setup this way:
int listenS = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
bind(listenS, (struct sockaddr*)&s, sizeof(s));
listen(listenS, QUEUE_LEN);
struct sockaddr_in clientIn;
int clientInSize = sizeof clientIn;
while (1)
{
int newfd = accept(listenS, (struct sockaddr*)&clientIn, (socklen_t*)&clientInSize);
//......
(There are tests I just removed them to make the code more readable)
The client is just:
int sock = socket(AF_INET, SOCK_STREAM, 0), nrecv;
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
if (connect(sock, (struct sockaddr*)&s, sizeof(s)) < 0)
{ //......
I get a connection, and everything is working great, the server recv a message the first time I send it from the client, but when I try to send another message to the server the server wont block the recv call and get nothing (returning the buffer size, not 0)
Here is the client code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.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 <fcntl.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#define PORT 0x0da2
#define IP_ADDR 0x7f000001
#define MAX_BUFFER_SIZE 1024
int send_all(int socket, void* buffer, size_t length)
{
char *ptr = (char*)buffer;
while (length > 0)
{
int i = send(socket, ptr, length, 0);
if (i < 1) return -1;
ptr += i;
length -= i;
}
return 0;
}
int main(int argc, char** argv)
{
if (argc > 1)
{
if ((strcmp(argv[1], "list-files") != 0) &&
(strcmp(argv[1], "upload-file") != 0) &&
(strcmp(argv[1], "download-file") != 0) &&
(strcmp(argv[1], "search") != 0))
{
perror("The arguments are incorrect.");
}
int sock = socket(AF_INET, SOCK_STREAM, 0), nrecv;
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
if (connect(sock, (struct sockaddr*)&s, sizeof(s)) < 0)
{
perror("connect");
return 1;
}
printf("Successfully connected.\n");
char sendBuffer[MAX_BUFFER_SIZE];
int lenOfArgv = strlen(argv[1]);
int sendBufferIndex = 0;
for (int i = 0;
i < lenOfArgv && sendBufferIndex < MAX_BUFFER_SIZE;
i++, sendBufferIndex++)
{
sendBuffer[sendBufferIndex] = argv[1][i];
}
if (argc == 3)
{
sendBuffer[sendBufferIndex++] = ' ';
int lenOfArgv = strlen(argv[2]);
for (int i = 0;
i < lenOfArgv && sendBufferIndex < MAX_BUFFER_SIZE;
i++, sendBufferIndex++)
{
sendBuffer[sendBufferIndex] = argv[2][i];
}
}
sendBuffer[sendBufferIndex] = 0;
// + 1 for terminating null
if (send_all(sock, sendBuffer, strlen(sendBuffer) + 1) < 0)
{
perror("send buffer to server failed");
return 1;
}
if(strcmp(argv[1], "download-file") == 0)
{
char sizeBuffer[256];
recv(sock, sizeBuffer, 256, 0);
int fileSize = atoi(sizeBuffer);
if(fileSize > 0)
{
FILE* recievedFile = fopen(argv[2], "w");
if(recievedFile != NULL)
{
int remainData = fileSize;
size_t len;
char fileBuffer[MAX_BUFFER_SIZE];
while(((len = recv(sock, fileBuffer, MAX_BUFFER_SIZE, 0)) > 0 && (remainData > 0)))
{
fwrite(fileBuffer, sizeof(char), len, recievedFile);
remainData -= len;
printf("Received %d bytes, %d is left..\n", len, remainData);
}
fclose(recievedFile);
printf("File downloaded!\n");
}
else
{
perror("Failed to download file\n");
}
}
}
else if(strcmp(argv[1], "upload-file") == 0)
{
char filePath[MAX_BUFFER_SIZE];
sprintf(filePath, "%s", argv[2]);
int fd = open(filePath, O_RDONLY);
int downloadFailed = 0;
if (fd != -1)
{
struct stat file_stat;
if(fstat(fd, &file_stat) >= 0)
{
char fileSize[256];
sprintf(fileSize, "%d", (int)file_stat.st_size);
int len = send(sock, fileSize, sizeof(fileSize), 0);
if(len >= 0)
{
int remainData = file_stat.st_size;
off_t offset = 0;
int sent_bytes = 0;
while(((sent_bytes = sendfile(sock, fd, &offset, MAX_BUFFER_SIZE)) > 0) && (remainData > 0))
{
remainData -= sent_bytes;
printf("sent %d bytes, %d is left...\n", sent_bytes, remainData);
}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
if(downloadFailed == 1)
{
perror("Failed to download file!\n");
}
}
else
{
char someBuffer[MAX_BUFFER_SIZE];
// nrecv is the number of bytes that we recieved
if ((nrecv = recv(sock, someBuffer, MAX_BUFFER_SIZE, 0)) < 0)
{
perror("recv");
return 1;
}
printf("%s\n", someBuffer);
}
close(sock);
return 0;
}
else
{
perror("The arguments are incorrect.");
}
}
here is the server code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.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 <math.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#define PORT 0x0da2 // 3490
#define IP_ADDR 0x7f000001 // 127.0.0.1
#define QUEUE_LEN 20
#define MAX_BUFFER_SIZE 1024
int send_all(int socket, void* buffer, int length)
{
char *ptr = (char*)buffer;
while (length > 0)
{
int i = send(socket, ptr, length, 0);
if (i < 1) return -1;
ptr += i;
length -= i;
}
return 0;
}
void list_dir()
{
DIR * directory;
struct dirent* dir;
directory = opendir(".");
if (directory)
{
while ((dir = readdir(directory)) != NULL)
{
printf("%s\n", dir->d_name); // /home/text.txt, text.txt
// get filesize (in bytes0 with dir->d_name
}
}
}
void list_files(char* buffer, int withBytes = 0)
{
DIR* d;
struct dirent* dir;
d = opendir("data");
int bufferIndex = 0;
while((dir = readdir(d)) != NULL)
{
char tempFilename[256] = "data/";
int tempIndex = 5;
char* scan = dir->d_name;
while(*scan)
{
tempFilename[tempIndex++] = *scan;
buffer[bufferIndex++] = *scan++;
}
tempFilename[tempIndex] = 0;
struct stat st = {0};
stat(tempFilename, &st);
int fileSize = st.st_size;
if(withBytes == 1)
{
// Adding file size to the buffer
bufferIndex += sprintf(&buffer[bufferIndex], " %d bytes", fileSize);
}
buffer[bufferIndex++] = '\n';
}
buffer[bufferIndex] = 0;
closedir(d);
}
int main(void)
{
int listenS = socket(AF_INET, SOCK_STREAM, 0);
if (listenS < 0)
{
perror("socket");
return 1;
}
struct sockaddr_in s = { 0 };
s.sin_family = AF_INET;
s.sin_port = htons(PORT);
s.sin_addr.s_addr = htonl(IP_ADDR);
if (bind(listenS, (struct sockaddr*)&s, sizeof(s)) < 0)
{
perror("bind");
return 1;
}
if (listen(listenS, QUEUE_LEN) < 0)
{
perror("listen");
return 1;
}
struct sockaddr_in clientIn;
int clientInSize = sizeof clientIn;
struct stat st = {0};
if(stat("data", &st) == -1)
{
mkdir("data", 0700);
}
while (1)
{
int newfd = accept(listenS, (struct sockaddr*)&clientIn, (socklen_t*)&clientInSize);
if (newfd < 0)
{
perror("accept");
return 1;
}
int pid = fork(); // creating new thread
if (pid == 0)
{
close(listenS); // duplicate=> thats why we need to close the socket
char someBuffer[MAX_BUFFER_SIZE];
int nrecv;
if ((nrecv = recv(newfd, someBuffer, MAX_BUFFER_SIZE, 0)) < 0)
{
perror("recv");
return 1;
}
printf("Message recieved: %s\n", someBuffer);
// Here we read the command the argument and split them
// into seperate variables
char command[256];
char argument[256];
int commandHasBeenSet = 0;
char* token = strtok(someBuffer, " ");
while(token != NULL)
{
if(commandHasBeenSet == 0)
{
strcpy(command, token);
commandHasBeenSet = 1;
}
else
{
strcpy(argument, token);
}
token = strtok(NULL, " ");
}
if (strcmp(command, "list-files") == 0)
{
char buffer[MAX_BUFFER_SIZE];
list_files(buffer, 1);
if (send_all(newfd, buffer, strlen(buffer) + 1) < 0)
{
perror("send buffer to client failed");
return 1;
}
printf("Sent a message to a client!\n");
}
else if (strcmp(command, "upload-file") == 0)
{
printf("Uploading file %s\n", argument);
char sizeBuffer[256];
recv(newfd, sizeBuffer, 256, 0);
int fileSize = atoi(sizeBuffer);
if(fileSize > 0)
{
char filePath[MAX_BUFFER_SIZE];
sprintf(filePath, "data/%s", argument);
printf("Downloading to %s", filePath);
FILE* recievedFile = fopen(filePath, "w");
if(recievedFile != NULL)
{
int remainData = fileSize;
size_t len;
char fileBuffer[MAX_BUFFER_SIZE];
while(((len = recv(newfd, fileBuffer, MAX_BUFFER_SIZE, 0)) > 0 && (remainData > 0)))
{
fwrite(fileBuffer, sizeof(char), len, recievedFile);
remainData -= len;
printf("Received %d bytes, %d is left..\n", len, remainData);
}
fclose(recievedFile);
printf("File downloaded!\n");
}
else
{
perror("Failed to download file\n");
}
}else
{
perror("Failed to get file size for download\n");
}
}
else if (strcmp(command, "download-file") == 0)
{
char filePath[MAX_BUFFER_SIZE];
sprintf(filePath, "data/%s", argument);
int fd = open(filePath, O_RDONLY);
int downloadFailed = 0;
if (fd != -1)
{
struct stat file_stat;
if(fstat(fd, &file_stat) >= 0)
{
char fileSize[256];
sprintf(fileSize, "%d", (int)file_stat.st_size);
int len = send(newfd, fileSize, sizeof(fileSize), 0);
if(len >= 0)
{
int remainData = file_stat.st_size;
off_t offset = 0;
int sent_bytes = 0;
while(((sent_bytes = sendfile(newfd, fd, &offset, MAX_BUFFER_SIZE)) > 0) && (remainData > 0))
{
remainData -= sent_bytes;
printf("Server sent %d bytes, %d is left...\n", sent_bytes, remainData);
}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
}else {downloadFailed = 1;}
if(downloadFailed == 1)
{
perror("Failed to download file!\n");
}
}
else if (strcmp(command, "search") == 0)
{
char buffer[MAX_BUFFER_SIZE];
char result[MAX_BUFFER_SIZE];
int resultIndex = 0;
list_files(buffer);
result[0] = 0;
char tempBuffer[MAX_BUFFER_SIZE];
strcpy(tempBuffer, buffer);
token = strtok(tempBuffer, "\n");
while(token != NULL)
{
char* scanToken = token;
char* scanArgument = argument;
int found = 1;
while(*scanToken && *scanArgument)
{
if(*scanToken++ != *scanArgument++)
{
found = 0;
break;
}
}
if(found == 1)
{
if(resultIndex > 0)
{
result[resultIndex++] = ' ';
}
strcpy(&result[resultIndex], token);
resultIndex += strlen(token);
result[resultIndex] = 0;
}
token = strtok(NULL, "\n");
}
if (send_all(newfd, result, strlen(result) + 1) < 0)
{
perror("send buffer to client failed");
return 1;
}
printf("Sent a message to a client!\n");
}
close(newfd);
exit(0);
}
else
close(newfd);
}
close(listenS);
return 0;
}
If you run the server, and then run the client with commands like:
./client list-files
./client download-file test.txt
it will work fine, the client will receive messages from the server and vice versa.
The problem occurs when I try to run:
./client upload-file test.txt
which is essentially the same as download-file command, just copied and pasted to the server from the client (same logic, should work the same), except it doesn't.
Specifically the program fail at line 175 of the server (recv(newfd, sizeBuffer, 256, 0);), it gets 0 instead of the value the client is sending it.
Any idea what I am missing?
(I tried searching online but didn't find anything)
TCP is a streaming protocol. There is no message boundaries, and server's recvs do not correspond to client's sends.
The client sends the command with
send_all(sock, sendBuffer, strlen(sendBuffer) + 1)
OTOH, the server tries to receive it with
nrecv = recv(newfd, someBuffer, MAX_BUFFER_SIZE, 0))
recv does not care whether the stream contains a '\0' or not. It blindly waits for MAX_BUFFER_SIZE bytes to come in. Some (valuable) data sent by the client is in someBuffer just past the command, but ignored by the server.
The server must parse the reply more diligently. For that you likely need a more elaborated protocol (e.g. prefix each string with its length).
You are assuming the stream socket will preserve your message boundaries. It will not. So, you're sending something like:
upload-file filename\0
NNN\0\0\0...\0 [256 bytes containing filesize]
<content of filename>
So probably the first recv you do on the server side (of MAX_BUFFER_SIZE bytes) is receiving not just the command string and the filename, but also the file size block and the entire file content. That is, you've already received it all and it's sitting in someBuffer. Your initial command string is null-terminated so you would not be aware of the rest of it unless you check nrecv. Hence, your next recv call gets end-of-file because the client is finished and has closed its end of the connection.
To send defined records over TCP, you need to know exactly how many bytes are expected at each point, and receive exactly those (or be prepared to parse the received data). See also https://stackoverflow.com/a/47440054/1076479
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.