I have a chat application for the command line, but I can't seem to figure out how to display an "Enter a message:" prompt for each message entry. This is a super simple problem, but the multi-threading and client/server relationship is throwing me off.
The client side code:
#define PORT 12000
#define BUFFER 4096
void * receive(void * socket) {
int socket_fd, response;
char buffer[BUFFER];
socket_fd = (int) socket;
while(true) {
response = recvfrom(socket_fd, buffer, BUFFER, 0, NULL, NULL);
if (response) {
printf("Server: %s", buffer);
}
}
}
int main(int argc, char**argv) {
struct sockaddr_in address, cl_addr;
char * server_address;
int socket_fd, response;
char buffer[BUFFER];
pthread_t thread;
if (argc < 2) {
printf("Usage: client [IP Address]\n");
exit(1);
}
server_address = argv[1];
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(server_address);
address.sin_port = PORT;
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
response = connect(socket_fd, (struct sockaddr *) &address, sizeof(address));
if (response < 0) {
printf("Failed to connect\n");
exit(1);
}
printf("Connected\n");
// Create new thread to receive messages
pthread_create(&thread, NULL, receive, (void *) socket_fd);
// Get message from stdin and send to server
printf("Enter a message: ");
while (fgets(buffer, BUFFER, stdin) != NULL) {
sendto(socket_fd, buffer, BUFFER, 0, (struct sockaddr *) &address, sizeof(address));
}
close(socket_fd);
pthread_exit(NULL);
return 0;
}
The issue is that "Enter a message:" is only displayed once. I've tried adding it inside the while(fgets) loop, which successfully puts the prompt on each line the client writes, but when the server sends something, it gets thrown off.
Connected
Enter a message: foo
Enter a message: bar
Enter a message: Server: foo
Server: bar
I've tried adding newlines in various places and adding another prompt after the receive
function, but no matter what I do, there's always some edge case that messes things up. Is there some way to just give fgets
a default prompt or something? I've spent way too much time on something that should be so simple.
Is there some way to just give fgets a default prompt or something?
Not that I'm aware of, unless you want to start using a library like ncurses to do more advanced terminal management (and for a toy program, you probably don't).
What you can do instead is:
Call fflush(stdout);
immediately after your printf("Enter a message: ");
call, to force the prompt's text to be displayed immediately (rather than only after the next newline is printed).
Put a carriage-return character at the front of your server-printf(), e.g. printf("\rServer: %s\n", buffer);
That will move the terminal's text-cursor back to the beginning of the line, so that the server-printf() text will overwrite the prompt rather than appending to it. (Note if your server-text isn't long enough, you might have to add some extra spaces at the end to ensure that all of your prompt's text gets overwritten)