Unix socket Multiple client group chat with client name in c - c

So far everything have been working perfectly. Just the problem I've been having is that I can't think of a way of assigning or i would say giving names to the clients who are connect to the server so that other clients who know is wrote a message
server:
while (1) {
FD_ZERO(&read_set);
//FD_ZERO(&write_set);
FD_SET(fd, &read_set);
//FD_SET(fd, &write_set);
for (i = 0; i < num_clients; i++) { //at first this part will not excute
FD_SET(clients[i], &read_set);
}
select(fd + num_clients + 1, &read_set, NULL, NULL, NULL);
if (FD_ISSET(fd, &read_set)) {
if ((clients[num_clients++] = accept(fd, NULL, NULL)) == -1) {
perror("accept error");
continue;
}
printf("we got a connection!\n");
}
for (i = 0; i < num_clients; i++) {
if (FD_ISSET(clients[i], &read_set)) {
msg = read(clients[i], buf, sizeof(buf));
if (msg > 0) {
int savedclnt = clients[i];
printf("client %d says: %s\n", i, buf);
for (int p = 0; p<num_clients; p++)
{
if (clients[p] != savedclnt) {
//write("from %d",clients[p]);
//char msg2 = strcat(clients[i],msg);
write(clients[p], buf, msg);
}
}
}
}
}
}

For what you're doing, you don't really need threads. From personal experience, a single threaded server would do the job just fine. Instead of having the main body wrap around a call to accept() you should use select() to see which file descriptors have input, something along the lines of...
fd_set readfds;
struct timeval timeout;
unsigned int num_clients;
unsigned int clients[100];
while(1)
{
FD_ZERO(&readfds);
FD_SET(sock, &readfds);
for(i=0; i<num_clients; i++)
{
FD_SET(clients[i],&readfds);
}
timeout.tv_sec=1;
timeout.tv_usec=0;
select(max_fds, &readfds, NULL, NULL, &timeout);
if(FD_ISSET(sock,&readfds))
{
clients[num_clients++] = accept(sock,(struct sockaddr *)&server,&size);
}
for(i=0;i<num_clients;i++)
{
if(FD_ISSET(clients[i],&readfds))
{
int err=read(clients[i],buf,1000);
if(err>0)
{
for(p=0;p<num_clients;p++)
{
write(clients[i],buf,err);
}
}
}
}
}
That code is just an example and needs error checking to see if a client has disconnected, a way of updating the list of clients and seeing if a client is ready to receive data. But hopefully it gives you the basic idea of where to go?

Related

how linux select() system call monitor two files(file descriptors) and how to use timer argument

I need to use select system call where I have to open two files (file descriptors) and do read operation on those files which are ready, I need to use some timeout after every 5ms and read from those files
Here is my sample code:
int main()
{
fd_set readfds,writefds;
ssize_t nbytes,bytes_read;
char buf[20];
int fd_1,fd_2,retval;
struct timeval tv;
fd_1 = open("test1.txt", O_RDWR);
if(fd_1 < 0) {
printf("Cannot open file...\n");
return 0;
}
fd_2 = open("test2.txt", O_RDWR);
if(fd_2 < 0) {
printf("Cannot open file_2...\n");
return 0;
}
/* Wait up to five seconds. */
tv.tv_sec = 5;
tv.tv_usec = 0;
for(;;)
{
retval = select(FD_SETSIZE, &readfds, &writefds, NULL, &tv);
printf("select");
perror("select");
exit(EXIT_FAILURE);
//}
for(int i=0; i < FD_SETSIZE; i++)
{
FD_ZERO(&readfds);
if (FD_ISSET(i, &readfds))
{
// read call happens here //
nbytes = sizeof(buf);
bytes_read = read(i, buf, nbytes);
printf("%ld", bytes_read);
}
else
{
perror("read");
exit(EXIT_FAILURE);
}
}
}
You need to initialize the file descriptors sets before calling select(). With your current code select ends up with EBADF.
Something like this should do it:
FD_ZERO (&writefds);
for(;;)
{
FD_ZERO (&readfds);
FD_SET(fd_1, &readfds);
FD_SET(fd_2, &readfds);
retval = select(FD_SETSIZE, &readfds, &writefds, NULL, &tv);
if (retval < 0) {
perror("select");
exit(EXIT_FAILURE);
}
for(int i=0; i<FD_SETSIZE; i++)
{
if (FD_ISSET(i, &readfds))
{
// read call happens here //
printf("reading from file: %d\n", i);
nbytes = sizeof(buf);
bytes_read = read(i, buf, nbytes);
printf("read %ld bytes\n", bytes_read);
}
}
}
And also you may want to check that for(;;) loop and only exit on error. Once your select behaves properly you can proceed with debugging inside your second for loop. You don't seem to use writefds here, so you may also just set it to NULL in select.
One more note: since you only have two files in the read set you could simply check FD_ISSET(fd_1) / FD_ISSET(fd_2) instead of iterating over all FD_SETSIZE entries.

while loop for a game in C?

I have a word guessing game which I needs to repeat until the user types Q:. Generally, the answer is in a format A: answer. The game's using sockets!
Instead of while(1), I tried using while(buffer[0]!='Q') but that does not seem to work.
How do I keep making the client play the game?
Additionally, I also need to tell the user how many games he passed correctly?
while (1)
{
FD_ZERO(&readfds);
FD_SET(server_fd, &readfds);
int max_fd = server_fd;
for (int i = 0; i < NUMCLIENT; i++)
{
file_descriptor = client_sockets[i];
if (file_descriptor > 0)
{
FD_SET(file_descriptor, &readfds);
}
if (file_descriptor > max_fd)
{
max_fd = file_descriptor;
}
}
int return_value = select(max_fd + 1, &readfds, NULL, NULL, NULL);
if (return_value == -1)
{
printf("Select Error\n");
}
if (FD_ISSET(server_fd, &readfds))
{
if ((new_socket = accept(server_fd, (struct sockaddr *) &address,
(socklen_t*) &addrlen)) < 0)
{
perror("accept");
}
send(new_socket, greetings[0], strlen(greetings[0]), 0);
send(new_socket, greetings[1], strlen(greetings[1]), 0);
for (int i = 0; i < NUMCLIENT; i++)
{
if (client_sockets[i] == 0)
{
my_fortune(i);
client_sockets[i] = new_socket;
send(new_socket, client_challenges->question, strlen(client_challenges->question), 0);
break;
}
}
}
for (int i = 0; i < NUMCLIENT; i++)
{
file_descriptor = client_sockets[i];
if (file_descriptor == 0)
{
continue;
}
if (FD_ISSET(file_descriptor, &readfds))
{
memset(buffer, 0, 1024);
valread = read(file_descriptor, buffer, 1024);
if (valread == 0)
{
close(file_descriptor);
client_sockets[i] = 0;
continue;
}
printf("%s\n", buffer);
if (buffer[0] == 'A')
{
if (strlen(client_challenges->answer) == (strlen(buffer) - 4))
{
char store[100];
for (int i = 1; i <= strlen(client_challenges->answer); i++)
{
store[i - 1] = buffer[2 + i];
}
store[strcspn(store, "\r\n")] = 0;
if (strcmp(store, client_challenges->answer) == 0)
{
send(file_descriptor, correct, strlen(correct), 0);
//printf("O: Congratulation - challenge passed!\n");
}
else
{
send(file_descriptor, wrong, strlen(wrong), 0);
send(file_descriptor, client_challenges->answer, strlen(client_challenges->answer), 0);
}
//printf("Wrong guess - expected: %s",client_challenges->answer);}
}
else
{
send(file_descriptor, wrong, strlen(wrong), 0);
send(file_descriptor, client_challenges->answer, strlen(client_challenges->answer), 0);
}
//{printf("Wrong guess - expected: %s",client_challenges->answer);}
}
else if (buffer[0] == 'Q')
{
send(file_descriptor, ending, strlen(ending), 0);
exit(1);
}
else
{
send(file_descriptor, error, strlen(error), 0);
exit(1);
}
}
}
}
If there will only be 1 client, you can simply do
// Actual function to start the game
int play() {
int run = 1;
while (run) {
// run the game
// if game over,
run = 0;
}
return 0; // indicate the game ends gracefully
}
int main(int argc, char** argv) {
int run = 1;
int playAgain = 0;
while (run) {
if (play()) {
// Error: the game ends ungracefully
}
// Prompt for play again
if (!playAgain) {
run = 0;
}
}
// Bye
}
The reason is that the variable run is used to control the game flow. If the user quits or something went wrong, you can set run to 0 and the game will stop running.
If there are going to be multiple clients, you might want to have a look at pthread. Every time you receive a new connection, you can create a new thread with pthread_create and set the play function as start_routine and the fd as the argument. For example, you can try
int MAX_CLIENT = 5;
struct pthread_args {
int fd;
};
void* play(void* arg) {
struct pthread_args* args = (struct pthread_args*)arg;
int fd = args->fd;
// Start the game
}
int main(int argc, char** argv) {
pthread_t pth[MAX_CLIENT];
struct pthread_args pth_args;
int new_fd;
int i = 0;
while(1) { // The server continuously receive connections
// Accept connections
new_fd = accept(/*...*/)
if (new_fd < 0) {
// Error: accepting new connection
}
pth_args.fd = new_fd;
int r = pthread_create(&pth[i], NULL, play, &pth_args);
if (!r) {
// Error: creating thread
}
++i;
}
}
PS: You may want to have a look at pthread_detach as well.
Instead of while(1), I tried using while(buffer[0]!='Q') but that does not seem to work.
The reason while(buffer[0] == 'Q') doesn't work is because by the time the while check for the condition, the buffer (assuming it was declared outside of the while loop) might not be the same buffer anymore because you are looping over all clients and save the buffers from clients to the same buffer variable. So if there are 2 clients and the first client says 'Q' and the second client says 'A', the while sees buffer[0] as 'A' instead of 'Q' and hence continuing the game.
Also, I don't think exit(1) in else if (buffer[0] == 'Q') is a good idea. It kills the server and all clients will be forced to quit the game. Just remove the particular client from the client_sockets array.
:) Hope it helps

TCP Socket Multiplexing Send Large Data

Got some trouble with TCP socket multiplexing.
//socket is non-blocking
const int MAX = 4096;
char *buff[MAX];
char *p = buff;
int fd, rvalue;
rvalue = 0;
if ( (fd = open(path, O_RDONLY)) < 0 ) {
return errno;
} else {
int didsend, didread;
int shouldsend;
while ((didread = read(fd, buff, MAX)) > 0) {
p = buff;
shouldsend = didread;
while ( 1 ) {
didsend = send(sockfd, p, shouldsend, 0);
//if send succeeds and returns the number of bytes fewer than asked for then try to send rest part in next time.
if (didsend < shouldsend) {
p += didsent;
shouldsend -= didsend;
continue;
}
//if there is no place for new data to send, then wait a brief time and try again.
if ( didsend < 0 && (errno == EWOULDBLOCK || errno == EAGAIN) ) {
usleep(1000);
continue;
}
//if all data has been sent then sending loop is over.
if (didsend == shouldsend) {
break;
}
//send error
if ( didsend < 0 ) {
rvalue = errno;
break;
}
}
}
close(fd);
if (didread == -1) {
return errno;
}
return rvalue;
}
Assume I use an I/O Multiplexing function poll() or kqueue(), and non-blocking socket, then if there are only some small data like send a short message, it works fine.
But if it comes to large data, I mean larger than send()'s buffer size, since using non-blocking socket, send() will just send a portion of data, and return how much data it sends, the rest part of data can only be sent in another call of send(), but it takes time, and can't tell how long it will takes. So the second while() is actually a blocking send which using non-blocking socket.
Equivalent to:
//socket is blocking
const int MAX = 4096;
char *buff[MAX];
int fd, n;
if ( (fd = open(path, O_RDONLY)) < 0 ) {
return errno;
} else {
while ((n = read(fd, buff, MAX)) > 0) {
if (send(sockfd, buff, n, 0) < 0) {
return errno;
}
}
close(fd);
return 0;
}
So, what is the solution to this, multithreading might work but that's kind of wasting resource maybe.
This is the general pattern for a single-threaded server that works with multiple connections and non-blocking sockets.
It's primarily pseudo-code in C and doesn't do the necessary error checking. But it gives you an idea that for each accepted connection, you keep a struct instance that maintains the socket handle, request parsing state, response stream, and any other "state" members of that connection. Then you just loop using "select" to wait or having multiple threads doing this same thing.
Again this is only pseudo-code and uses select/poll as an example. You can get even more scalability with epoll.
while (1)
{
fd_set readset = {};
fd_set writeset = {};
for (int i = 0; i < number_of_client_connections; i++)
{
if (client_connections[i].reading_request)
FD_SET(client_connection.sock, &readset);
else
FD_SET(client_connection.sock, &writeset);
}
// add the listen socket to the read set
FD_SET(listen_socket, &readset);
select(n + 1, &readset, &writeset, &timeout); // wait for a socket to be ready (not shown - check for errors and return value)
if (FD_ISSET(listen_socket, &readset))
{
int new_client_socket = accept(listen_socket, &addr, &addrlength);
// create a struct that keeps track of the connection state data
struct ConnectionData client_connection = {};
client_connection.sock = new_client_socket;
client_connection.reading_request = 1; // awaiting for all the request bytes to come in
client_connections[number_of_client_connections++] = client_connection; // pseudo code, add the client_connection to the list
}
for (int i = 0; i < number_of_client_connections; i++)
{
if (client_connections[i].reading_request)
{
if (FD_ISSET(client_connections[i], &readset))
{
char buffer[2000];
int len = recv(client_connections[i].sock, buffer, 2000, 0);
// not shown - handle error case when (recv < 0)
// not shown - handle case when (recv == 0)
ProcessIncomingData(client_connections[i], buffer, len); // do all the request parsing here. Flip the client_connections[i].reading_request to 0 if ready to respond
}
}
else if (client_connections[i].reading_request == 0)
{
if (FD_ISSET(client_connections[i], &writeset))
{
client_connection* conn = &client_connections[i];
int len = send(conn->sock, conn->response_buffer + conn->txCount, conn->response_size - conn->txCount, 0);
conn->txCount += len;
if (conn->txCount == conn->response_size)
{
// done sending response - we can close this connection or change it to back to the reading state
}
}
}
}

Segmentation fault on second call of a function

I'm doing a bit of socket programming and one of the nodes in the system runs this loop.
while(1) {
read_fd_set = active_fd_set;
waiting = select(FD_SETSIZE, &read_fd_set, NULL, NULL, NULL);
if (waiting < 0) {
error("ERROR on select");
}
for (i = 0; i < FD_SETSIZE; i++) {
if (FD_ISSET (i, &read_fd_set)) {
if (i == sockfd[S_IN]) {
proccess_packet(sockfd[S_IN], sockfd[R_OUT],
buffer, atoi(argv[7]));
}
if (i == sockfd[R_IN]) {
perror("yay");
proccess_packet(sockfd[R_IN], sockfd[S_OUT],
buffer, atoi(argv[7]));
}
}
}
}
When the initial sender sends a packet everything works fine, including the first call of "process_packet". But when the receiver sends its acknowledgement the program throws a segmentation fault without even entering "process_packet". So I assume it is the call itself considering the perror still triggers before.
void proccess_packet(int sockfd_in, int sockfd_out,
Packet buffer, int p_trans)
{
int n;
bzero(&buffer,BUFLEN);
n = read(sockfd_in,&buffer,BUFLEN);
if (n < 0) {
error("ERROR reading from socket");
}
if(buffer.magicno == 0x497E) {
//if((double)rand()/ (double)((unsigned)RAND_MAX + 1) > p_trans) {
write(sockfd_out,&buffer,sizeof(buffer));
//}
}
}
EDIT:
typedef struct {
int magicno;
int type;
int seqno;
int dataLen;
char* data;
} Packet;

socket programming with select

I have two nodes communicating with a socket. Each node has a read thread and a write thread to communicate with the other. Given below is the code for the read thread. The communication works fine between the two nodes with that code. But I am trying to add a select function in this thread and that is giving me problems (the code for select is in the comments. I just uncomment it to add the functionality). The problem is one node does not receive messages and only does the timeout. The other node gets the messages from the other node but never timesout. That problem is not there (both nodes send and receive messages) without the select (keeping the comments /* */).
Can anyone point out what the problem might be? Thanks.
void *Read_Thread(void *arg_passed)
{
int numbytes;
unsigned char *buf;
buf = (unsigned char *)malloc(MAXDATASIZE);
/*
fd_set master;
int fdmax;
FD_ZERO(&master);
*/
struct RWThread_args_template *my_args = (struct RWThread_args_template *)arg_passed;
/*
FD_SET(my_args->new_fd, &master);
struct timeval tv;
tv.tv_sec = 2;
tv.tv_usec = 0;
int s_rv = 0;
fdmax = my_args->new_fd;
*/
while(1)
{
/*
s_rv = -1;
if((s_rv = select(fdmax+1, &master, NULL, NULL, &tv)) == -1)
{
perror("select");
exit(1);
}
if(s_rv == 0)
{
printf("Read: Timed out\n");
continue;
}
else
{
printf("Read: Received msg\n");
}
*/
if( (numbytes = recv(my_args->new_fd, buf, MAXDATASIZE-1, 0)) == -1 )
{
perror("recv");
exit(1);
}
buf[numbytes] = '\0';
printf("Read: received '%s'\n", buf);
}
pthread_exit(NULL);
}
You must set up master and tv before each call to select(), within the loop. They are both modified by the select() call.
In particular, if select() returned 0, then master will now be empty.

Resources