C UDP Client-Server stuck comunication - c

for one of my university courses I must realize a simple chat program in C that uses UDP Client-Server, this is the description that the teacher sent us:
You should develop a private chat environment to exchange
text messages between hosts. Message encryption is optional but not required.
The project should be composed by 2 main modules:
Server: receives and stores each message in a sort of chat database.
A very naive database would consist in a User struct,
that contains everything (login credentials, chats, ...).
Each Chat structure contains the actual messages.
Client: provides a very simple interface to select a
receiver for our message and then write the content of the message.
Login is required. If a receiver is not subscribed returns an error.
The project should be tested with at least 3 users :)
I managed to implement the authentication phase but then when trying to implement the message exchange phase I got stuck. When I try to send the linked_list of online users from the Server to the Client the execution freezes and not only that but it gives somewhat of random behavior, sometimes gets stuck on the first try sometimes on the second and so on. I also noticed that when I introduced a separated thread in the Client to handle the inbox of messages the situation got worst getting stuck more often then before. I will add the code of the functions responsible of sending and receiving the online users and also the link to my git repo where if you want you can find the complete code.
This is the code in the Server:
void Send_list(ListHead* head, int sockfd,struct sockaddr_in cliaddr, int size){
int written_bytes;
int len = sizeof(cliaddr);
char username[50];
if(head->size == 1){
return;
}
ListItem* aux = head->first;
for(int i=0;i<size;i++){
memset(username,0,sizeof(username));
UserListItem* uitem = (UserListItem*) aux;
strcpy(username,uitem->user.username);
do{
written_bytes = 0;
written_bytes = sendto(sockfd,(const char *)username,strlen(username),0,(const struct sockaddr*)&cliaddr,len);
}while(written_bytes != strlen(username));
printf("\nusername mandato: %s",username);
printf("\n");
if(aux->next){
aux = aux->next;
}
}
}
And this is the code in the Client:
int recv_list(int sockfd,struct sockaddr_in servaddr, ListHead* head,int size, char username[50]){
char onuser[50];
int len = sizeof(servaddr);
int read_bytes;
int idx = 1;
if(size == 1){
return 1;
}
for(int i=0;i<size;i++){
read_bytes = recvfrom(sockfd,(char *)onuser,sizeof(onuser),0,(struct sockaddr*)&servaddr,&len );
onuser[read_bytes] = '\0';
if(List_find_by_username(head,onuser) == 0 || strcmp(onuser,username)){
UserListItem* uitem = malloc(sizeof(UserListItem));
memset(uitem,0,sizeof(UserListItem));
UList_init(uitem,onuser);
uitem->idx = idx++;
ListItem* result = List_insert(head,head->last,(ListItem*)uitem);
assert(result);
}
memset(onuser,0,sizeof(onuser));
}
UserList_print(head);
return 0;
}
And this is the link to my git repo: https://gitlab.com/antonio_ciprani/so-progetto-20_21
I work in an Ubuntu based system.
I really hope that somebody can help me because this is driving me crazy :(

I also noticed that when I introduced a separated thread in the Client to handle the inbox of messages the situation got worst getting stuck more often then before.
Indeed the use of threads in your program does more harm than good. Especially that in the main loop you pthread_create a new reciving thread, which competes with the main thread for the incoming messages, disrupts the course of recv_list. Better don't use threads for your project - you'll avoid a lot of problems.
Let's first write two helper functions:
void store(thread_args_t *targs, Message *msg)
{ // code taken from your function "reciving"
if (!strcmp(targs->user->username, msg->reciver))
{
Inbox *mitem = malloc(sizeof (Inbox));
strcpy(mitem->msg.sender, msg->sender);
strcpy(mitem->msg.reciver, msg->reciver);
strcpy(mitem->msg.data, msg->data);
ListItem *result =
List_insert(targs->inbox, targs->inbox->last, (ListItem *)mitem);
assert(result);
}
}
char *input(thread_args_t *targs)
{ // wait for user input and store incoming messages
fflush(stdout);
fd_set fds, fdr;
FD_ZERO(&fds);
FD_SET(0, &fds); // add STDIN to the fd set
FD_SET(targs->sockfd, &fds); // add socket to the fd set
for (; ; )
{
if (fdr = fds, select(targs->sockfd+1, &fdr, NULL, NULL, NULL) < 0)
perror("select"), exit(1);
if (FD_ISSET(0, &fdr))
{ // this is the user's input
static char data[256];
if (!fgets(data, sizeof data, stdin)) return NULL; // no more user input
data[strlen(data)-1] = '\0';
return data;
}
// if no user input, then there's a message
Message msg;
socklen_t len = sizeof targs->servaddr;
if (recvfrom(targs->sockfd, &msg, sizeof msg, 0,
(struct sockaddr *)targs->servaddr, &len) < 0)
perror("recvfrom"), exit(1);
store(targs, &msg);
}
}
Now you can replace the main loop body in main with this:
int ret, op;
printf("\nPlease choose an option: ");
printf("\n1.Send a message!");
printf("\n2.Incoming messages!");
printf("\n3.Logout!");
printf("\nYour choice:\t");
char *str = input(&targs);
sscanf(str, "%d", &ret);
printf("\nqua bro?\n");
if (ret == 1)
{
printf("\nqua loz?\n");
int res, read_bytes, size, id;
op = 3;
socklen_t len = sizeof servaddr;
sendto(sockfd, &op, sizeof op, MSG_CONFIRM,
(struct sockaddr *)&servaddr, len);
printf("\nqua shiiis?\n");
// We cannot preclude that a message arrives here,
// therefore we must handle that case.
Message msg;
while ((read_bytes = recvfrom(sockfd, &msg, sizeof msg, 0,
(struct sockaddr *)&servaddr,
&len)) == sizeof msg)
store(&targs, &msg);
if (read_bytes == -1) perror("recvfrom"), exit(1);
size = *(int *)&msg;
printf("\nqua ci siamo?\n");
res = recv_list(sockfd, servaddr, &on_list, size, user.username);
printf("\nqua?\n");
if (res == 0)
{
printf("\nChoose whom you want to send a message to");
printf("\nYour choice:\t");
str = input(&targs);
sscanf(str, "%d", &id);
printf("\nWrite the message you want to send:\n");
str = input(&targs);
Init_msg(&msg, str, id, &on_list, user.username);
int written_bytes = sendto(sockfd, &msg, sizeof msg, MSG_CONFIRM,
(struct sockaddr *)&servaddr, len);
if (written_bytes == -1) perror("sendto"), exit(1);
// With your present server, a message cannot arrive here, but you
// possibly will want to change that, so let's handle it already.
while ((read_bytes = recvfrom(sockfd, &msg, sizeof msg, 0,
(struct sockaddr *)&servaddr,
&len)) == sizeof msg)
store(&targs, &msg);
if (read_bytes == -1) perror("recvfrom"), exit(1);
int sent = *(int *)&msg;
if (sent == 0) printf("\nMessage sent!");
else
if (sent == 1) printf("\nUser is offline :(");
}
else
if (res == 1) printf("\nNo online user :(");
}
else
if (ret == 2) Print_msg(&inbox);
The next thing you possibly want to improve is modifying the server function Forward_message so that it allows for incoming commands from another client while waiting for a message.

Related

Recieve a message from server asynchronously

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

Segmentation fault using the buffer recived from recv() tcpServer c

I'm trying to create an application client/server in c, but after recv() when I try to use the buffer received the program give segmentation fault (core dump created), I can't work out it.
This is my code at server side:
int req_socket_id;
int comunication_socket_id;
struct sockaddr_in server_add;
struct sockaddr_in client_add;
socklen_t client_add_size;
char buffer[255];
//char mess[1024];
int i, n;
//int index;
unsigned int num;
// AF_INET = famiglia di indirizzi iPv4
req_socket_id = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(req_socket_id<0)
{
printf("Socket initialization failed !!");
return -1;
}
e
memset(&server_add, 0, sizeof(server_add)); // azzeramento struttura
server_add.sin_family = AF_INET; // dominio indirizzi IP
server_add.sin_addr.s_addr = 0; // indirizzo IP
server_add.sin_port = htons(23165); // numero di porta UDP
if(bind(req_socket_id, (struct sockaddr*) &server_add, sizeof(server_add)) < 0)
{
perror("\nErrore associazione porta e socket!\n");
close(req_socket_id);
return -1;
}
if(listen(req_socket_id, 1)<0)
{
perror("\nErrore nell'ascolto!\n");
close(req_socket_id);
return -1;
}
while(1)
{
client_add_size = sizeof(client_add);
comunication_socket_id = accept(req_socket_id, (struct sockaddr*) &client_add, &client_add_size);
if(comunication_socket_id>=0)
{
//index = 0;
char tmp[100];
while(1)
{
printf("\nOk");
//n = recv(comunication_socket_id, (char*) buffer, sizeof(buffer)+1, 0);
//n = recv(comunication_socket_id, (void*) buffer, sizeof(buffer)+1, 0);
n = recv(comunication_socket_id, buffer, sizeof(buffer)+1, 0);
printf("\nReceved! n: %d", n);
printf("\nReceved: %s", buffer);
if(strcmp(buffer, "end")==0)
{
close(comunication_socket_id);
printf("\n...socket closed");
return -1;
}
[...]
and this is client side code :
unsigned long start, now;
unsigned int *num = (unsigned int*)buffer;
int i, n;
TCPclient_send(buffer, strlen(buffer)+1);
printf("\nSent: |%s|\n", buffer);
start = clock(); // tempo iniziale attesa
now = clock(); // tempo attuale
while ((now - start) < TIMEOUT)
{
if ((n = TCPclient_receive(&buffer[i], sizeof(buffer)-i)) > 0)
{
i += n;
if (i >= sizeof(unsigned int))
{
// risposta completa
printf("Receved number %u.\r\n", ntohl(*num));
TCPclient_disconnect();
return 0;
}
}
now = clock();
}
printf("No answer receved!\r\n");
TCPclient_disconnect();
Console server side output:
davide#davide-VirtualBox:~/Documenti/RubricaTCP$ ./TCPserver
Ok
Receved! n: 11
Errore di segmentazione (core dump creato) //-->segmentation fault, end of process
Console client side output:
Insert the command (end to close): SET=A;A;A;
Sent: |SET=A;A;A;|
No answer receved!
PS: this is my first application with sockets, so it's possible that i've done some stupid mistakes. I looked for answers in many topics, but i didn't find anything that can work out it.
Thank you very much
n = recv(comunication_socket_id, buffer, sizeof(buffer)+1, 0);
sizeof(buffer)+1 is inherently incorrect. It should be sizeof(buffer). You are using memory that does not exist.
printf("\nReceved! n: %d", n);
OK.
printf("\nReceved: %s", buffer);
Wrong. This should be
printf("\nReceved: %.*s", n, buffer);
Then:
if(strcmp(buffer, "end")==0)
This is also wrong. There is no guarantee that you've received a null terminated string or a complete command. It's a byte-stream protocol. If you want messages you have to implement them yourself. It is rarely correct to write networking code or any I/O code for that matter that doesn't have read or receive loops.

How to use nanomsg survey architecture without while loop?

I was going through nanomsg usage for IPC and wanted to use SURVEY-archetype architecture described here.In this, processes run as client and server and exchange data. Now server has following code (also listed in the provided link):
int server (const char *url)
{
int sock = nn_socket (AF_SP, NN_SURVEYOR);
assert (sock >= 0);
assert (nn_bind (sock, url) >= 0);
sleep(1); // wait for connections
int sz_d = strlen(DATE) + 1; // '\0' too
printf ("SERVER: SENDING DATE SURVEY REQUEST\n");
int bytes = nn_send (sock, DATE, sz_d, 0);
assert (bytes == sz_d);
while (1)
{
char *buf = NULL;
int bytes = nn_recv (sock, &buf, NN_MSG, 0);
if (bytes == ETIMEDOUT) break;
if (bytes >= 0)
{
printf ("SERVER: RECEIVED \"%s\" SURVEY RESPONSE\n", buf);
nn_freemsg (buf);
}
}
return nn_shutdown (sock, 0);
}
Since socket type is NN_SURVEYOR, the while-loop doesn't wait at nn_recv. For client, socket type is NN_RESPONDENT and so, while loop in client waits at nn_recv.
Now since this while loop runs continuously and infinitely, the CPU usage shoots up to 99%. Can you please tell me that is there any other way to make survey architecture using nanomsg.
Try adding yield() call in the end of loop if nothing was received.

Executing child process in new terminal

I want to make a simple chat application for unix.
I have created one server which supports multiple clients. When ever a new client connects to the server a new process is created using fork command. Now the problem is all the child processes share the same stdin on the server, cause of this in order to send a message to 2nd clien 1st child prosess has to terminte. In order to resolve this I would like to run each child process in a new terminal.
This can be achieved by writing the code for the child process code in a new file and executing it like xterm -e sh -c .(i have not tried this though).
What i really want is not to have two file just to fireup a new terminal and run rest of the code in it.
int say(int socket)
{
char *s;
fscanf(stdin,"%79s",s);
int result=send(socket,s,strlen(s),0);
return result;
}
int main()
{
int listener_d;
struct sockaddr_in name;
listener_d=socket(PF_INET,SOCK_STREAM,0);
name.sin_family=PF_INET;
name.sin_port=(in_port_t)htons(30000);
name.sin_addr.s_addr=htonl(INADDR_ANY);
int c = bind(listener_d,(struct sockaddr *)&name,sizeof(name)); //Bind
if(c== -1)
{
printf("\nCan't bind to socket\n");
}
if(listen(listener_d,10) == -1) // Listen
{
printf("\nCan't listen\n");
}
puts("\nWait for connection\n");
while(1)
{
struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
int connect_d = accept(listener_d,
(struct sockaddr*)&client_addr,&address_size); //Accept
if(connect_d== -1)
{
printf("\nCan't open secondary socket\n");
}
if(!fork())
{
close(listener_d);
char *msg = "welcome Sweetone\n";
if(send(connect_d,msg,strlen(msg),0))
{
printf("send");
}
int k=0;
while(k<5)
{
say(connect_d);
++k;
}
close(connect_d);
exit(0);
}
close(connect_d);
}
close(listener_d);
return 0;
}
I think the message sending between your client and servers is a bit unusual. It is more common, in this simple "just test how it works" scenario to have the clients sending messages to the server. As an example I could mention a simple echo service, which mirrors everything a client sends, back to the client. Is this design forced by some requirements?
Critique aside, I have two separate changes that could make your current design work. They both involve changing the reading of input in the subservers.
Alternative 1:
Instead of reading from stdin, create a named pipe ( see man 3 mkfifo), fex /tmp/childpipe"pid_of_subserver_here". You could create the pipe in say() and open it for reading. Then use echo (man echo) to write to the pipe echo "My message" > /tmp/childpipe"NNNN". Before exiting the child, remember to remove the pipe with unlink()
Alternative 2:
Create an unnamed pipe between server and each subserver. This makes the code much more messy, but avoids creating named pipes and using echo. Example code is included below. It has insufficient error handling (like most example code) and does not handle disconnecting client properly.
Example usage: 1) start server ./a.out 2) (connect client in external window (e.g. nc localhost 30000) 3) write to client 1 by typing "1Hello client one" 4) (connect second client in third window etc) 4) Write to second client by typing "2Hello second client"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>
enum max_childeren{
MAX_CHILDEREN = 50
};
int say(int socket)
{
char buf[513] = {0};
fgets(buf, sizeof(buf), stdin);
int result=send(socket, buf, strlen(buf),0);
return result;
}
int main()
{
int listener_d;
struct sockaddr_in name;
listener_d=socket(PF_INET,SOCK_STREAM,0);
name.sin_family=PF_INET;
name.sin_port=(in_port_t)htons(30000);
name.sin_addr.s_addr=htonl(INADDR_ANY);
int on = 1;
if (setsockopt(listener_d, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0){
perror("setsockopt()");
}
int c = bind(listener_d,(struct sockaddr *)&name,sizeof(name)); //Bind
if(c== -1)
{
printf("\nCan't bind to socket\n");
}
if(listen(listener_d,10) == -1) // Listen
{
printf("\nCan't listen\n");
}
// Edited here
int number_of_childeren = 0;
int pipes[2] = {0};
int child_pipe_write_ends[MAX_CHILDEREN] = {0};
fd_set select_fds;
FD_ZERO(&select_fds);
puts("\nWait for connection\n");
while(1)
{
struct sockaddr_storage client_addr;
unsigned int address_size = sizeof(client_addr);
// Edited here, to multiplex IO
FD_SET(listener_d, &select_fds);
FD_SET(STDIN_FILENO, &select_fds);
int maxfd = listener_d + 1;
int create_new_child = 0;
int connect_d = -1; // moved here
select(maxfd, &select_fds, NULL, NULL, NULL);
if (FD_ISSET(listener_d, &select_fds)){
connect_d = accept(listener_d,
(struct sockaddr*)&client_addr,&address_size); //Accept
if(connect_d== -1)
{
printf("\nCan't open secondary socket\n");
exit(EXIT_FAILURE);
}
create_new_child = 1;
}
char buf[512] ={0};
char *endptr = NULL;
if (FD_ISSET(STDIN_FILENO, &select_fds)){
fgets(buf, sizeof(buf), stdin);
long int child_num = strtol(buf, &endptr, 10);
if (child_num > 0 && child_num <= number_of_childeren) {
write(child_pipe_write_ends[child_num - 1], endptr, strnlen(buf, sizeof(buf)) - (endptr - buf));
}
else {
printf("Skipping invalid input: %s\n", buf);
}
}
if (create_new_child != 1)
continue;
number_of_childeren++; // Edited here
int error = pipe(pipes);
if (error != 0){
//handle errors
perror("pipe():");
exit(EXIT_FAILURE);
}
child_pipe_write_ends[number_of_childeren - 1] = pipes[1];
if(!fork())
{
error = dup2(pipes[0], STDIN_FILENO);
if (error < 0){ // could also test != STDIN_FILENO but thats confusing
//handle errors
perror("dup2");
exit(EXIT_FAILURE);
}
close(pipes[0]);
close(listener_d);
char *msg = "welcome Sweetone\n";
if(send(connect_d,msg,strlen(msg),0))
{
printf("send\n");
}
int k=0;
while(k<5)
{
say(connect_d);
++k;
}
close(connect_d);
exit(0);
}
close(connect_d);
close(pipes[0]);
}
close(listener_d);
return 0;
}
The code needs refactoring into functions. It is too long. I tried to do the least possible amount of changes, so I left the restructuring as an exercise.
fscanf(stdin,"%79s",s);
Why? Is it tcp-chat? You have some socket for each client and if yoy want to "say" something then you must to use client. It's true logick.
The server usually sends a service messages only. It's true logick too.
But if you want new terminal then you can try to use a exec's family from unistd.h .

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