Thread: Mini chat

  1. #1
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302

    Mini chat

    is there a way to make this client/server program into a mini chat? As of now I got it to send and receives a single message at a time then close. Maybe I could loop the write and read functions?

    SERVER:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h> 
    #include <sys/socket.h>
    #include <netinet/in.h>
    
    void error(const char *msg)
    {
        perror(msg);
        exit(1);
    }
    
    int main(int argc, char *argv[])
    {
         int sockfd, newsockfd, portno, n;
         socklen_t clilen;
         char buffer[256];
         struct sockaddr_in serv_addr, cli_addr;
         if (argc < 2) {
             fprintf(stderr,"ERROR, no port provided\n");
             exit(1);
         }
         sockfd = socket(AF_INET, SOCK_STREAM, 0);
         if (sockfd < 0) 
            error("ERROR opening socket");
         bzero(&serv_addr, sizeof(serv_addr));
         portno = atoi(argv[1]);
         serv_addr.sin_family = AF_INET;
         serv_addr.sin_addr.s_addr = INADDR_ANY;
         serv_addr.sin_port = htons(portno);
         if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) 
                  error("ERROR on binding");
         listen(sockfd,5);
         clilen = sizeof(cli_addr);
         newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
         if (newsockfd < 0) 
              error("ERROR on accept");
         bzero(buffer,256);
         n = read(newsockfd,buffer,255);
         if (n < 0) error("ERROR reading from socket");
         printf("Here is the message: %s\n",buffer);
         n = write(newsockfd,"I got your message",18);
         if (n < 0) error("ERROR writing to socket");
         close(newsockfd);
         close(sockfd);
         return 0; 
    }
    CLIENT:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h> 
    
    void error(const char *msg)
    {
        perror(msg);
        exit(0);
    }
    
    int main(int argc, char *argv[])
    {
        int sockfd, portno, n;
        struct sockaddr_in serv_addr;
        struct hostent *server;
    
        char buffer[256];
        if (argc < 3) {
           fprintf(stderr,"usage %s hostname port\n", argv[0]);
           exit(0);
        }
        portno = atoi(argv[2]);
        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd < 0) 
            error("ERROR opening socket");
        server = gethostbyname(argv[1]);
        if (server == NULL) {
            fprintf(stderr,"ERROR, no such host\n");
            exit(0);
        }
        bzero((char *) &serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        bcopy((char *)server->h_addr, 
             (char *)&serv_addr.sin_addr.s_addr,
             server->h_length);
        serv_addr.sin_port = htons(portno);
        if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0) 
            error("ERROR connecting");
        printf("Please enter the message: ");
        bzero(buffer,256);
        fgets(buffer,255,stdin);
        n = write(sockfd,buffer,strlen(buffer));
        if (n < 0) 
             error("ERROR writing to socket");
        bzero(buffer,256);
        n = read(sockfd,buffer,255);
        if (n < 0) 
             error("ERROR reading from socket");
        printf("%s\n",buffer);
        close(sockfd);
        return 0;
    }

  2. #2
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    Maybe I could loop the write and read functions?
    Sounds like you just solved your problem.
    bit∙hub [bit-huhb] n. A source and destination for information.

  3. #3
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    I was thinking maybe I can use this while loop:

    CLIENT LOOP:
    Code:
    printf("ME: ");
        bzero(buffer,256);
        while (fgets(buffer,255,stdin) != NULL) {
        n = write(sockfd,buffer,strlen(buffer));
        if (n < 0) 
             error("ERROR writing to socket");
        bzero(buffer,256);
        n = read(sockfd,buffer,255);
        if (n < 0) 
             error("ERROR reading from socket");
        fputs(buffer,stdout);
        }
      close(sockfd);
      return 0;
      }
    SERVER LOOP:
    Code:
    do {
         bzero(buffer,256);
         n = read(newsockfd,buffer,255);
         if (n < 0) error("ERROR reading from socket");
         fprintf(stdout, "CLIENT: %s\n",buffer); /*start point of change. I write to server.
         It gets what I wrote, then prints the word CLIENT. Instead I need the server to 
         allow me to respond and then the client to wait for a response.*/
         n = write(newsockfd,"SERVER: ",18); /*When i send a message to this server, it sends back this message: server.*/
         if (n < 0) error("ERROR writing to socket");
       } while (buffer != NULL);
         close(newsockfd);
         close(sockfd);
         return 0; 
    }
    Last edited by Annonymous; 05-03-2011 at 11:21 PM.

  4. #4
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Annonymous View Post
    I was thinking maybe I can use this while loop:
    Looks like it should work, altho I'm not sure about your choice for the while condition in the server loop.

    Once you get that down, another challenge is to make the server work with several clients simultaneously via select() polling.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  5. #5
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    IT kind of works MK27. The client loops fine, it's the server that is giving me a problem. I can write an infintie amount of messages on the client side to the server. The server can "RECEIVE" an unlimited amount but can not send back. That is the part I am having trouble with. And the while loop condition is working so I'm just going to leave it. Don't fix what's not broken!
    Last edited by Annonymous; 05-04-2011 at 07:03 AM.

  6. #6
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Annonymous View Post
    IT kind of works MK27. The client loops fine, it's the server that is giving me a problem. I can write an infintie amount of messages on the client side to the server. The server can "RECEIVE" an unlimited amount but can not send back.
    Network code can be fickle. Read() is a blocking call tho, so the client is getting something. Maybe in place of this:

    Code:
    fputs(buffer,stdout);
    You could try this:

    Code:
    fprintf(stderr, "%d bytes: -->%s<--\n", n, buffer);
    This gives you a little more reporting, and it prints to stderr, which is unbuffered; ie, it is output immediately. Stdout is buffered, meaning the system actually accumulates data into it before it prints it out. You can probably see that this way:

    Code:
    #include <stdio.h>
    
    int main(void) {
    	int i;
    	for (i = 0; i < 5; i++) {
    		fprintf(stderr,"stderr %d -- ", i);
    		fprintf(stdout,"stdout %d -- ", i);
    	} 
    
    	return 0;
    }
    I get:

    stderr 0 -- stderr 1 -- stderr 2 -- stderr 3 -- stderr 4 -- stdout 0 -- stdout 1 -- stdout 2 -- stdout 3 -- stdout 4 --

    All the stderrs appear before any of the stdouts. Add a newline to the end of the stdout one:

    Code:
    		fprintf(stderr,"stderr %d -- ", i); 
    		fprintf(stderr,"stdout %d -- \n", i);
    stderr 0 -- stdout 0 --
    stderr 1 -- stdout 1 --
    stderr 2 -- stdout 2 --
    stderr 3 -- stdout 3 --
    stderr 4 -- stdout 4 --


    Now they are in the expected sequence, heh-heh. Try a newline on stderr, then on both. This behavior is not certain, but it hopefully demonstrates the difference between "buffered" and "unbuffered". You can force output from a buffered output stream with fflush():

    Code:
    		fprintf(stderr,"stderr %d -- ", i); 
    		fprintf(stdout,"stdout %d -- ", i);  
    		fflush(stdout);
    stderr 0 -- stdout 0 -- stderr 1 -- stdout 1 -- stderr 2 -- stdout 2 -- stderr 3 -- stdout 3 -- stderr 4 -- stdout 4 --

    I believe socket I/O is also buffered. However, you cannot fflush() an input stream. But what you can do is add a newline at the end of your transmission:

    Code:
    n = write(newsockfd,"SERVER: \n", 10);
    Nb. I also changed the second parameter to match the length of the first one. In the client, you are using a char array that really is 255 characters long, but here you are using a string literal which is only 9 characters long. This is from the GNU docs for wrtie() which I presume follow the standard:

    The write function writes up to size bytes from buffer to the file with descriptor filedes. The data in buffer is not necessarily a character string and a null character is output like any other character.
    Ie, when you do that, you are sending that entire 255 characters including the junk at the end. The reason you don't see it from the server is that fprintf stops at the null terminator, which is at the end of the string at the beginning of those 255 bytes. So using "strlen(buffer)+1" will be more efficient. Of course, the same is true for "read()" at the server, you can play around with this and see what happens to the value for "n" there. It is not such a big deal at the receiver; what's important to consider is that actual transmission is a bottleneck in networking.

    That's just tweaking, but I would say that supplying write with a 10 byte string and telling it to write 18 bytes is a genuine access violation, and could (but won't necessarily) produce a seg fault.

    That is the part I am having trouble with. And the while loop condition is working so I'm just going to leave it. Don't fix what's not broken!
    That specifically is no big deal, but be careful with that attitude: just because something seems to work right now does not mean it was done correctly. Potential seg faults like the one I just mentioned can show up at any time, and also tend to appear suddenly when you compile your code on a slightly different machine/compiler (eg, you write the code on a 32-bit machine, think "it's not broken", then it breaks on 64-bit because the compiler has arranged things differently, and your previously harmless access violation is now a real problem).

    The deal with read() and write() is they are "low level" routines that need to be wrapped in another function to be most useful, possibly involving some kind of impromptu protocol (akin to http, which is a networking protocol that includes a Content-Length: field with all messages, maybe that gives you a clue about how to set parameters for read()). So you could use read() and write() inside custom functions to implement such networking protocols. They do not have to be anything as complex as HTTP, of course, and the simpler the better. But I gotta run now (or bike, in fact) ...have fun
    Last edited by MK27; 05-04-2011 at 08:34 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  7. #7
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    Thank you MK27. That was quite a bit of information but very informative! Much appreciated!

  8. #8
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    So I've spent my whole day trying to tackle this project. I have looped the read write functions in both the client and server. But the only problem is that the program is freaking syncronous. Is there a function that I am missing? A function I should be reading up on?

    I got rid of the do while loops.They were allowing me to chat back and forth 2 or 3 times. Though they did not loop the read and write functions to allow unlimited communication. Nothing a little for loop can't change!

    SERVER:
    Code:
      for( ; ; ) {
         int i = sizeof(buffer); if (i > 255) bzero(buffer, 256);
         n = read(newsockfd,buffer,sizeof(buffer));
         if (n < 0) error("ERROR reading from socket");
         fprintf(stdout, "CLIENT: %s\n",buffer); 
    
         i = sizeof(buffer); if (i > 255) bzero(buffer, 256);
         fprintf(stdout, "ME: ");
         fgets(buffer, 255, stdin);
         n = write(newsockfd, buffer, strlen(buffer)+1);
         if (n < 0)
             error("Error has occured.");
         }
         close(newsockfd);
         close(sockfd);
         return 0; 
    }
    CLIENT:
    Code:
    for ( ; ; ) {
        int i = sizeof(buffer); if (i > 255) bzero(buffer, 256);
        fprintf(stdout, "ME: ");
        fgets(buffer,255,stdin); 
        n = write(sockfd,buffer,strlen(buffer)+1);
        if (n < 0) 
             error("ERROR writing to socket");
        i = sizeof(buffer); if (i > 255) bzero(buffer, 256);
            n = read(sockfd,buffer,sizeof(buffer));
        if (n < 0) 
             error("ERROR reading from socket");
        fprintf(stdout, "SERVER: %s\n",buffer);
        }
      close(sockfd);
      return 0;
      }

    BTW MK27, in my read function i replaced the 3rd argument of 256 with sizeof(buffer). Don't know if that's a fix for sending all 256 bytes, and instead only sending what was written to the buffer like you said. Hopefully it was. I also bzero'd both the buffers in the read function/write functions.


    well I thought I had my answer but it seems I'm actually kind of confused as how to make the socket asynchronous.
    Last edited by Annonymous; 05-04-2011 at 09:24 PM.

  9. #9
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    If you're dealing with multiple descriptors, you need to start looking at select()

    select_tut(2): synchronous I/O multiplexing - Linux man page
    Experiment with this, then think how you would use these ideas in your program.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  10. #10
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Annonymous View Post
    So I've spent my whole day trying to tackle this project. I have looped the read write functions in both the client and server. But the only problem is that the program is freaking syncronous. Is there a function that I am missing? A function I should be reading up on?
    WRT the server, there are two ways. One is to use select(), but remove the user input within the server code. Instead, you would have two or more clients connect and the server would just pass messages on. This would be the most conventional method best reflecting what a "server" is and does.

    The other is to use non-blocking mode, in which case the server would work identically to a client. This is not such a good idea, because it makes handling multiple clients awkward, and so limits the potential of the server. It is, however, how you would make the client asynchronous. I think you will find it is much easier to write a server that uses select(), and attaching a second client to reply to the first client, than trying to arrange a non-blocking server that can reply to a non-blocking client.


    To set non-blocking mode on a socket, use this in your client code:

    Code:
    fcntl(sock_fd, F_SETFL,O_NONBLOCK);
    "sock_fd" is the int file descriptor returned by socket(). In non-blocking mode, a read() call will not wait for data; it will just return -1 if there is no incoming message waiting.

    That means you can transmit asynchronously -- you do not have to wait for a reply. However, you can't receive asynchronously (which is another reason doing the server this way is a bad idea) because the client will be waiting for input from the user (I have not tried setting stdin NONBLOCK but am sure that is not a good way to go).

    The only way to get around that is to use fork() or threads for reception. Fork() is much easier, and sufficient (ie, threading is not necessary). You should do some research and experimentation with fork() on it's own -- that is, in a separate program -- before you try and use it here, because it will lead to hopeless confusion otherwise and take you longer to work out.

    The complication with that in a CLI client is that this will happen:
    Code:
    > I'm typin
    incoming message: hi!
    g a message...
    Which is irritating, but when you hit return the red stuff from stdout is not included (you transmit "I'm typing a message..."), so it can work.

    I got rid of the do while loops.They were allowing me to chat back and forth 2 or 3 times. Though they did not loop the read and write functions to allow unlimited communication. Nothing a little for loop can't change!
    Hmmm, do you understand why the for loop works where the while loop doesn't? If not you should try to. The obvious clue will be your condition. Your for doesn't have one, meaning it is the same thing as a "while(1)" loop. Ie, this is not the difference between for and while, it's the difference between one condition and another (or lack there of).

    There is nothing wrong with infinite loops like that, btw, since they can be exited via "break".

    BTW MK27, in my read function i replaced the 3rd argument of 256 with sizeof(buffer). Don't know if that's a fix for sending all 256 bytes, and instead only sending what was written to the buffer like you said. Hopefully it was.
    Well, there is an easy way to check that:
    Code:
    printf("%d\n", sizeof(buffer));
    Which, depending on whether buffer was created statically (on the stack) or malloc'd (on the heap, hence is a pointer), will demonstrate that your guess is either harmlessly wrong or fatally wrong. Checking stuff like this may seem tedious, but it is a required activity of successful programming! If you aren't already, it is good to use short, dedicated programs for "premise testing" at the same time as you are working on a project, so that you don't have to "premise test" in a large complex piece of code as much (that's what I'm recommending WRT fork(), and please please please take that advice).

    There's a reason I recommended strlen() for getting the size of the data in the buffer, hopefully you can figure out why
    Last edited by MK27; 05-05-2011 at 09:35 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  11. #11
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by MK27 View Post
    (I have not tried setting stdin NONBLOCK but am sure that is not a good way to go).
    Or maybe it is. Try this:

    Code:
    #include <stdio.h>
    #include <fcntl.h>
    #include <unistd.h>
    
    int main(void) {
    	ssize_t bytes;
    	char buffer[256];
    	int count = 0;
    
    	printf("Enter: ");
    	fflush(stdout);
    	fcntl(0,F_SETFL,O_NONBLOCK);
    	while (1) {
    		bytes = read(0,buffer,256);
    		if (bytes > 0) {
    			printf("%d seconds elapsed\nYou entered: %sEnter: ", count, buffer);
    			fflush(stdout);
    		}
    		sleep(1);
    		count++;
    		if (!(count%5)) puts("\nhey there!");
    	}
    
    
    	return 0;
    }
    The sleep(1) is pretty important. You need some kind of delay in a non-blocking read loop (one more reason doing the server that way is a bad idea), or the process will hog the CPU completely. If you want to use a less noticable delay, you need to use nanosleep, qv:

    SourceForge.net: POSIX timers - cpwiki

    Something like 1/20th of a second is fine -- it is unnoticeable and still plenty of time to prevent the process from hogging resources pointlessly.

    If you can work the client out that way, you will not have to use fork(). Even if you do use fork, that delay is still important in the read loop.
    Last edited by MK27; 05-05-2011 at 09:54 AM.
    C programming resources:
    GNU C Function and Macro Index -- glibc reference manual
    The C Book -- nice online learner guide
    Current ISO draft standard
    CCAN -- new CPAN like open source library repository
    3 (different) GNU debugger tutorials: #1 -- #2 -- #3
    cpwiki -- our wiki on sourceforge

  12. #12
    Registered User Annonymous's Avatar
    Join Date
    Apr 2011
    Location
    Jackson, New Jersey, United States
    Posts
    302
    Yeah Salem, I was looking into select and it seems a little complex. So I tried fork, and even that seemed a bit complicated! But def the lesser of the 2! It's not for work or school, just a hobby so I have no time constraints. But I will take MK27's advice and not use the fcntl() function. I read up on that and it seems like a bad idea! But I haven't been messing around with the mini chat project because I just rebuilt my dirtbike and it's riding season baby! But man I had no idea network programming could be so complex!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Mini If
    By Milhas in forum C Programming
    Replies: 4
    Last Post: 03-27-2008, 04:04 PM
  2. Mini-screens
    By pianorain in forum Tech Board
    Replies: 2
    Last Post: 07-25-2004, 11:21 PM
  3. New Mini Project
    By jverkoey in forum Game Programming
    Replies: 3
    Last Post: 05-12-2004, 11:00 PM
  4. mini-ITX stealth computers
    By major_small in forum Tech Board
    Replies: 5
    Last Post: 10-28-2003, 10:19 PM
  5. mini-itx
    By whistlenm1 in forum A Brief History of Cprogramming.com
    Replies: 4
    Last Post: 06-18-2003, 03:58 PM