I am quite inexperienced with multithreading in C, so I would really appreciate some input on this piece of client-side code (extraneous parts have been stripped out for simplicity's sake).
// client_read(): read from server
void *client_read(int sockfd) {
char text[MAX_TEXT_SIZE + 2];
while (1) {
// clean out buffer
bzero(text, sizeof(text));
// read from server
if (read(sockfd, text, sizeof(text)) <= 0) {
fprintf(stderr, "failed to read\n");
exit(1);
}
// print message from server to stdout
fprintf(stdout, "%s", text);
// quit conversation
if (strncmp(text, "bye\n", 4) == 0) {
fprintf(stdout, "end to conversation\n");
close(sockfd);
exit(0);
}
}
}
// client_write(): write to server
void *client_write(int sockfd) {
char text[MAX_TEXT_SIZE + 2];
int c, d, i;
size_t text_len;
while (1) {
// clean out buffer
bzero(text, sizeof(text));
i = 0;
// read from stdin
while ((c = getchar()) != '\n' && c != EOF && i < MAX_TEXT_SIZE) {
text[i++] = c;
}
// clean out stdin if MAX_TEXT_SIZE exceeded
if (i == MAX_TEXT_SIZE && c != '\n') {
while ((d = getchar()) != EOF && d != '\n') {
}
}
text[i++] = '\n';
text[i] = '\0';
text_len = strlen(text);
// write to server
if (write(sockfd, text, text_len) != text_len) {
fprintf(stderr, "sent wrong number of bytes\n");
exit(1);
}
// quit conversation
if (strncmp(text, "bye\n", 4) == 0) {
fprintf(stdout, "end to conversation\n");
close(sockfd);
exit(0);
}
}
}
int main(int argc, char **argv) {
char keyword[MAX_KEY_SIZE + 1], *server_IP;
in_port_t server_port = 6000;
int r_val, rtn_val, sockfd, w_val;
pthread_t th_r, th_w;
struct sockaddr_in server_addr;
// checking number of command line arguments
if (argc != 3 && argc != 5) {
fprintf(stderr, "Usage: ./client server_IP [-p server_port] key\n");
exit(1);
}
// Usage: ./client server_IP key
else if (argc == 3) {
if (strlen(argv[2]) > MAX_KEY_SIZE) {
fprintf(stderr, "maximum key length exceeded\n");
exit(1);
}
else {
strcpy(keyword, argv[2]);
}
}
// Usage: ./client server_IP -p server_port key
else {
if (strcmp("-p", argv[2]) != 0) {
fprintf(stderr, "client: illegal option %s\n", argv[2]);
exit(1);
}
else {
server_port = atoi(argv[3]);
}
if (strlen(argv[4]) > MAX_KEY_SIZE) {
fprintf(stderr, "maximum key length exceeded\n");
exit(1);
}
else {
strcpy(keyword, argv[4]);
}
}
server_IP = argv[1];
// creating reliable stream socket using TCP
if ((sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
fprintf(stderr, "unable to create TCP socket\n");
exit(1);
}
// Constructing server address structure
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
// Converting address into proper format
if ((rtn_val = inet_pton(AF_INET, server_IP, &server_addr.sin_addr.s_addr)) == 0) {
fprintf(stderr, "invalid IP address\n");
exit(1);
}
else if (rtn_val < 0) {
fprintf(stderr, "failed to convert IP string\n");
exit(1);
}
// server port
server_addr.sin_port = htons(server_port);
// connecting to server
if (connect(sockfd, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
fprintf(stderr, "failed to connect to server\n");
exit(1);
}
// beginning chat
else {
fprintf(stdout, "successfully connected to server\n");
// starting separate thread for reading from server
if ((r_val = pthread_create(&th_r, NULL, (void *) client_read, (void *) sockfd)) != 0) {
fprintf(stderr, "failed to create read thread\n");
exit(1);
}
// starting separate thread for writing to server
if ((w_val = pthread_create(&th_w, NULL, (void *) client_write, (void *) sockfd)) != 0) {
fprintf(stderr, "failed to create write thread\n");
exit(1);
}
// Waiting until threads close to return resources
printf("hello1\n");
pthread_join(th_r, NULL);
printf("hello2\n");
pthread_join(th_w, NULL);
printf("hello3\n");
}
return 0;
}
For those of you familiar with client-server programming, you should see that besides the use of POSIX threads there is nothing out of the ordinary about the client code in terms of processing user input and reading and writing to a server. I decided to use a separate thread for reading and writing; otherwise, the client would have to write something, wait for the server to reply and then write something again.
I use the pthread_join() methods at the very end of main(). These functions are supposed to wait until the read (th_r) and write (th_w) threads to finish and relinquish the resources they use. The problem I have is that these methods are apparently never called.
When I start the client, I see it reaches "hello1" right away, but when either the client or server terminates the communication stream, I see neither "hello2" or "hello3," which means neither is ever reached.
Could someone please shed some light on what I am missing?
You aren't supposed to call exit
in your threads, you are supposed to return NULL
when the thread is finished. Calling exit
just terminates the whole program.