IPC message queue - c

I am creating two apps (client and server) in c to communicate via IPC message queues.
The server have to operate for many clients using only one queue. Clients' ids have to be provided for the server as the Command Line arguments, as well as the id for each client. Clients are recognized by a mesg_type.
I have a problem to receive messages from all clients - I can receive the message a few times from client A, then a few times from client B, a few times from client A and so on. I think that the problem is in the following part of the code:
int status;
key_t key;
int msgid;
for(int i=0;i<argc-1;i++){
clients_ids[i]=atoi(argv[i+1]);
}
key = ftok(".", 50);
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid == -1) {
fprintf(stderr, "msgget failed: %d\n", errno);
exit(EXIT_FAILURE);
}
do{
for (int k=0;k<=argc-2;k++){
status = msgrcv(msgid, &message, sizeof(message), clients_ids[k], 0);
if(status != -1){
type = message.mesg_type;
strcpy(mesg, message.mesg_text);
}
}
printf("Message received (from id: %d): %s ",type, mesg);
Can you please advise me what should I change?

move the call to printf() to inside the for (int k=0;k<=argc-2;k++){ loop. then all messages will be displayed.

Related

read socket raise errno 9 randomly

I have a client/server setup communicating over tcp sockets.
Functional wise works very nice except i got an errno 9 , randomly but quite often, when reading the socket on the server side.
According to docs and discussions i could find, errno is raised when read operation is done for a socket which is locally closed.
I'm pretty sure i'm not closing the socket in the reading loop on server side. The socket is closed only by client after message is sent.
Here is the reading loop on server side
void *client_listener_thread(void *args)
{
struct th_params *param = (void *) args;
int sockfd, n, client_pos;
message_t messageR;
char logbuf[256], buf[256];
sockfd = param->socket;
while(1)
{
n = read(sockfd, &messageR, sizeof(message_t));
if (n < 0)
{
sprintf(logbuf, "cmd_thread: ERROR reading from socket errno=%d sock=%d thread=%x", errno, sockfd, (uint32_t)(param->client_listener));
logwts(logbuf);
break;
}
else if(n == 0)
{
sprintf(logbuf, "cmd_thread: socket closed by remote peer 2 %x", (uint32_t)(param->client_listener));
logwts(logbuf);
break;
}
else
{
inet_ntop(AF_INET, &messageR.header.sender_ip, buf, 255);
sprintf(logbuf, "cmd_thread:URC Message: ID = %d sender = %s\n", messageR.header.messageID, buf);
logwts(logbuf);
// process message
switch(messageR.header.messageID)
{
case IDENT_ACK:
...
sprintf(logbuf, "New client registered on socket %d %d / %s:%d", sockfd, client_pos, buf, messageR.header.sender_port);
logwts(logbuf);
break;
...
default:
sprintf(logbuf, "cmd_thread:unprocessed message %d", messageR.header.messageID);
logwts(logbuf);
break;
}
}
}
if(sockfd)
close(sockfd);
pthread_exit(NULL);
}
On client side, it is just open the socket, send the message, close the socket.
When running, on server side, i got "New client registered ..." message for the first read and on the second read i got either the expected "socket closed by remote peer..." message or unexpected "ERROR reading from socket.." with errno = 9
As i said functional has no problem, no message lost, the message is received and processed. I could ignore the error but i'm tring to understand it first.
Thx to strace could find the culprit.
A race condition between threads. One thread was closing the socket while the other thread was still using it. A mutex did the job.
Completely true #Sinic's comment
"... that the descriptor (in this case 5) is getting closed elsewhere..."

C Socket Programming: Trying to serve new client connections using fork()

So each client connection is to be served on a new child process.
Right now, I have a function generate_client() that creates a client and gives it a random id number (that is returned to client).
client_t generate_client()
{
client_t *client = malloc(sizeof(client_t));
client->clientID = randomClientIdGenerator(); < ----
client->entryIndexConstant = 0;
client->messageQueueIndex = 0;
client->readMsg = 0;
client->totalMessageSent = 0;
client->unReadMsg = 0;
client->status = CLIENT_INACTIVE;
return *client;
}
int randomClientIdGenerator()
{
int num = rand() % MAX_CLIENTS;
return num;
}
PROBLEM: For each connection using fork(), the child process is copied over from parent and as you can see in the implementation below the client object with the same client id is copied over to the child process (at least this is what I think is happening).
For example: connecting to server using terminal 1 generates client id 83, and terminal 2 connection also sends id 83.
/* bind the socket to the end point */
if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)
{
perror("bind");
exit(1);
}
/* start listnening */
if (listen(sockfd, BACKLOG) == -1)
{
perror("listen");
exit(1);
}
while (1)
{
sin_size = sizeof(struct sockaddr_in);
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -1)
{
perror("accept.");
printf("\n...error: accept new_fd failed\n");
// continue;
}
printf("server: got connection from %s\n",
inet_ntoa(their_addr.sin_addr));
if (!fork())
{ /* this is the child process */
printf("\n-----------------------CHILD START ----------\n");
printf("\n child process id is %d. parent id is: %d\n", getpid(), getppid());
/* ***Server-Client Connected*** */
client_t client = generate_client();
printf("\n =>client id %d STATUS: %d\n", client.clientID, client.status);
if (client.clientID < -1)
{
perror("SERVER: failed to create client object (Max. 100 clients allowed)");
printf("SERVER: failed to create client object (Max. 100 clients allowed) \n");
exit(1);
// send response to client Cant accept connection
}
// Send: Welcome Message. ------------> SAME id of 83 is given to child process!!!
if (send(new_fd, &client.clientID, sizeof(int), 0) == -1)
{
perror("send");
printf("Error: Welcome message not sent to client \n");
}
}
}
I think the problem is with client_t client = generate_client(); inside fork().. which generates the client that is copied over from parent process, how do I re-call this in each process maybe?
This seems to be identical of a question posted just few hours ago:
Trying to fork() after new client connection to server [Socket Programming C]
Short answer:
The 'rand' function uses a hidden 'state' to generate the next random number. Since the parent never uses rand, each forked child will get the same state, and will generate the same sequence of random number.
Few possible fixes:
Make one call to rand in the parent (BEFORE forking). This will result in each child starting with different state.
Call rand in the parent, before the fork, and save the id for the child to use.
Setup random see for each child, using srand.

Message queue passing old values C

I am trying to develop a simple message queue in C but I am running towards some frustrating problems. I have a number of 10 processes that are divided in two groups of five. Each of the processes of this group must speak with only one of the processes of the other group, and to do that I have assigned different keys to the message queue creation (sorry if it is not the best idea but i'm new to C and trying to adapt). My main issue though is that when I run the code the first time if tells me that 'No such file or directory exists" as a mq error. When I run it from this point on the server receives the message just fine, but it receives the 'old' messages, which are the ones that the client has sent the prior execution. I've been trying to look for what to change but haven't come up with anything. This is my code:
server.c
int keys[] = {1111, 1112, 1113, 1114, 1115};
int msqid;
key_t keymq1;
struct msgbuf rcvbuffer;
keymq1 = keys[indiceProcesso];
if ((msqid = msgget(keymq1, 0666)) < 0)
die("msgget()");
if (msgrcv(msqid, &rcvbuffer, MAXSIZE, 1, 0) < 0)
die("msgrcv");
printf("Messaggio ricevuto da A con pid %d: %s\n",getpid(), rcvbuffer.mtext);
client.c
int msqid1;
int msgflg = IPC_CREAT | 0666;
key_t keymq1;
struct msgbuf sbuf;
size_t buflen;
keymq1 = keys[indiceProcesso];
sbuf.mtype = 1;
if ((msqid1 = msgget(keymq1, msgflg )) < 0)
die("msgget");
sbuf.mtype = 1;
strcpy(sbuf.mtext,"My message");
buflen = strlen(sbuf.mtext) + 1 ;
if (msgsnd(msqid1, &sbuf, buflen, IPC_NOWAIT) < 0)
{
printf ("%d, %ld, %s, %zu\n", msqid1, sbuf.mtype, sbuf.mtext, buflen);
die("msgsnd");
}
else
printf("Message Sent\n");
Hope you can help me out and thank you for your time.

ChatRoom Service between Clients in C - TCP

Well to get started, what i'm trying to do is a multi client chat service.
I have read thousands of posts related to it but most of them are implemented with threads and nothing helps me, and i need to do it using FORKS.
I have my server that supports connections of multiple clients. Every time that a client request connection the server does the following:
Sets the shared variables that are needed.
Get the proper socket to handle the connection,
When a client connects, saves data of the client in a array of clients implemented with a struct,
Forks a process to handle this connection,
Goes back and blocks in the accept() function waiting another client.
The fork does the following:
Waits commands that the user sends,
Completes the request,
Waits for another command.
The fork works with a shared array of clients protected by a semaphore. This was done (by the father process) with shm_open, mmap and shm_open, to get the array shared among the child processes.
For now, the only 3 options are:
clientlist : to see the list of connected clients,
sendchat : to send a message to a desired client,
quit_ : to quit the programm and disconnects from the server.
Saying that, the problem is that i can't in any way to notice a client that a message is ready for him. The flow of the execution is:
Client C1 connects, Client C2 connects.
C1 whants to send a message to C2, C1 tells his process that he wants to talk to C2.
The process handling the connection of C1, search in the shared array the name of C2, and then writes the message sent by C1 in the buffer of C2.
And here is where i'm get stuck..i don't know how to make that C2 notice that is a new message for im.
I know this is long for anyone to care, but if you can, I'll be glad to get some help, please.
Below are the client, and server scripts.
Note: server.c compile with -lptrhead and -lrt for binding the shared memory library.
Note: the server gets correctly the socket from the function get_tcp_listen as you will see, no need to worry about this.
How should i approach this problem? Thanks !.
client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include "socketlib.h" // FOR BINDINGS, GET SOCKET, AND STUFF
#define SERVER_PORT "50000"
#define CLIENT_PORT "50001"
#define MAXDATASIZE 256
#define MAXTIMESIZE 30
#define NICKSIZE 25
#define MAXCLIENT 10
#define MAXMSG 1024
typedef struct{
char nick[NICKSIZE]; // NICK
char ip[NI_MAXHOST]; // IP
char port[NI_MAXSERV]; // PORT
char connTime[MAXTIMESIZE]; // TIMESTAMP
int connected; // STATE
pid_t pidConn; // PROCESS PID
char *msg; // MSG BUFFER
}connData;
int main (int argc, char **argv) {
// GET SOCKET
int sockfd;
if ((sockfd = get_tcp_connect(argv[1], SERVER_PORT, CLIENT_PORT)) == -1) {
fprintf(stderr, "Error client: client_connect\n");
exit(1);}
///////////////////////////////////////////////////////////////////////
time_t ltime;
ltime = time(NULL);
char timeStamp[MAXTIMESIZE];
strcpy(timeStamp,ctime(&ltime));
printf("\n%s\n", timeStamp);
// GET MY IP : PORT
char ip_num[NI_MAXHOST];
char port_num[NI_MAXSERV];
get_socket_addr(sockfd, ip_num, port_num);
get_peer_addr(sockfd, ip_num, port_num);
///////////////////////////////////////////////////////////////////////
// WELLCOME MSG FROM SERVER
char *well = (char*) malloc(MAXDATASIZE); int numbytes;
if ((numbytes = recv(sockfd, well, MAXDATASIZE-1, 0)) == -1) {
fprintf(stderr, "Error client: recv WELLCOME\n");
exit(1);
}
well[numbytes] = '\0'; printf("%s\n", well); free(well);
//////////////////////////////////////////////////////////////////////
// SEND NICK TO SERVER
char nick[NICKSIZE];
printf("\nEnter your NickName (25 chars): "); scanf("%s",nick);
if(send(sockfd, nick, NICKSIZE, 0) == -1){
fprintf(stderr,"Error client: send NICK\n");
exit(1);
}
///////////////////////////////////////////////////////////////////////
// GET CONNECTED USERS LIST FROM SERVER
int cantClients = 0; // FIRST: QUANTITY OF USERS
if (recv(sockfd, &cantClients, sizeof(int), 0) == -1) {
fprintf(stderr, "Error client: recv CANT CLIENTs\n");
exit(1);
}
connData *tmpCl = (connData *) malloc(sizeof(connData)*MAXCLIENT);
if (recv(sockfd, tmpCl, sizeof(connData)*MAXCLIENT, 0) == -1) {
fprintf(stderr, "Error client: recv ARRAY CLIENTS\n");
exit(1);
}
printf("\n****\tConnected Users\t****\n");
int i;
for(i = 0; i < cantClients; i++){
if(tmpCl[i].connected == 1){
printf("\nNick: %s\n", tmpCl[i].nick);
printf("IP: %s\n", tmpCl[i].ip);
printf("PORT: %s\n", tmpCl[i].port);
printf("Time: %s", tmpCl[i].connTime);
printf("Connected: %d\n", tmpCl[i].connected);
printf("PID: %d\n", tmpCl[i].pidConn);
printf("**********************************\n");
}
} free(tmpCl);
///////////////////////////////////////////////////////////////////////
// THE CLIENT PROCESS WAITS UNTIL THE USER TYPES A COMMAND
char *comm = (char*)malloc(MAXDATASIZE);
printf("\nEnter one option: ");
printf("\n\t-> clientlist TO SEE THE LIST OF CONNECTED CLIENTS\n");
printf("\t-> sendchat TO SEND A MESSAGE\n");
printf("\t-> quit_ TO QUIT CHAT\n>> ");
scanf("%s",comm);
int exitvar = 0;
while(exitvar == 0){
// PARA TRAER DATOS DEL SERVIDOR, ENVIO EL COMANDO, Y ME QUEDO ESPERANDO
if(send(sockfd, comm, MAXDATASIZE-1, 0) == -1){
fprintf(stderr,"Error client: send\n");
exit(1);
}
if(strcmp(comm,"clientlist") == 0){
// GET CONNECTED USERS LIST FROM SERVER
connData *tmpCl = (connData *) malloc(sizeof(connData)*MAXCLIENT);
if (recv(sockfd, tmpCl, sizeof(connData)*MAXCLIENT, 0) == -1) {
fprintf(stderr, "Error client: recv ARRAY CLIENT\n");
exit(1);
}
printf("\n****\tConnected Users\t****\n"); int i;
cantClients = (unsigned) sizeof(*tmpCl) / (unsigned) sizeof(connData);
for(i = 0; i < MAXCLIENT; i++){
if(tmpCl[i].connected == 1){
printf("\nNick: %s\n", tmpCl[i].nick);
printf("IP: %s\n", tmpCl[i].ip);
printf("PORT: %s\n", tmpCl[i].port);
printf("Time: %s", tmpCl[i].connTime);
printf("Connected: %d\n", tmpCl[i].connected);
printf("PID: %d\n", tmpCl[i].pidConn);
printf("**********************************\n");
}
} free(tmpCl);
}else if(strcmp(comm,"sendchat") == 0){
printf("To whom you want to talk?... ");
char *chatNick = (char *) malloc(NICKSIZE);
fgets(chatNick, NICKSIZE, stdin);
fgets(chatNick, NICKSIZE, stdin);
if((strlen(chatNick)>0) && (chatNick[strlen(chatNick)-1] == '\n') ){
chatNick[strlen(chatNick)-1] = '\0';
}
if(send(sockfd, chatNick, NICKSIZE, 0) == -1){
fprintf(stderr, "Error client: send CHAT NICK\n");
}
printf("Type your message...\n");
char *chat_msg = (char *) malloc(MAXMSG);
fgets(chat_msg,MAXMSG,stdin) ;
if((strlen(chat_msg)>0) && (chat_msg[strlen(chat_msg)-1] == '\n') ){
chat_msg[strlen(chat_msg)-1] = '\0';
}
if(send(sockfd, chat_msg, MAXMSG, 0) == -1){
fprintf(stderr, "Error client: send CHAT\n");
}
free(chatNick);
free(chat_msg);
}else{
char *buf = (char*) malloc(MAXDATASIZE); int numbytes;
if ((numbytes = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
fprintf(stderr, "Error client: recv\n");
exit(1);
}
buf[numbytes] = '\0'; printf("-> %s\n", buf);
free(buf);
}
if(strcmp(comm, "quit_") != 0){
free(comm); comm = (char*)malloc(MAXDATASIZE);
printf("\nWhats next?... "); scanf("%s",comm);
}else{
close(sockfd);
exitvar = 1;
}
}
return 0;
}
server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include "socketlib.h"
#include <semaphore.h>
#define SERVER_PORT "50000"
#define BACKLOG 10
#define MAXDATASIZE 256
#define NICKSIZE 25
#define MAXCLIENT 10
#define MAXTIMESIZE 30
#define MAXMSG 1024
// ESTRUCTURA QUE MANEJARA LA LISTA DE CLIENTES
typedef struct{
char nick[NICKSIZE]; // NICK
char ip[NI_MAXHOST]; // IP
char port[NI_MAXSERV]; // PORT
char connTime[MAXTIMESIZE]; // TIMESTAMP
int connected; // STATE
pid_t pidConn; // PROCESS PID
char *msg; // MSG BUFFER
}connData;
// NOT ZOMBIE PROCESSES
void sigchld_handler(int s) {
while (waitpid(-1, NULL, WNOHANG) > 0);
}
connData *client;
int *id;
int main (int argc, char **argv) {
// THE ARRAY OF CLIENTS IS SHARED BETWEEN THE PROCESSES
int smid = shm_open("shm1", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ftruncate(smid, sizeof(connData)*MAXCLIENT);
// JUST FOR MAXCLIENT 10 CLIENTS AT THE MOMMENT
client = mmap(NULL, sizeof(connData)*MAXCLIENT, PROT_READ | PROT_WRITE, MAP_SHARED, smid, 0);
sem_t *sem; sem = sem_open("sem1", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, 1);
// THE ARRAY INDEX IS ALSO SHARED
int smid2 = shm_open("shm2", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
ftruncate(smid2, sizeof(int));
id = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, smid2, 0);
sem_t *sem2; sem2 = sem_open("sem2", O_CREAT | O_RDWR, S_IRUSR | S_IWUSR, 1);
sem_wait(sem2);
*id = 0;
sem_post(sem2);
// CONN CONFIG
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
fprintf(stderr, "Error server: sigaction\n");
exit(1);
}
int sockfd; // LISTENER
if ((sockfd = get_tcp_listen(SERVER_PORT, BACKLOG)) == -1) {
fprintf(stderr, "Error get_tcp_listen\n");
exit(1);
}
printf("server: waiting for connections...\n");
char ip_num[NI_MAXHOST];
char port_num[NI_MAXSERV];
get_socket_addr(sockfd, ip_num, port_num);
//////////////////////////////////////////////////////////////////
while (1) {
// BLOCKS UNTIL SOMEONE REQUEST CONN
int new_fd;
if ((new_fd = accept(sockfd, NULL, NULL)) == -1) {
fprintf(stderr, "Error server: accept\n");
continue;}
////////////////////////////////////////////////////////
// IP:PORT OF JUST CONNECTED USER
get_socket_addr(new_fd, ip_num, port_num);
get_peer_addr(new_fd, ip_num, port_num);
printf("server: got connection from: %s, %s\n", ip_num, port_num);
////////////////////////////////////////////////////////
// TIMESTAMP OF USER CONN
time_t ltime; ltime = time(NULL);
char timeStamp[MAXTIMESIZE]; strcpy(timeStamp,ctime(&ltime));
////////////////////////////////////////////////////////////////////////
// WELLCOME MESSAGE SENT TO THE CLIENT
char *well = (char*) malloc(MAXDATASIZE);
if (send(new_fd, "Wellcome to the Chat Service!!\n", MAXDATASIZE-1, 0) == -1) {
fprintf(stderr, "Error sending WELLCOME\n");
} free(well);
///////////////////////////////////////////////////////////////
// SAVES IN THE ARRAY OF CLIENTS, THE DATA OF THE CLIENT THAT JUST CONNECTED
int idTmp1;
sem_wait(sem2);
idTmp1 = *id;
sem_post(sem2);
if(sem_wait(sem) == 0){
strcpy(client[idTmp1].ip, ip_num); // IP
strcpy(client[idTmp1].port, port_num); // PORT
strcpy(client[idTmp1].connTime, timeStamp); // TIMESTAMP
client[idTmp1].connected = 1;
}else{
fprintf(stderr, "Error SEM_WAIT\n");
}
sem_post(sem);
sem_wait(sem2); (*id)++; sem_post(sem2);
//////////////////////////////////////////////////////////////
// FORKS A PROCESS TO DEAL WITH THE JUST CONNECTED USER
if (fork() == 0) {
close(sockfd); // CLOSES THE FATHERS SOCKET
int numbytes = 0;
// SAVES THE NICK IN THE ARRAY
char userNick[NICKSIZE];
if(( numbytes = recv(new_fd, userNick, NICKSIZE, 0)) == -1){
fprintf(stderr,"Error rcv\n");
} userNick[numbytes-1] = '\0';
int idTmp2;
sem_wait(sem2);
pid_t pidAct = getpid(); // PID OF THE NEW CREATED FORK
idTmp2 = *id; // ID OF THE USER
idTmp2--;
strcpy(client[idTmp2].nick,userNick);
client[idTmp2].pidConn = pidAct;
idTmp2 = *id;
sem_post(sem2);
//////////////////////////////////////////////////////////////
// SENDS THE LIST OF CONNECTED CLIENTES
if (send(new_fd, id, sizeof(int), 0) == -1) {
fprintf(stderr, "Error send ID\n");
}
if (send(new_fd, client, sizeof(connData)*MAXCLIENT, 0) == -1) { // SEND THE WHOLE LIST
fprintf(stderr, "Error send LIST\n");
}
//////////////////////////////////////////////////////////////
// THE FORK WAITS SOME COMMAND OF THE USER
char *comm = (char*)malloc(MAXDATASIZE);
if( (numbytes = recv(new_fd, comm, MAXDATASIZE-1, 0)) == -1){
fprintf(stderr,"Error rcv COMMAND\n");
}
comm[numbytes] = '\0';
// THE FORK ENTERS IN A LOOP WAITING COMMANDS
int wait = 0;
while(wait == 0){
if(strcmp(comm,"clientlist") == 0){
if (send(new_fd, client, sizeof(connData)*MAXCLIENT, 0) == -1) {
fprintf(stderr, "Error send CLIENT LIST\n");
}
}else if(strcmp(comm,"sendchat") == 0){
char *chatNick = (char *) malloc(NICKSIZE); // WAIT FOR THE CLIENT TO TALK TO
if( (numbytes = recv(new_fd,chatNick, NICKSIZE, 0)) == -1){
fprintf(stderr,"Error server rcv CHAT NICK\n");
} chatNick[numbytes-1] = '\0';
char *chatmsg = (char *)malloc(MAXMSG); // WAIT FOR MSG
if((numbytes = recv(new_fd, chatmsg, MAXMSG, 0)) == -1){
fprintf(stderr,"Error server rcv CHAT\n");
} chatmsg[numbytes-1] = '\0';
int client_id;
sem_wait(sem2);
for(client_id = 0; client_id < *id; client_id++){
if(strcmp(client[client_id].nick, chatNick) == 0){
if(client[client_id].msg != NULL){
free(client[client_id].msg);
}
client[client_id].msg = (char * )malloc(MAXMSG); // COPY THE MESSAGE TO THE DESIRED USER
strcpy(client[client_id].msg, chatmsg);
printf("\nTHE MESSAGE TO: %s IS %s\n", client[client_id].nick, client[client_id].msg);
}
}
sem_post(sem2);
/*
HERE I HAVE THE NICK, SAY, 'client1' OF THE CLIENT TO WHICH I WANT TO TALK.
THE MSG NOW ITS IN HIS MSG BUFFER LIKE ABOVE.
HOW CAN I NOTICE THE FORKED PROCESS HANDLING THE CONNECTION of 'client1'
TO READ THE MESSAGE ?
*/
free(chatmsg);
free(chatNick);
}else if(strcmp(comm,"quit_") == 0){
if (send(new_fd, "Byee!!", MAXDATASIZE-1, 0) == -1) {
fprintf(stderr, "Error send EXIT\n");
}
wait = 1; // FOR EXIT AND CLOSE THE SOCKET
}else{
if (send(new_fd, "Invalid option!", MAXDATASIZE-1, 0) == -1) {
fprintf(stderr, "Error send INVALID\n");
}
}
if(wait == 0){
// WHEN THE FORKED PROCESS HAS FULFILL THE USERS REQUEST, IT JUST WAITS FOR OTHER REQUEST
free(comm); comm = (char*)malloc(MAXDATASIZE);
if((numbytes = recv(new_fd, comm, MAXDATASIZE-1, 0)) == -1){
fprintf(stderr,"Error rcv REQUEST\n");
} comm[numbytes] = '\0';
}
}
if(munmap(client,sizeof(connData)*MAXCLIENT) != 0){ printf("ERROR FREEING MEM\n");}
sem_unlink("sem1"); shm_unlink("shm1");
printf("Connection ended with %d \n", new_fd);
close(new_fd); exit(0);
}
printf("Keep waiting connections.....\n");
close(new_fd); // SOCKET DEL ACCEPT, DEL CLIENTE QUE SE HABIA CONECTADO
//////////////////////////////////////////////////////////////////////////////
}
if(munmap(client,sizeof(connData)*MAXCLIENT) != 0){ printf("ERROR FREEING MEM\n");}
sem_unlink("sem1");
shm_unlink("shm1");
return 0;
}
I'll start by noting that fork() and shared memory are not the best or easiest approach to creating a chat server; if you have the option, I'd recommend doing it a different way (e.g. via multiple threads in a single process, or even a single thread and select(), instead).
Assuming that you are required to use this approach (e.g. because it's stipulated in a class assignment, or something), however... what you're missing is an IPC notification mechanism, i.e. (as you say) a way for process C2 to notice that process C1 has some data for C2 to handle.
There are a couple of ways to go about implementing notification... the quick-and-dirty way (which might be good enough for getting a class assignment done) is simply to have each process poll the shared memory area; e.g. have each process check its portion of the shared memory area every 100 milliseconds and see if anything has changed since last time. In a scenario like that, C1 might write some new data to the shared memory area, and then increment an integer value in the shared memory area when it's done writing. The next time C2 wakes up to check the integer, it can notice that the integer's value is different from what it was on the previous check, and take that as its cue that there is fresh data in the shared memory area for it to handle. (side note: when using shared memory you should be serializing access to the shared memory regions somehow, otherwise you risk encountering race conditions where e.g. C2 starts reading memory at the same time C1 is still writing it. The symptom of that would be a system that works correctly 99.999% of the time but occasionally does something weird/wrong when the timing is "just right")
If you want a less hackish method of notification, however (i.e. one that doesn't eat CPU cycles 24/7 and doesn't cause an unnecessary 100mS of latency on every trip through the server), then you'll need to choose a notification mechanism. One mechanism would be to use the (arguably misnamed) kill() system call to send a UNIX signal to C2's process; C2 will then need to have a signal handler installed that causes it to do the right thing when it receives a signal of that type.
If you want to avoid Unix signals, however (and I try to avoid them because they are rather antiquated and ugly), you'll want a gentler mechanism by which the C2 process can be awoken when either (an IPC notification is received) or (I/O data is received), whichever condition happens first... a naive blocking-I/O-call mechanism is insufficient since it only allows your process to wait for one thing or the other, but not both. A good way to get both is to use select() or poll() to monitor two sockets simultaneously: one socket is the TCP connection to the client, and the other socket is a UDP socket that the process has set up specifically to receive IPC notifications. Each forked process sets up a UDP socket listening on a particular port, and when C1 wants to wake up C2, C1 does so by send()-ing a UDP packet to C2's UDP port. (The contents of the UDP packet don't matter, since its only purpose is to cause C2 to return from select() and, since the UDP socket has selected as ready-for-read, C2 then knows to read the packet from the UDP socket, throw the packet away, and then checked the shared memory region for fresh data.
(Of course, once you've done all that, you might find it easier for C1 to simply include C2's data in the UDP packet itself so C2 doesn't have to muck about with the potentially-racy shared memory region, but that's up to you)
You are almost finish #Emiliano, that's the point I also got stuck once ;).
Well, you have some options to tell to other process about message.
1. You always look for your own message buffer (this is bad, consume much CPU and also a bad idea)
Process C1 looks always in shared memory for C1, its own, buffer and check if there is new message, and send to client.
2. You use signal ( better than 1)
a. Client C2 send a message for C1.
b. Process C2 store message in C1 buffer on shared memory.(If i correctly understand your shared memory structure)
c. Process C2 send a signal to C1 to notify that I have placed a message for you in your buffer.(In this case, you need to know which pid handles which client)
d. Upon getting signal from a process, C1 check its buffer and send to its client.
EDIT:
It seems you are having trouble in signal.
Here is a simple snippet which show signal sending/catching.
recvsig.c
static void handler(int sig, siginfo_t *si, void *data)
{
printf("%s = %d\n", "Got a signal, signal number is ", sig);
//you can also code here what you want, after getting a signal
}
void init_signal()
{
struct sigaction act;
act.sa_sigaction = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction(SIGRTMIN + 1, &act, NULL);
}
void main(int argc, char **argv)
{
printf("%s %d\n", "PID", getpid());
init_signal();
while(1)
{
pause();
{
printf("%s\n", "Received a signal");
//code here anything after you got signal
}
}
return;
}
sendsig.c
void main(int argc, char **argv)
{
int pid = atoi(argv[1]);
while(1)
{
sleep(5);
{
kill(pid, SIGRTMIN+1);
printf("%s %d\n", "Sent a signal to ", pid);
}
}
return;
}
In your program , call init_signal() after each forked - in child process.
Be sure you manage pid list of all forked process + connected clients.
And use kill() to signal the correct pid.

Boss Worker Pthreads Web Server in C - Server crashes if more requests sent than number of threads

I'm writing a web server in C (which I suck with) using Pthreads (which I suck with even more) and I'm stuck at this point. The model for the server is boss-worker so the boss thread instantiates all worker threads at the beginning of the program. There is a global queue that stores the socket of the incoming connection(s). The boss thread is the one that adds all items (sockets) to the queue as the connections are accepted. All of the worker threads then wait for an item to be added to a global queue in order for them to take up the processing.
The server works fine as long as I connect to it less times than the number of worker threads that the server has. Because of that, I think that either something is wrong with my mutexes (maybe the signals are getting lost?) or the threads are being disabled after they run once (which would explain why if there are 8 threads, it can only parse the first 8 http requests).
Here is my global queue variable.
int queue[QUEUE_SIZE];
This is the main thread. It creates a queue struct (defined elsewhere) with methods enqueue, dequeue, empty, etc. When the server accepts a connection, it enqueues the socket that the incoming connection is on. The worker threads which were dispatched at the beginning are constantly checking this queue to see if any jobs have been added, and if there are jobs, then they dequeue the socket, connect to that port, and read/parse/write the incoming http request.
int main(int argc, char* argv[])
{
int hSocket, hServerSocket; /* handle to socket */
struct hostent* pHostInfo; /* holds info about a machine */
struct sockaddr_in Address; /* Internet socket address stuct */
int nAddressSize = sizeof(struct sockaddr_in);
int nHostPort;
int numThreads;
int i;
init(&head,&tail);
//**********************************************
//ALL OF THIS JUST SETS UP SERVER (ADDR STRUCT,PORT,HOST INFO, ETC)
if(argc < 3) {
printf("\nserver-usage port-num num-thread\n");
return 0;
}
else {
nHostPort=atoi(argv[1]);
numThreads=atoi(argv[2]);
}
printf("\nStarting server");
printf("\nMaking socket");
/* make a socket */
hServerSocket=socket(AF_INET,SOCK_STREAM,0);
if(hServerSocket == SOCKET_ERROR)
{
printf("\nCould not make a socket\n");
return 0;
}
/* fill address struct */
Address.sin_addr.s_addr = INADDR_ANY;
Address.sin_port = htons(nHostPort);
Address.sin_family = AF_INET;
printf("\nBinding to port %d\n",nHostPort);
/* bind to a port */
if(bind(hServerSocket,(struct sockaddr*)&Address,sizeof(Address)) == SOCKET_ERROR) {
printf("\nCould not connect to host\n");
return 0;
}
/* get port number */
getsockname(hServerSocket, (struct sockaddr *) &Address,(socklen_t *)&nAddressSize);
printf("Opened socket as fd (%d) on port (%d) for stream i/o\n",hServerSocket, ntohs(Address.sin_port));
printf("Server\n\
sin_family = %d\n\
sin_addr.s_addr = %d\n\
sin_port = %d\n"
, Address.sin_family
, Address.sin_addr.s_addr
, ntohs(Address.sin_port)
);
//Up to this point is boring server set up stuff. I need help below this.
//**********************************************
//instantiate all threads
pthread_t tid[numThreads];
for(i = 0; i < numThreads; i++) {
pthread_create(&tid[i],NULL,worker,NULL);
}
printf("\nMaking a listen queue of %d elements",QUEUE_SIZE);
/* establish listen queue */
if(listen(hServerSocket,QUEUE_SIZE) == SOCKET_ERROR) {
printf("\nCould not listen\n");
return 0;
}
while(1) {
pthread_mutex_lock(&mtx);
printf("\nWaiting for a connection");
while(!empty(head,tail)) {
pthread_cond_wait (&cond2, &mtx);
}
/* get the connected socket */
hSocket = accept(hServerSocket,(struct sockaddr*)&Address,(socklen_t *)&nAddressSize);
printf("\nGot a connection");
enqueue(queue,&tail,hSocket);
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cond); // wake worker thread
}
}
Here is the worker thread. This should be always running checking for new requests (by seeing if the queue is not empty). At the end of this method, it should be deferring back to the boss thread to wait for the next time it is needed.
void *worker(void *threadarg) {
pthread_mutex_lock(&mtx);
while(empty(head,tail)) {
pthread_cond_wait(&cond, &mtx);
}
int hSocket = dequeue(queue,&head);
unsigned nSendAmount, nRecvAmount;
char line[BUFFER_SIZE];
nRecvAmount = read(hSocket,line,sizeof line);
printf("\nReceived %s from client\n",line);
//***********************************************
//DO ALL HTTP PARSING (Removed for the sake of space; I can add it back if needed)
//***********************************************
nSendAmount = write(hSocket,allText,sizeof(allText));
if(nSendAmount != -1) {
totalBytesSent = totalBytesSent + nSendAmount;
}
printf("\nSending result: \"%s\" back to client\n",allText);
printf("\nClosing the socket");
/* close socket */
if(close(hSocket) == SOCKET_ERROR) {
printf("\nCould not close socket\n");
return 0;
}
pthread_mutex_unlock(&mtx);
pthread_cond_signal(&cond2);
}
Any help would be greatly appreciated. I can post more of the code if anyone needs it, just let me know. I'm not the best with OS stuff, especially in C, but I know the basics of mutexes, cond. variables, semaphores, etc. Like I said, I'll take all the help I can get. (Also, I'm not sure if I posted the code exactly right since this is my first question. Let me know if I should change the formatting at all to make it more readable.)
Thanks!
Time for a workers' revolution.
The work threads seem to be missing a while(true) loop. After the HTTP exchange and closing the socket, they should be looping back to wait on the queue for more sockets/requests.

Resources