Thread: Multiple clients to "internet" server

  1. #1
    Registered User
    Join Date
    Apr 2008
    Posts
    40

    Multiple clients to "internet" server

    Hello!

    I am trying to create a sort of chat program as an exercise. I have an example from a book showing how to create a connectionless server and a connectionless client with internet sockets, but I want to be able to connect with multiple clients and do it so that any client can write at any time and the message is sent to all other clients.

    I've tried connecting with two clients to the same server, and the result is that the server gets messages from both clients, but the clients only get every other message from the server (i.e. the server seems to send to the client that sent a message last).

    Anyone who knows how to fix this? Do I need one thread/process per client and have some sort of interprocess communication between the server threads? Or maybe multiple sockets?

    I've also found that part of the solution is something called non-blocking io which is also in this book, but I don't have the include "filio.h" which might be a UNIX-specific include. Anyone who knows what the equivalent is in linux (Xubuntu 8.04)?

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    FWIW:
    This program acts like a simple multi-user chat server. Start it running in one window, then telnet to it ("telnet hostname 9034") from multiple other windows. When you type something in one telnet session, it should appear in all the others.

    Code:
    /*
    ** selectserver.c -- a cheezy multiperson chat server
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <netdb.h>
    
    #define PORT "9034"   // port we're listening on
    
    // get sockaddr, IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa)
    {
        if (sa->sa_family == AF_INET) {
            return &(((struct sockaddr_in*)sa)->sin_addr);
        }
    
        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }
    
    int main(void)
    {
        fd_set master;    // master file descriptor list
        fd_set read_fds;  // temp file descriptor list for select()
        int fdmax;        // maximum file descriptor number
    
        int listener;     // listening socket descriptor
        int newfd;        // newly accept()ed socket descriptor
        struct sockaddr_storage remoteaddr; // client address
        socklen_t addrlen;
    
        char buf[256];    // buffer for client data
        int nbytes;
    
        char remoteIP[INET6_ADDRSTRLEN];
    
        int yes=1;        // for setsockopt() SO_REUSEADDR, below
        int i, j, rv;
    
        struct addrinfo hints, *ai, *p;
    
        FD_ZERO(&master);    // clear the master and temp sets
        FD_ZERO(&read_fds);
    
        // get us a socket and bind it
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_flags = AI_PASSIVE;
        if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
            fprintf(stderr, "selectserver: &#37;s\n", gai_strerror(rv));
            exit(1);
        }
        
        for(p = ai; p != NULL; p = p->ai_next) {
            listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
            if (listener < 0) { 
                continue;
            }
            
            // lose the pesky "address already in use" error message
            setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
    
            if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
                close(listener);
                continue;
            }
    
            break;
        }
    
        // if we got here, it means we didn't get bound
        if (p == NULL) {
            fprintf(stderr, "selectserver: failed to bind\n");
            exit(2);
        }
    
        freeaddrinfo(ai); // all done with this
    
        // listen
        if (listen(listener, 10) == -1) {
            perror("listen");
            exit(3);
        }
    
        // add the listener to the master set
        FD_SET(listener, &master);
    
        // keep track of the biggest file descriptor
        fdmax = listener; // so far, it's this one
    
        // main loop
        for(;;) {
            read_fds = master; // copy it
            if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
                perror("select");
                exit(4);
            }
    
            // run through the existing connections looking for data to read
            for(i = 0; i <= fdmax; i++) {
                if (FD_ISSET(i, &read_fds)) { // we got one!!
                    if (i == listener) {
                        // handle new connections
                        addrlen = sizeof remoteaddr;
                        newfd = accept(listener, (struct sockaddr *)&remoteaddr,
                            &addrlen);
    
                        if (newfd == -1) {
                            perror("accept");
                        } else {
                            FD_SET(newfd, &master); // add to master set
                            if (newfd > fdmax) {    // keep track of the maximum
                                fdmax = newfd;
                            }
                            printf("selectserver: new connection from %s on "
                                "socket %d\n",
                                inet_ntop(remoteaddr.ss_family,
                                    get_in_addr((struct sockaddr*)&remoteaddr),
                                    remoteIP, INET6_ADDRSTRLEN),
                                newfd);
                        }
                    } else {
                        // handle data from a client
                        if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
                            // got error or connection closed by client
                            if (nbytes == 0) {
                                // connection closed
                                printf("selectserver: socket %d hung up\n", i);
                            } else {
                                perror("recv");
                            }
                            close(i); // bye!
                            FD_CLR(i, &master); // remove from master set
                        } else {
                            // we got some data from a client
                            for(j = 0; j <= fdmax; j++) {
                                // send to everyone!
                                if (FD_ISSET(j, &master)) {
                                    // except the listener and ourselves
                                    if (j != listener && j != i) {
                                        if (send(j, buf, nbytes, 0) == -1) {
                                            perror("send");
                                        }
                                    }
                                }
                            }
                        }
                    } // END handle data from client
                } // END got new incoming connection
            } // END looping through file descriptors
        } // END for(;;)--and you thought it would never end!
        
        return 0;
    }
    From -- where else? -- Beej's Guide to Network Programming.
    http://www.beej.us/guide/bgnet/outpu...et.html#select
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  3. #3
    spurious conceit MK27's Avatar
    Join Date
    Jul 2008
    Location
    segmentation fault
    Posts
    8,300
    Quote Originally Posted by Zarniwoop View Post
    I've also found that part of the solution is something called non-blocking io which is also in this book, but I don't have the include "filio.h" which might be a UNIX-specific include. Anyone who knows what the equivalent is in linux (Xubuntu 8.04)?
    You can set non-blocking mode on a file descriptor with

    Code:
    fcntl(fd,F_SETFL,O_NONBLOCK);
    All you need is fcntl.h. See section 13.14.3 in the GNU C Manual.
    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

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. TCP Sockets: multiple clients - one server
    By Printisor in forum C Programming
    Replies: 4
    Last Post: 11-01-2007, 10:34 AM
  2. MISO Soup: Multiple Clients and one Server
    By doubleanti in forum Networking/Device Communication
    Replies: 2
    Last Post: 07-24-2007, 02:29 AM
  3. Socket Help - Multiple Clients
    By project95talon in forum C Programming
    Replies: 5
    Last Post: 11-17-2005, 02:51 AM
  4. Server with multiple clients
    By Thantos in forum Networking/Device Communication
    Replies: 20
    Last Post: 09-02-2003, 05:52 PM
  5. server with multiple clients
    By Unregistered in forum C Programming
    Replies: 1
    Last Post: 02-23-2002, 09:15 PM