Chatty Kathy (Chat program in C)

Thread Solved

Join Date: Oct 2008
Posts: 40
Reputation: JimD C++ Newb is an unknown quantity at this point 
Solved Threads: 0
JimD C++ Newb JimD C++ Newb is offline Offline
Light Poster

Chatty Kathy (Chat program in C)

 
0
  #1
Apr 20th, 2009
Good afternoon, all!

I'm in the process of writing a chat program. The server is completed, and if I simply telnet into the server with 2 different windows, 2 people can chat in fluid fashion.

However, when I connect 2 clients, the chat appears in lock-step fashion. (ie. Client 1 types a msg to Client 2, but Client 2 doesn't see the msg until Client2 types a message first.)

Here is a snippet of the code. Any pointers would be great!

And thank you in advance for any assistance!

- Jim

  1. // Open an unbound TCP socket
  2.  
  3. if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  4. pdie("Opening stream socket");
  5.  
  6.  
  7. // Prepare for connection to server
  8.  
  9. bzero((char *) &server, sizeof(server));
  10. server.sin_family = AF_INET;
  11. if ((hp = gethostbyname(argv[1])) == NULL)
  12.  
  13. {
  14. sprintf(buf, "%s: unknown host\n", argv[1]);
  15. die(buf);
  16. }//end preparation
  17.  
  18. bcopy(hp->h_addr, &server.sin_addr, hp->h_length);
  19. server.sin_port = htons((u_short) SERVER_PORT);
  20.  
  21. // Establish connection to server
  22.  
  23. if (connect(sock, (struct sockaddr *) &server, sizeof(server)) <
  24. 0)
  25. pdie("Connecting stream socket");
  26.  
  27. // What socket number am I talking on?
  28.  
  29. clientLen = sizeof(client);
  30. if (getsockname(sock, (struct sockaddr *) &client, &clientLen))
  31. pdie("Getting socket name");
  32.  
  33. if (clientLen != sizeof(client))
  34. die("getsockname() overwrote name structure");
  35.  
  36. printf("Client socket has port %hu\n", ntohs(client.sin_port));
  37.  
  38.  
  39. int name = ntohs(client.sin_port);
  40. printf("Now the client name is %d\n\n", name);
  41. gets (msg);
  42.  
  43. while (msg != "q")
  44. {
  45. /* Write out message. */
  46. if (write(sock, msg, sizeof(msg)) < 0)
  47. pdie("Writing on stream socket");
  48.  
  49. /* Prepare buffer and read from it. */
  50. bzero(buf, sizeof(buf));
  51. if (read(sock, buf, BUFFER_SIZE) < 0)
  52. pdie("Reading stream message");
  53.  
  54. printf("User %d says: %s\n", name, buf);
  55.  
  56. gets(msg);
  57. }
  58. close(sock);
  59.  
  60.  
  61. exit(0);
  62.  
  63. }
Reply With Quote Quick reply to this message  
Join Date: May 2008
Posts: 538
Reputation: Murtan is a jewel in the rough Murtan is a jewel in the rough Murtan is a jewel in the rough Murtan is a jewel in the rough 
Solved Threads: 86
Murtan Murtan is offline Offline
Posting Pro

Re: Chatty Kathy (Chat program in C)

 
1
  #2
Apr 21st, 2009
Not surprisingly, but your program is doing what you told it to:

  1. // Wait here for the user to enter a message
  2. gets (msg);
  3.  
  4. while (msg != "q")
  5. {
  6. /* Write out message. */
  7. if (write(sock, msg, sizeof(msg)) < 0)
  8. pdie("Writing on stream socket");
  9.  
  10. /* Prepare buffer and read from it. */
  11. bzero(buf, sizeof(buf));
  12. if (read(sock, buf, BUFFER_SIZE) < 0)
  13. pdie("Reading stream message");
  14.  
  15. printf("User %d says: %s\n", name, buf);
  16.  
  17. // Wait here for the user to enter a message
  18. gets(msg);
  19. }

If you're wanting the client to be both waiting for user input and watching for messages from the server (other client) you will need to implement some form of multi-threading.

One implementation might have a thread that supports the socket and the main display of the chat. (It would add lines to the window as they were received or sent.) Another thread would support the user input, it would wait for the user to enter some data (without blocking the other thread) and then send the message the user entered to the other thread for sending to the server and adding to the chat window.

As an alternative to 'actual' multi-threading, you might be able to do something with a 'main loop' that would check for user input and check for socket data repeatedly. Something like the following:
  1. loop forever
  2. if there is user input data available:
  3. if the user input is an 'enter':
  4. if the input buffer contains 'q':
  5. break the loop
  6. send the input buffer
  7. else if the user input is a 'backspace':
  8. if the input buffer is not empty:
  9. remove the last character from the input buffer
  10. else if the user input is a printable character:
  11. add the character to the input buffer
  12. if the socket is closed:
  13. break the loop
  14. if the socket has data:
  15. read the data from the socket
  16. display the data to the user
Reply With Quote Quick reply to this message  
Join Date: Oct 2008
Posts: 476
Reputation: nucleon has a spectacular aura about nucleon has a spectacular aura about 
Solved Threads: 91
nucleon's Avatar
nucleon nucleon is offline Offline
Posting Pro in Training

Re: Chatty Kathy (Chat program in C)

 
0
  #3
Apr 21st, 2009
You do not need multi-threading for this task.

Your program proceeds in lockstep because your I/O calls are of the "blocking" type and you are calling them without knowing that they are "ready". If they are not ready, they'll wait until something is ready to return. This includes fgets, which waits until the Enter key is pressed to return (line-buffered input).

You have 4 I/O statements in the loop, a simplified version of which appears below (error handling removed):
  1. for ( ; ; ) {
  2.  
  3. gets ( msg ); /* IO_1 */
  4. if ( strcmp ( msg, "q" ) == 0 ) break;
  5.  
  6. write ( ... ); /* IO_2 */
  7.  
  8. read ( ... ); /* IO_3 */
  9. printf ( ... ); /* IO_4 */
  10. }
Instantly, the gets() blocks reading from the terminal, waiting for an Enter keypress. If the other client had sent us a message, we won't see it until we send our message. To fix that you must read the keyboard in a non-blocking manner and yet avoid active polling.

You avoid active polling with the select() function, which will block until one of a given set of file descriptors are "ready". Blocking is good because it means you're not looping and testing (active polling), which is worse than wasteful in a multitasking environment! select() allows you to block on a set of file descriptors, waiting until any one of them is ready. (I don't know how standardized select() is, but it should perform the same basic function on different systems.)

For keyboard input, just read a character at a time using getchar() and respond to the newline character by calling write. The STDIN_FILENO file descriptor would be part of the set passed to select(), so you will know that a character is there to read.
Reply With Quote Quick reply to this message  
Join Date: Oct 2008
Posts: 476
Reputation: nucleon has a spectacular aura about nucleon has a spectacular aura about 
Solved Threads: 91
nucleon's Avatar
nucleon nucleon is offline Offline
Posting Pro in Training

Re: Chatty Kathy (Chat program in C)

 
0
  #4
Apr 21st, 2009
I was forgetting that you've already written the server, so you must know about select (?). In the client, you can try using select the same way, adding STDIN_FILENO to the fds, but it is system dependent whether that will work. If it doesn't, there may be some other system dependent way to do it (like setting it up so a signal is sent every keystroke, interrupting the select).

Alternatively, the threaded solution is actually very clean. The "keyboard" thread reads keys, sending its buffer to the "main" thread (either when enter is pressed or even char by char) which communicates with the server.
Reply With Quote Quick reply to this message  
Join Date: Oct 2008
Posts: 40
Reputation: JimD C++ Newb is an unknown quantity at this point 
Solved Threads: 0
JimD C++ Newb JimD C++ Newb is offline Offline
Light Poster

Re: Chatty Kathy (Chat program in C)

 
0
  #5
Apr 21st, 2009
I've can't seem to get the select() in the Client to work. I was looking at multithreading in C, but it seems a bit fuzzy to me.

http://softpixel.com/~cwright/progra.../threads.c.php

was one of the places I looked. Maybe I'm making it out to be more difficult than it needs to be?

- Jim
Reply With Quote Quick reply to this message  
Join Date: Oct 2008
Posts: 476
Reputation: nucleon has a spectacular aura about nucleon has a spectacular aura about 
Solved Threads: 91
nucleon's Avatar
nucleon nucleon is offline Offline
Posting Pro in Training

Re: Chatty Kathy (Chat program in C)

 
1
  #6
Apr 21st, 2009
Basically you just say "start a new thread of execution here". It shares the same address space as your main (or original) line of execution, which can be handy. How about something like this:
  1.  
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <sys/types.h>
  5. #include <sys/time.h>
  6. #include <pthread.h>
  7.  
  8.  
  9. void*
  10. KeyboardThread (void *unused)
  11. {
  12. printf ("In keyboard thread\nPress Enter...");
  13. getchar();
  14. printf ("Exiting keyboard thread\n:);
  15. return NULL; /* Or pthread_exit(NULL) */
  16. }
  17.  
  18.  
  19. pthread_t
  20. StartKeyboardThread (void)
  21. {
  22. pthread_t thread;
  23. if (pthread_create (&thread, /* Thread id */
  24. NULL, /* Default attributes */
  25. KeyboardThread, /* Thread start routine */
  26. NULL) /* Arg to start routine */
  27. != 0)
  28. {
  29. perror ("StartKeyboardThread");
  30. exit (-1);
  31. }
  32. return thread;
  33. }
  34.  
  35.  
  36. int
  37. main (void)
  38. {
  39. /* To start it */
  40. pthread_t thread = StartKeyboardThread();
  41.  
  42. /* And when you want to kill it */
  43. pthread_cancel (thread);
  44.  
  45. return 0;
  46. }
  47.  
Reply With Quote Quick reply to this message  
Join Date: Oct 2008
Posts: 40
Reputation: JimD C++ Newb is an unknown quantity at this point 
Solved Threads: 0
JimD C++ Newb JimD C++ Newb is offline Offline
Light Poster

Re: Chatty Kathy (Chat program in C)

 
0
  #7
Apr 22nd, 2009
I finally got the select() syntax right! Thank you all for the wonderful pointers!

After the project is complete, I'd like to go back and try to redo it with the multi-thread idea. That sounds like a lot of fun to learn.

- Jim
Reply With Quote Quick reply to this message  
Reply

This thread has been marked solved.
Perhaps start a new thread instead?
Message:


Thread Tools Search this Thread



About Us | Contact Us | Advertise | DaniWeb | Acceptable Use Policy | RSS Feed

©2003 - 2009 DaniWeb® LLC