Thread: Chat server not using select

  1. #1
    Registered User
    Join Date
    Jul 2012
    Posts
    1

    Chat server not using select

    I have a problem creating a chat server which should respond to multiple clients. A message from one client should be sent to all the other clients connected to the server.

    The processes that handless the connections to the clients are stored in a double linked list (lnode). That list is only reachable from one process. Another process is handling all messages that arrive to the server. My problem is that when the process that is handling the messages is going to send to the clients it does not know about the double linked list.

    What I want now is that every time the double linked list is altered the new list should be available to the message handling process. I have tried ending it through a fifo que, i have tried shared memory and I have tried to pipe it. It all ends up in disaster.

    Can any one give me a hint on how to share a double linked list between processes in C. If anyone has a more clever way of dealing with this issue I will gladly consider that alternative.


    The code so far: (I hope i get the code tags right)


    Code:
    /*
    ** server.c
    */
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <arpa/inet.h>
    #include <sys/wait.h>
    #include <signal.h>
    #include <sys/msg.h>
    
    #define PORT "3490"      // the port users will be connecting to
    #define BACKLOG 10         // how many pending connections queue will hold
    #define SHM_SIZE 1024      // make it a 1K shared memory segment
    #define QUEKEY 101
    
    void sigchld_handler(int s)
    {
        while(waitpid(-1, NULL, WNOHANG) > 0);
    }
    
    // signal(SIGCHLD, SIG_IGN);            // So i dont have to use wait();
    
    // 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);
    }
    
        struct node {
            int client;
            int nodeId;
            struct node *next;
            struct node *prev;
        };
        
        struct node *first, *last;
        
        //Struct to be used with message
        struct myMsg {
            long mtype;
            char mtext[200];
        };
        
        //Function Prototypes
        void myabort(char *);
        void append_node(struct node *lnode);
        void insert_node(struct node *lnode, struct node *after);
        void remove_node(struct node *lnode);
        void sendAll(char *);
    
    int main(void)
    {
        int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
        struct addrinfo hints, *servinfo, *p;
        struct sockaddr_storage their_addr; // connector's address information
        socklen_t sin_size;
        struct sigaction sa;
        int yes=1;
        char s[INET6_ADDRSTRLEN];
        int rv,n;
        char tmp[100];
        int childcount = 0;
        struct node *lnode;                    //This is the doubly linked list that i will use to store the sockets in
        first=NULL;
        int msqid;
        struct myMsg buf;
    
                            
        
        //Creating message
            /* make the key: */
        if ((key = ftok("text", 'R')) == -1) {    //Creats a "random" key
            perror("ftok");
            exit(1);
        }
        
        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_flags = AI_PASSIVE; // use my IP
    
        if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
            return 1;
        }
    
        // loop through all the results and bind to the first we can
        for(p = servinfo; p != NULL; p = p->ai_next) {
            if ((sockfd = socket(p->ai_family, p->ai_socktype,
                    p->ai_protocol)) == -1) {
                perror("server: socket");
                continue;
            }
    
            if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                    sizeof(int)) == -1) {
                perror("setsockopt");
                exit(1);
            }
    
            if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
                close(sockfd);
                perror("server: bind");
                continue;
            }
    
            break;
        }
    
        if (p == NULL)  {
            fprintf(stderr, "server: failed to bind\n");
            return 2;
        }
    
        freeaddrinfo(servinfo); // all done with this structure
    
        if (listen(sockfd, BACKLOG) == -1) {
            perror("listen");
            exit(1);
        }
    
        sa.sa_handler = sigchld_handler; // reap all dead processes
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = SA_RESTART;
        if (sigaction(SIGCHLD, &sa, NULL) == -1) {
            perror("sigaction");
            exit(1);
        }
        if (fork()==0){                                 //This is the child that will be use to receive message from all the clients
            printf ("Mottagar processen snurrar\n");
            if ((msqid = msgget(QUEKEY, 0666 | IPC_CREAT)) == -1) {        //Creats the message que
                perror("msgget");
                exit(1);
            }    
            while(1){
    
                if (msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0) == -1) {
                    perror("msgrcv");
                    exit(1);
                }
                printf("Detta är skrivet från Parrent %s\n",buf.mtext);
            }
        }
        printf("server: waiting for connections...\n");
    
        while(1) {  // main accept() loop
            sin_size = sizeof their_addr;
            new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
            printf("new_fd är %d\n",new_fd);
            if (new_fd == -1) {
                perror("accept");
                continue;
            }
    
            inet_ntop(their_addr.ss_family,
                get_in_addr((struct sockaddr *)&their_addr),
                s, sizeof s);
            printf("server: got connection from %s\n", s);
    
            if (fork()==0) {                                 // this is the child process
                if ((msqid = msgget(QUEKEY, 0666 | IPC_CREAT)) == -1) {        //Creats the message que
                    perror("msgget");
                    exit(1);
                }
                close(sockfd);                                 // child doesn't need the listener
                if (send(new_fd, "Hello, world!", 13, 0) == -1)
                    perror("send");
    
                buf.mtype=1;                        //It took a lot of troubleshooting before i realized that this had to be set
                while (1){ //read string from client
    
                    bzero(&tmp, sizeof(tmp));                // empty tmp
                    n = read(new_fd,(char *) &tmp, 100);    // Reads a line from the client
                    if (n == -1) 
                        perror("read()");
                    tmp[n] = '\0';
                    printf("client processen har tagit emot %s\n",tmp);
                    strcpy(buf.mtext,tmp);
                    int len = strlen(buf.mtext);
                    printf("client processen försöker sända %s med länden %d till kön\n",buf.mtext,len);
                    
                    if (msgsnd(msqid, &buf, len+1, 0) == -1)                             
                        perror("msgsnd");
    
                }
            }
            //Parrent process
    
            lnode = (struct node *)malloc(sizeof(struct node));            //Make room for a node
            lnode->client = new_fd;                                        //populaate the node with some data
            lnode->nodeId=childcount;
            append_node(lnode);                                            //append node to list
            for(lnode = first; lnode != NULL; lnode = lnode->next) {
            if (write(lnode->client, "Nu anslöt en klient \n", 20) == -1)
                perror("send");
            }
            childcount++;
    
    
            for(lnode = first; lnode != NULL; lnode = lnode->next) {
            printf("hepp\n");
            if (write(lnode->client, &buf, 20) == -1)
                perror("send");
            }
        }
    
        return 0;
    }
    void append_node(struct node *lnode) {
        printf ("Nu lägger vi till en node i listan den har lnode->client %d\n",lnode->client);
        if(first == NULL) {
            first = lnode;
            if (first==NULL)
                printf("........ in append\n");
            lnode->prev = NULL;
        } else {
            last->next = lnode;
            lnode->prev = last;
        }
        last = lnode;
        lnode->next = NULL;
    }
    
    void insert_node(struct node *lnode, struct node *after) {
        lnode->next = after->next;
        lnode->prev = after;
        if(after->next != NULL)
            after->next->prev = lnode;
        else
            last = lnode;    
        after->next = lnode;
    }
    
    void remove_node(struct node *lnode) {
        if(lnode->prev == NULL)
            first = lnode->next;
        else
            lnode->prev->next = lnode->next;
        if(lnode->next == NULL)
            last = lnode->prev;
        else
            lnode->next->prev = lnode->prev;
    }

  2. #2
    Registered User
    Join Date
    May 2012
    Location
    Arizona, USA
    Posts
    945
    Let me see if I understand your server correctly. It creates a new child process to handle each new connection, but currently those child processes cannot send messages to all other child processes because they don't have a list of processes from the parent?

    If that's how it works, could you just create a single pipe in the parent where the parent reads from that pipe, and when a child process receives a message from the client it sends the message to the parent, then the parent sends the message to all connections? This would mean the parent has to listen for new connections and read from the pipe at the same time, which can be done by setting the listener socket and the pipe to non-blocking and then polling both of them.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. select() chat client blocking.
    By netpumber in forum C Programming
    Replies: 19
    Last Post: 01-16-2012, 11:49 AM
  2. udp chat server
    By cemalinanc in forum Linux Programming
    Replies: 5
    Last Post: 11-22-2010, 06:41 AM
  3. Chat udp server/client
    By Phoenix_Rebirth in forum C Programming
    Replies: 1
    Last Post: 11-17-2008, 12:30 PM
  4. Select(); server help!
    By klipseracer in forum Networking/Device Communication
    Replies: 3
    Last Post: 03-02-2008, 11:16 PM
  5. select() server
    By chrismiceli in forum Linux Programming
    Replies: 0
    Last Post: 09-09-2003, 08:56 PM