Related
I have a client program and a server program. There could be multiple servers and multiple
clients that can connect to multiple servers of there choice
The client program lists a menu
connect 4000 // connects to server on port 4000
bid 1000 4000 // send a bid value of 1000 to the server at port 4000
Now a server may recieve bids from several clients connected to it and keeps track of the highest
bid till now. Whenever a new bid is placed the server sends a broadcast to each client connected
to it one by one like - write(users[i].sock_fd, msg, size).
How do I listen to this message on the client side ?
There are two things here
The client needs to listen to the message sent by server.
The client is also reading the text or menu items (connect and bid) from command line from the user.
I have coded the part 2) But confused how to code 1) into client and simultaneously make the 2) also working
Client code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define BUF_SIZE 128
#define MAX_AUCTIONS 5
#ifndef VERBOSE
#define VERBOSE 0
#endif
#define ADD 0
#define SHOW 1
#define BID 2
#define QUIT 3
/* Auction struct - this is different than the struct in the server program
*/
typedef struct auction_data
{
int sock_fd;
char item[BUF_SIZE];
int current_bid;
} auction_data;
auction_data *auction_data_ptr;
/* Displays the command options available for the user.
* The user will type these commands on stdin.
*/
void print_menu()
{
printf("The following operations are available:\n");
printf(" show\n");
printf(" add <server address> <port number>\n");
printf(" bid <item index> <bid value>\n");
printf(" quit\n");
}
/* Prompt the user for the next command
*/
void print_prompt()
{
printf("Enter new command: ");
fflush(stdout);
}
/* Unpack buf which contains the input entered by the user.
* Return the command that is found as the first word in the line, or -1
* for an invalid command.
* If the command has arguments (add and bid), then copy these values to
* arg1 and arg2.
*/
int parse_command(char *buf, int size, char *arg1, char *arg2)
{
int result = -1;
char *ptr = NULL;
if (strncmp(buf, "show", strlen("show")) == 0)
{
return SHOW;
}
else if (strncmp(buf, "quit", strlen("quit")) == 0)
{
return QUIT;
}
else if (strncmp(buf, "add", strlen("add")) == 0)
{
result = ADD;
}
else if (strncmp(buf, "bid", strlen("bid")) == 0)
{
result = BID;
}
ptr = strtok(buf, " "); // first word in buf
ptr = strtok(NULL, " "); // second word in buf
if (ptr != NULL)
{
strncpy(arg1, ptr, BUF_SIZE);
}
else
{
return -1;
}
ptr = strtok(NULL, " "); // third word in buf
if (ptr != NULL)
{
strncpy(arg2, ptr, BUF_SIZE);
return result;
}
else
{
return -1;
}
return -1;
}
/* Connect to a server given a hostname and port number.
* Return the socket for this server
*/
int add_server(char *hostname, int port)
{
// Create the socket FD.
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
{
perror("client: socket");
exit(1);
}
// Set the IP and port of the server to connect to.
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
struct addrinfo *ai;
/* this call declares memory and populates ailist */
if (getaddrinfo(hostname, NULL, NULL, &ai) != 0)
{
close(sock_fd);
return -1;
}
/* we only make use of the first element in the list */
server.sin_addr = ((struct sockaddr_in *)ai->ai_addr)->sin_addr;
// free the memory that was allocated by getaddrinfo for this list
freeaddrinfo(ai);
// Connect to the server.
if (connect(sock_fd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
perror("client: connect");
close(sock_fd);
return -1;
}
if (VERBOSE)
{
fprintf(stderr, "\nDebug: New server connected on socket %d. Awaiting item\n", sock_fd);
}
return sock_fd;
}
/* ========================= Add helper functions below ========================
* Please add helper functions below to make it easier for the TAs to find the
* work that you have done. Helper functions that you need to complete are also
* given below.
*/
/* Print to standard output information about the auction
*/
void print_auctions(struct auction_data *a, int size)
{
printf("Current Auctions:\n");
for (int i = 0; i < size; i++)
{
struct auction_data auction_data = a[i];
printf("(%d) %s bid = %d\n", i, auction_data.item, auction_data.current_bid);
}
/* TODO Print the auction data for each currently connected
* server. Use the follosing format string:
* "(%d) %s bid = %d\n", index, item, current bid
* The array may have some elements where the auction has closed and
* should not be printed.
*/
}
/* Process the input that was sent from the auction server at a[index].
* If it is the first message from the server, then copy the item name
* to the item field. (Note that an item cannot have a space character in it.)
*/
void update_auction(char *buf, int size, struct auction_data *a, int index)
{
// TODO: Complete this function
// fprintf(stderr, "ERROR malformed bid: %s", buf);
// printf("\nNew bid for %s [%d] is %d (%d seconds left)\n", );
}
int main(void)
{
char name[BUF_SIZE];
int size = 0;
// Declare and initialize necessary variables
// TODO
// Get the user to provide a name.
printf("Please enter a username: ");
fflush(stdout);
int num_read = read(STDIN_FILENO, name, BUF_SIZE);
printf("%s-name\n", name);
if (num_read <= 0)
{
fprintf(stderr, "ERROR: read from stdin failed\n");
exit(1);
}
print_menu();
// TODO
char server_reply[2000];
while (1)
{
print_prompt();
char *command;
scanf("%m[^\n]s", &command);
getchar();
char arg1[100];
char arg2[100];
int commandNumber = parse_command(command, 1000, arg1, arg2);
char dest[100] = "";
strcpy(dest, name);
dest[strlen(dest) - 1] = '\0';
if (commandNumber == ADD)
{
printf("%s-name4\n", dest);
int port = atoi(arg2);
int sock_fd = add_server(arg1, port);
printf("%s-server\n", server_reply);
write(sock_fd, dest, strlen(dest));
auction_data_ptr = (auction_data *)realloc(auction_data_ptr, (size + 1) * sizeof(auction_data_ptr));
auction_data_ptr[size].sock_fd = sock_fd;
size++;
}
else if (commandNumber == SHOW)
{
print_auctions(auction_data_ptr, size);
}
else if (commandNumber == BID)
{
int itemIndex = atoi(arg1);
int bidValue = atoi(arg2);
printf("%d-test\n", auction_data_ptr[itemIndex].sock_fd);
send(auction_data_ptr[itemIndex].sock_fd, arg2, strlen(arg2), 0);
}
else if (commandNumber == QUIT)
{
}
// TODO
}
return 0; // Shoud never get here
}
Server Code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef PORT
#define PORT 30000
#endif
#define MAX_BACKLOG 5
#define MAX_CONNECTIONS 20
#define BUF_SIZE 128
#define MAX_NAME 56
int verbose = 0;
struct user
{
int sock_fd;
char name[MAX_NAME];
int bid;
};
typedef struct
{
char *item;
int highest_bid; // value of the highest bid so far
int client; // index into the users array of the top bidder
} Auction;
/*
* Accept a connection. Note that a new file descriptor is created for
* communication with the client. The initial socket descriptor is used
* to accept connections, but the new socket is used to communicate.
* Return the new client's file descriptor or -1 on error.
*/
int accept_connection(int fd, struct user *users)
{
int user_index = 0;
while (user_index < MAX_CONNECTIONS && users[user_index].sock_fd != -1)
{
user_index++;
}
if (user_index == MAX_CONNECTIONS)
{
fprintf(stderr, "server: max concurrent connections\n");
return -1;
}
int client_fd = accept(fd, NULL, NULL);
if (client_fd < 0)
{
perror("server: accept");
close(fd);
exit(1);
}
users[user_index].sock_fd = client_fd;
users[user_index].name[0] = '\0';
return client_fd;
}
/* Remove \r\n from str if the characters are at the end of the string.
* Defensively assuming that \r could be the last or second last character.
*/
void strip_newline(char *str)
{
if (str[strlen(str) - 1] == '\n' || str[strlen(str) - 1] == '\r')
{
if (str[strlen(str) - 2] == '\r')
{
str[strlen(str) - 2] = '\0';
}
else
{
str[strlen(str) - 1] = '\0';
}
}
}
/*
* Read a name from a client and store in users.
* Return the fd if it has been closed or 0 otherwise.
*/
int read_name(int client_index, struct user *users)
{
int fd = users[client_index].sock_fd;
/* Note: This is not the best way to do this. We are counting
* on the client not to send more than BUF_SIZE bytes for the
* name.
*/
int num_read = read(fd, users[client_index].name, MAX_NAME);
if (num_read == 0)
{
users[client_index].sock_fd = -1;
return fd;
}
users[client_index].name[num_read] = '\0';
strip_newline(users[client_index].name);
if (verbose)
{
fprintf(stderr, "[%d] Name: %s\n", fd, users[client_index].name);
}
/*
if (num_read == 0 || write(fd, buf, strlen(buf)) != strlen(buf)) {
users[client_index].sock_fd = -1;
return fd;
}
*/
return 0;
}
/* Read a bid from a client and store it in bid.
* If the client does not send a number, bid will be set to -1
* Return fd if the socket is closed, or 0 otherwise.
*/
int read_bid(int client_index, struct user *users, int *bid)
{
printf("inside bid\n");
int fd = users[client_index].sock_fd;
char buf[BUF_SIZE];
char *endptr;
int num_read = read(fd, buf, BUF_SIZE);
if (num_read == 0)
{
return fd;
}
buf[num_read] = '\0';
if (verbose)
{
fprintf(stderr, "[%d] bid: %s", fd, buf);
}
// Check if the client sent a valid number
// (We are not checking for a good bid here.)
errno = 0;
*bid = strtol(buf, &endptr, 10);
if (errno != 0 || endptr == buf)
{
*bid = -1;
}
return 0;
}
void broadcast(struct user *users, char *msg, int size)
{
for (int i = 0; i < MAX_CONNECTIONS; i++)
{
if (users[i].sock_fd != -1)
{
if (write(users[i].sock_fd, msg, size) == -1)
{
// Design flaw: can't remove this socket from select set
close(users[i].sock_fd);
users[i].sock_fd = -1;
}
}
}
}
int prep_bid(char *buf, Auction *a, struct timeval *t)
{
// send item, current bid, time left in seconds
printf("robin2-%s-%d\n", a->item, a->highest_bid);
printf("robin-%ld\n", t->tv_sec);
sprintf(buf, "%s %d %ld", a->item, a->highest_bid, t->tv_sec);
printf("robin-bid2\n");
return 0;
}
/* Update auction if new_bid is higher than current bid.
* Write to the client who made the bid if it is lower
* Broadcast to all clients if the bid is higher
*/
int update_bids(int client_index, struct user *users,
int new_bid, Auction *auction, struct timeval *t)
{
char buf[BUF_SIZE];
if (new_bid > auction->highest_bid)
{
auction->highest_bid = new_bid;
auction->client = client_index;
prep_bid(buf, auction, t);
if (verbose)
{
fprintf(stderr, "[%d] Sending to %d:\n %s\n",
getpid(), users[client_index].sock_fd, buf);
}
broadcast(users, buf, strlen(buf) + 1);
}
else
{
fprintf(stderr, "Client %d sent bid that was too low. Ignored\n",
client_index);
}
return 0;
}
int main(int argc, char **argv)
{
argc = 7;
argv[1] = "-v";
argv[2] = "-t";
argv[3] = "5";
argv[4] = "-p";
argv[5] = "4000";
argv[6] = "robin";
Auction auction;
int opt;
int port = PORT;
struct timeval timeout;
struct timeval *time_ptr = NULL;
int minutes = 0;
while ((opt = getopt(argc, argv, "vt:p:")) != -1)
{
switch (opt)
{
case 'v':
verbose = 1;
break;
case 't':
minutes = atoi(optarg);
timeout.tv_sec = minutes * 60;
timeout.tv_usec = 0;
time_ptr = &timeout;
break;
case 'p':
port = atoi(optarg);
break;
default:
fprintf(stderr, "Usage: auction_server [-v] [-t timeout] [-p port] item\n");
exit(1);
}
}
if (optind >= argc)
{
fprintf(stderr, "Expected argument after options\n");
exit(1);
}
auction.item = argv[optind];
auction.client = -1;
auction.highest_bid = -1;
struct user users[MAX_CONNECTIONS];
for (int index = 0; index < MAX_CONNECTIONS; index++)
{
users[index].sock_fd = -1;
users[index].name[0] = '\0';
}
// Create the socket FD.
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
{
perror("server: socket");
exit(1);
}
// Set information about the port (and IP) we want to be connected to.
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = INADDR_ANY;
// This sets an option on the socket so that its port can be reused right
// away. Since you are likely to run, stop, edit, compile and rerun your
// server fairly quickly, this will mean you can reuse the same port.
int on = 1;
int status = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
(const char *)&on, sizeof(on));
if (status == -1)
{
perror("setsockopt -- REUSEADDR");
}
// This should always be zero. On some systems, it won't error if you
// forget, but on others, you'll get mysterious errors. So zero it.
memset(&server.sin_zero, 0, 8);
// Bind the selected port to the socket.
if (bind(sock_fd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("server: bind");
close(sock_fd);
exit(1);
}
// Announce willingness to accept connections on this socket.
if (listen(sock_fd, MAX_BACKLOG) < 0)
{
perror("server: listen");
close(sock_fd);
exit(1);
}
if (verbose)
{
fprintf(stderr, "[%d] Ready to accept connections on %d\n",
getpid(), port);
}
// The client accept - message accept loop. First, we prepare to listen
// to multiple file descriptors by initializing a set of file descriptors.
int max_fd = sock_fd;
fd_set all_fds;
FD_ZERO(&all_fds);
FD_SET(sock_fd, &all_fds);
while (1)
{
// select updates the fd_set it receives, so we always use a copy
// and retain the original.
fd_set listen_fds = all_fds;
int nready;
if ((nready = select(max_fd + 1, &listen_fds, NULL, NULL, time_ptr)) == -1)
{
perror("server: select");
exit(1);
}
if (nready == 0)
{
char buf[BUF_SIZE];
sprintf(buf, "Auction closed: %s wins with a bid of %d\r\n",
users[auction.client].name, auction.highest_bid);
printf("%s", buf);
broadcast(users, buf, BUF_SIZE);
exit(0);
}
// Is it the original socket? Create a new connection ...
if (FD_ISSET(sock_fd, &listen_fds))
{
int client_fd = accept_connection(sock_fd, users);
if (client_fd != -1)
{
if (client_fd > max_fd)
{
max_fd = client_fd;
}
FD_SET(client_fd, &all_fds);
if (verbose)
{
fprintf(stderr, "[%d] Accepted connection on %d\n",
getpid(), client_fd);
}
}
}
// Next, check the clients.
for (int index = 0; index < MAX_CONNECTIONS; index++)
{
if (users[index].sock_fd > -1 && FD_ISSET(users[index].sock_fd, &listen_fds))
{
int client_closed = 0;
int new_bid = 0;
if (users[index].name[0] == '\0')
{
client_closed = read_name(index, users);
if (client_closed == 0)
{
char buf[BUF_SIZE];
prep_bid(buf, &auction, time_ptr);
if (verbose)
{
fprintf(stderr, "[%d] Sending to %d:\n %s\n",
getpid(), users[index].sock_fd, buf);
}
if (write(users[index].sock_fd, buf, strlen(buf) + 1) == -1)
{
fprintf(stderr, "Write to %d failed\n", sock_fd);
close(sock_fd);
}
}
}
else
{ // read a bid
client_closed = read_bid(index, users, &new_bid);
if (client_closed == 0)
{
update_bids(index, users, new_bid, &auction, time_ptr);
}
}
if (client_closed > 0)
{
FD_CLR(client_closed, &all_fds);
printf("Client %d disconnected\n", client_closed);
}
}
}
}
// Should never get here.
return 1;
}
Caveat: Because you've only posted partial code for server and client, this will be some suggestions.
Your client can attach/connect to multiple bid servers simultaneously. As such, it must be able to keep track of the multiple connections in a manner similar to a server.
Your main [stated] issue is that you're blocking the client on a user prompt (e.g. from stdin via scanf et. al.). Presently, this means that the client is "stuck" at user input prompt and can not field messages from the servers it is connected to. More on how to fix this below.
So, you'll have a bunch of code from the server that needs to be in the client with some minor differences. You may wish to generalize some of the server code a bit, so it can work both in server and client (e.g. you may want to move it to common.c).
You already have code in the server to handle multiple connections. The server needs a select mask that is the OR of the listen fd and all active client fds.
Likewise, your client needs a select mask that is the OR of the fd for user input (e.g. 0) and all active server connections.
Doing select on fd 0 and using stdio.h streams won't work too well. So, replace access to stdin with (e.g.) read(0,line_buffer,sizeof(line_buffer)). You do this if fd 0 is set in the select mask. The role is very similar to what your server does for the accept on sock_fd.
You'll need to allow for partial reads and append to the buffer until you see a newline. So, you'll have to do the work that fgets would normally do in assembling a whole line. Then, you can call parse_command.
Because read doesn't understand newline demarcations, the user could enter more than one line before you can do a read.
So, for user input of:
connect 4000\n
bid 100 4000\n
connect 5000\n
You may get partial reads of:
conn
ect
4000\nbid 100 4000
\nconnect
5000\n
You may also need to use the FIONREAD ioctl on the fd 0 to prevent blocking. And, you may need to set the kernel TTY layer into raw mode via termios calls.
The client now becomes very similar to your server code. It will handle [asynchronously] actions by any connected servers and user input.
A tip: Under the DRY principle ["don't repeat yourself"] ...
You already have a struct user in the server. The client will need something similar/identical, such as struct server. When generalizing the code, rather than having two distinct structs that do essentially the same thing, consider renaming the existing struct to (e.g.) struct connection
I have a multiclient Server Socket and I want to block the acces for other Clients, when sending "BEG" to the Server. To open the other Clients again, the Client has to send "END" to the Server. While other Clients are blocked off, they only can use "quit" to exit the Server and if they use conditions() they fall asleep.
So other Clients are blocked for the function conditions() if one process used "BEG", but the process himself has still acces to the function.
If I compile my Code, the Server is running, everything is fine but the Mutexe doesn't work.
The Code is going into the if statement of "BEG" and the Mutex should be locked, but other Clients aren't blocked off.
If I connect a second Client, the Client gets kicked if I use conditions().
My question is, why does the mutex not work for other Clients or in generell? How to check if the Mutex is working?
Edit:
Now my Semaphore doesn't block other processes
Edit 2: I found a way, not the best but one. Now some clients are getting kicked from the Server after using one condition().
main.c:
int state = 0;
int beg() {
state = 1;
return 0;
}
int end() {
state = 0;
return 0;
}
int main() {
int pid, t;
char *eingabe, *inputBuffer[BUFSIZE];
char delimiter[] = "\n ";
int rfd = erstelleSocket();
int cfd;
semaphor semID1 = semGET();
semaphor semID2 = semGET2();
marker[0] = 1;
t = semctl(semID1, 1, SETALL, marker);
if (t == -1) {
fprintf(stderr, "Error with marker\n");
}
t = semctl(semID2, 1, SETALL, marker);
if (t == -1) {
fprintf(stderr, "Error with marker\n");
}
while(1){
cfd = accept(rfd, (struct sockaddr *) &client, &client_len);
if (cfd < 0) {
close(cfd);
fprintf(stderr, "connection failed\n");
break;
}
pid = fork();
if (pid < 0) {
fprintf(stderr, "Error in new process creation\n");
}
if (pid == 0) {
bzero(input, sizeof(input));
bytes_read = read(cfd, input, BUFSIZE);
strncat(input, " ", strlen(" "));
input[strcspn(input, "\r\n")] = 0;
while (bytes_read > 0) {
eingabe = strtok(input, delimiter);
int i = 0;
while (eingabe != NULL) {
inputBuffer[i++] = eingabe;
eingabe = strtok(NULL, delimiter);
}
if (strncmp("quit", inputBuffer[0], 4) == 0) {
close(cfd);
break;
}
if (state != 1) {
down(semID2, 0); //down is a function with semop()
}
down(semID1, 0);
conditions(inputBuffer[0],
inputBuffer[1],
inputBuffer[2],
cfd, semID1, shmID);
up(semID1, 0);
if (state != 1) {
up(semID2, 0); //up is a function with semop()
}
bzero(input, sizeof(input));
bytes_read = read(cfd, input, BUFSIZE);
strncat(input, " ", strlen(" "));
input[strcspn(input, "\r\n")] = 0;
close(rfd);
}
}
close(cfd);
}
close(rfd);
}
my condition function:
void conditions(char *eingabehalter1,
char *eingabehalter2,
char *eingabehalter3,
int cfd, int shmID) {
if (strncmp("PUT", eingabehalter1, 3) == 0) {
put(eingabehalter2, eingabehalter3, cfd, shmID);
} else if (strncmp("GET", eingabehalter1, 3) == 0) {
get(eingabehalter2, cfd, shmID);
} else if (strncmp("DEL", eingabehalter1, 3) == 0) {
del(eingabehalter2, cfd, shmID);
} else if (strncmp("BEG", eingabehalter1, 3) == 0) {
beg();
} else if (strncmp("END", eingabehalter1, 3) == 0) {
end();
} else {
write(cfd, "cmd_nonexistent\n", strlen("cmd_nonexistent\n"));
}
}
createSocket.c:
int rfd; // Rendevouz-Descriptor
rfd = socket(AF_INET, SOCK_STREAM, 0);
int option = 1;
setsockopt(rfd,SOL_SOCKET, SO_REUSEADDR, (const void *) &option, sizeof(int));
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT);
int brt = bind(rfd, (struct sockaddr *) &server, sizeof(server));
int lrt = listen(rfd, 5);
return rfd;
}
main.h:
#include "shmmemory.h"
#include "semaphoren.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <netinet/in.h>
#define PORT 5678
struct sockaddr_in server;
struct sockaddr_in client;
socklen_t client_len;
char input[BUFSIZE];
int bytes_read;
int erstelleSocket();
void conditions(char *eingabehalter1,
char *eingabehalter2,
char *eingabehalter3,
int cfd, int shmID);
int beg();
int end();
unsigned short marker[2];
Your approach cannot work because you're trying to combine fork with threads. fork creates a copy of the parent's address space for each child process, which means that each child process has its own copy of the mutex object. Process-shared mutexes are possible in POSIX, with special attributes, but I suspect even those don't work with fork; they have to be placed in shared memory.
Have you considered creating threads with pthread_create for the service loop? Or else you can implement this entirely using fork (no pthread material). The children can use POSIX named semaphores (sem_open, et al) or possibly, dare I say it, System V IPC.
Also, don't use strtok in multithreaded code, and clearing memory to zero was standardized in 1989's ANSI C as memset(pointer, 0, size). Since that was 31 years ago, it's okay to lay bzero to rest.
The way you initialize the semaphores is wrong for your use case. From the man page of sem_init():
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
If pshared has the value 0, then the semaphore is shared between the
threads of a process, and should be located at some address that is
visible to all threads (e.g., a global variable, or a variable
allocated dynamically on the heap).
If pshared is nonzero, then the semaphore is shared between
processes, and should be located in a region of shared memory
Based on the above explanations from the man page, the things you need change are:
Semaphore declaration
Since you are using semaphores between processes, you need to declare the variable as shared. You can do that via mmap() to create unnamed UNIX semaphore as follows:
sem_t* sem_var = mmap(NULL, sizeof(sem_t), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0)
if (sem_var == MAP_FAILED) // Shared memory creation failed.
goto handle_shm_fail;
Semaphore initialization
Since you are using POSIX semaphores, to make your child use the same semaphores, pshared is set to 1 indicating semaphore is shared between processes.
if (sem_init(sem_var, 1, 1) != 0) // Semaphore initialization failed.
goto handle_sem_fail;
NOTE: In your code sem_var is of type sem_t, now it is a pointer to sem_t. Accordingly, you need to update your code.
I have a small problem, in practice I have to let two clients communicate (which perform different functions), with my concurrent server,
I discovered that I can solve this using the select, but if I try to implement it in the code it gives me a segmentation error, could someone help me kindly?
I state that before with a single client was a fable, now unfortunately implementing the select, I spoiled a bit 'all,
I should fix this thing, you can make a concurrent server with select ()?
can you tell me where I'm wrong with this code?
int main (int argc , char *argv[])
{
int list_fd,conn_fd;
int i,j;
struct sockaddr_in serv_add,client;
char buffer [1024];
socklen_t len;
time_t timeval;
char fd_open[FD_SETSIZE];
pid_t pid;
int logging = 1;
char swi;
fd_set fset;
int max_fd = 0;
int waiting = 0;
int compat = 0;
sqlite3 *db;
sqlite3_open("Prova.db", &db);
start2();
start3();
printf("ServerREP Avviato \n");
if ( ( list_fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0 ) {
perror("socket");
exit(1);
}
if (setsockopt(list_fd, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0)
perror("setsockopt(SO_REUSEADDR) failed");
memset((void *)&serv_add, 0, sizeof(serv_add)); /* clear server address */
serv_add.sin_family = AF_INET;
serv_add.sin_port = htons(SERVERS_PORT2);
serv_add.sin_addr.s_addr = inet_addr(SERVERS_IP2);
if ( bind(list_fd, (struct sockaddr *) &serv_add, sizeof(serv_add)) < 0 ) {
perror("bind");
exit(1);
}
if ( listen(list_fd, 1024) < 0 ) {
perror("listen");
exit(1);
}
/* initialize all needed variables */
memset(fd_open, 0, FD_SETSIZE); /* clear array of open files */
max_fd = list_fd; /* maximum now is listening socket */
fd_open[max_fd] = 1;
//max_fd = max(conn_fd, sockMED);
while (1) {
FD_ZERO(&fset);
FD_SET(conn_fd, &fset);
FD_SET(sockMED, &fset);
len = sizeof(client);
if(select(max_fd + 1, &fset, NULL, NULL, NULL) < 0){exit(1);}
if(FD_ISSET(conn_fd, &fset))
{
if ( (conn_fd = accept(list_fd, (struct sockaddr *)&client, &len)) <0 )
perror("accept error");
exit(-1);
}
/* fork to handle connection */
if ( (pid = fork()) < 0 ){
perror("fork error");
exit(-1);
}
if (pid == 0) { /* child */
close(list_fd);
close(sockMED);
Menu_2(db,conn_fd);
close(conn_fd);
exit(0);
} else { /* parent */
close(conn_fd);
}
if(FD_ISSET(sockMED, &fset))
MenuMED(db,sockMED);
FD_CLR(conn_fd, &fset);
FD_CLR(sockMED, &fset);
}
sqlite3_close(db);
exit(0);
}
I cannot understand how you are trying to use select here, and why you want to use both fork to let a child handle the accepted connection socket, and select.
Common designs are:
multi processing server:
The parent process setups the listening socket and loops on waiting actual connections with accept. Then it forks a child to process the newly accepted connection and simple waits for next one.
multi threaded server:
A variant of previous one. The master thread starts a new thread to process the newly accepted connection instead of forking a new process.
asynchronous server:
The server setups a fd_set to know which sockets require processing. Initially, only the listening socket is set. Then the main loop is (in pseudo code:
loop on select
if the listening socket is present in read ready sockets, accept the pending connection and add is to the `fd_set`, then return to loop
if another socket is present in read ready socket
read from it
if a zero read (closed by peer), close the socket and remove it from the `fd_set`
else process the request and return to loop
The hard part here is that is processing takes a long time, the whole process is blocked, and it processing involves sending a lot of data, you will have to use select for the sending part too...
I am currently studying TCP multiclient echo server and client using fork, thread, multiplexing IO and so on.
Below are the simple server and client using fork().
server_fork.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <arpa/inet.h>
static const int BUFSIZE = 1024;
int readn(int fd, char *buf, short n);
int main(void)
{
int cnt = 0;
int listenFD, connectFD;
struct sockaddr_in listenSocket, connectSocket;
char buffer [BUFSIZE];
if ((listenFD = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket() error\n");
exit(0);
}
if (setsockopt(listenFD, SOL_SOCKET, SO_REUSEADDR, &(int){ 1 }, sizeof(int)) < 0) {
perror("sockopt error\n");
exit(0);
}
memset(&listenSocket, 0, sizeof(listenSocket));
listenSocket.sin_family = AF_INET;
listenSocket.sin_addr.s_addr = inet_addr("0.0.0.0");
listenSocket.sin_port = htons(7777);
if (bind(listenFD, (struct sockaddr *)&listenSocket, sizeof(listenSocket)) < 0) {
perror("bind() error\n");
exit(0);
}
if (listen(listenFD, 1) < 0) {
perror("listen() error\n");
exit(0);
}
signal(SIGCHLD, SIG_IGN);
int connectSocketLen;
short readLen;
pid_t pid;
while (1) {
connectSocketLen = sizeof(connectSocket);
if ((connectFD = accept(listenFD, (struct sockaddr *)&connectSocket,
&connectSocketLen)) < 0) {
perror("accept() error\n");
exit(0);
}
pid = fork();
cnt++;
if (pid == 0) {
close(listenFD);
while (1) {
memset(buffer, 0, BUFSIZE);
if (readn(connectFD, buffer, 2) == 0) {
break;
}
readLen = (*(short *)&buffer);
if(readLen != 12)
printf("[%d] : %d\n", cnt, readLen);
if (readn(connectFD, buffer, readLen) == 0) {
break;
}
buffer[readLen] = 0;
int n;
if ((n = write(connectFD, buffer, readLen)) <= 0) {
perror("!!");
}
sleep(0);
}
close(connectFD);
exit(0);
}
else if (pid > 0) {
close(connectFD);
}
else {
perror("fork() error\n");
exit(0);
}
}
close(listenFD);
return 0;
}
int readn(int fd, char *buf, short n)
{
short sp = 0, readed;
while (n) {
readed = read(fd, buf + sp, n);
if (readed <= 0) {
return 0;
}
n -= readed;
sp += readed;
}
return 1;
}
client.c
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
static const int bufSize = 1024;
int main(int argc,char *argv[])
{
signal(SIGCHLD, SIG_IGN);
fork();
fork();
fork();
fork();
fork();
fork();
fork();
fork();
//fork();
//fork();
char length[2], recvBuf[bufSize];
char buf[]="hello, world\0";
short len = strlen(buf);
sprintf(length,"%c",len);
int client_sockfd, size, i, n, state;
uint64_t delta_us = 0;
struct sockaddr_in server_addr;
struct timespec start, end;
client_sockfd = socket(PF_INET, SOCK_STREAM, 0);
memset(&server_addr, 0, sizeof server_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(7777);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr.s_addr);
state = connect(client_sockfd, (struct sockaddr *)&server_addr,
sizeof server_addr);
if (state < 0) {
perror("connect err");
exit(1);
}
for (i=0;i<10;i++) {
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
n = write(client_sockfd, length, sizeof length);
if (n<=0) {
perror("write err");
exit(1);
}
n = write(client_sockfd, buf, *((short *)&length));
if (n<=0) {
perror("write err");
exit(1);
}
n = read(client_sockfd, recvBuf, *((short *)&length));
if (n<=0) {
perror("read err");
exit(1);
}
clock_gettime(CLOCK_MONOTONIC_RAW, &end);
delta_us += (end.tv_sec - start.tv_sec) * 1000000 +
(end.tv_nsec - start.tv_nsec)/1000;
printf("%lu\n", delta_us);
sleep(1);
}
return 0;
}
The client first transmits the length of the message represented by 2 bytes.
Then client sends a "hello, world" message to the server in buf.
The server first reads 2 bytes through readn () and reads the message as much.
But in all cases (fork, thread, multiplexing IO... whatever), I have a common difficulty.
The problem is that : In the above source code, the length of the message("hello, world" is 12. so it is expected to read 12 in the first readn () of the server.
In fact, when the number of clients is small (the number of forks on the client is 7 or less), it works fine.
However, if the number of clients increases, the value of readLen will be 25960 on some connections. 25960 is the value that represents "he" in hex.
Why is this problem happening when there are many clients?
I wonder if it is the same as sending a TCP packet at one time and dividing it two times.
Below is a tcpdump capture of the above problem situation.
I'm sorry that I could not upload the image right away.
tcpdump
On the client side, you have multiple problems surrounding how you send the message length. Starting here:
char length[2], recvBuf[bufSize];
char buf[]="hello, world\0";
short len = strlen(buf);
sprintf(length,"%c",len);
Your sprintf format promises that the third argument will be of type char (promoted to int), and instructs it to output the corresponding character into the string. In fact, the argument is a short (promoted to int), and this mismatch produces undefined behavior.
In practice, the overall sprintf call is probably equivalent to this:
length[0] = (char)(int)len;
length[1] = '\0';
That has implementation-defined characteristics if char is a signed type, but in any event, it cannot capture a length greater than the maximum value that can be represented by an unsigned char.
The client goes on to do this:
n = write(client_sockfd, length, sizeof length);
That's not inherently wrong, but it does fail to accommodate the possibility of a short write. Moreover, the server does not interpret this part of the message in a manner consistent with the way it was prepared:
if (readn(connectFD, buffer, 2) == 0) {
break;
}
readLen = (*(short *)&buffer);
As it turns out, that combination might happen to work if the server uses a 16-bit, little-endian representation for type short (subject to the restriction I already described on representable message length) and an execution character set compatible with the client's, but those are not safe assumptions for network software in general.
In part, you seem to be missing an important point about read() and write() and char pointers: a char * can be used to read the representation of an object of any type, so you do not need to move your data into a char array in order to send or receive it.
Overall, this would be a more appropriate approach:
// Client:
uint16_t len = strlen(buf); // risk: buf may be too long
uint16_t len_n = htons(len);
int n = writen(client_sockfd, &len_n, 2); // a write() analog of readn()
// ... handle possible error ...
// Sever:
uint16_t len_n;
int n = readn(connectFD, &len_n, 2);
// ... possible handle error ...
uint16_t readLen = ntohs(len_n);
Note that there is no need to copy the length into a separate char array to send it, nor to receive it into a char array. On the other hand, note also the use of a specified-size data type (uint16_t) on both sides, and the use of htons() and ntohs() to ensure that the client and server interpret the bytes of the data the same way. Furthermore, note the use of a write analog of readn() to send the data, which otherwise could be sent incompletely.
By the same token the client and server should both use the (hypothetical) writen() function to send the text of the message, and just like the server, the client should use readn() to read it. Failing to account for the possibility of short reads and writes is a significant flaw, especially in a protocol such as yours whose message boundaries are not easily distinguishable from data. An unnoticed short read or write will cause your client and server to fall out of sync, with no easy way to recover.
Apart from the problems already noticed by #JohnBollinger, you only use a listen window of 1 in server.c when all your forked client processes try to connect at the same time. It caused plenty of connect : conn reset by peer errors in my tests. I had to use a window greater than 64 on my FreeBSD (no errors at 256) to get rid of them :
if (listen(listenFD, 256) < 0) {
perror("listen() error\n");
exit(0);
}
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(<ime));
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(<ime));
////////////////////////////////////////////////////////////////////////
// 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.