Thread: Need implementation advice , using threading and sockets

  1. #1
    Registered User
    Join Date
    Feb 2019
    Posts
    7

    Need implementation advice , using threading and sockets

    I am programming a internet chat program.

    I am not sure how to implement "multiple chat rooms with multiple users". I have a few ideas but I'm not sure what is the best solution or proper way to do this, as I am a low-medium level C/Socket programmer. This is a simple project for learning, I don't want to try to implement thread pools or something complicated for now.

    First implementation idea is to have multiple sockets listening on different ports OR should it be one socket listening on multiple ports?

    - each group of clients will have their own port (or chat room)

    - In my server driver , I will run/create a thread for each of those sockets? so that they can sit in a while loop and wait for client connections to accept()

    - Then each socket thread will create a thread for each client connection? so that they can send back the chat room messages.

    Ive quickly typed up some code, but I don't want to continue with this approach unless its correct...


    Code:
    server_drive.c
    
    int ports[3] = {3000,5000,8000};
    
    for(int i=0; i < 3; i++){
    
      chat_room_sockets[i] =  new_chat_room_socket(ports[i]);
      
      pthread_t thread;
      int* sock = malloc(sizeof(int));
      *sock = chat_room_sockets[i];
      pthread_create(&thread,NULL, start_chat_room, sock);
      pthread_detatch(thread);
    }
    
    ..



    Code:
    server_interface.h
    
    int new_chat_room_socket(int port){
        int server_sock;
        struct sockaddr_in server_address;
    
    
        if((server_sock = socket(PF_INET, SOCK_STREAM,  IPPROTO_TCP)) < 0){
         return -1;
        }
    
    
        memset(&server_address, 0, sizeof(server_address));
        server_address.sin_family = AF_INET;
        server_address.sin_addr.s_addr = htonl(INADDR_ANY);
        server_address.sin_port = htons(port);
    
    
        if (bind(server_sock, (struct sockaddr *)&server_address, sizeof(server_address)) < 0){
         return -1;
        }
        
        if (listen(server_sock, MAXPENDING) < 0){
         return -1;
        }
     
     return server_sock;
    }
    
    
    
    
    void* start_chat_room(void* sock){
      
      int client_sock;
      struct sockaddr_in client_address;
      int client_length = sizeof(client_address);
    
       for(;;){
          if((client_sock = accept((int)sock , (struct sockaddr*) &client_address , &client_length )) < 0){
        
        //add_chat_user(client_sock);
       }
    }
    
      
    ..
    Last edited by frododo1; 02-26-2019 at 01:16 AM.

  2. #2
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,811
    TBH, I would suggest ditch threads and study the select() system call.
    With select(), you can monitor all your live sockets for activity, then decide what to do with each one.
    select() even has a timeout, so there's no need to worry about blocking yourself.

    You have a single port you listen() on for incoming connections.

    Each accept() generates a new socket, which you can associate with a user+room.

    Your select() loop can tell you which sockets are ready to be processed.

    Perhaps maintain a separate list of each room, and all the sockets connected to that room.
    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.

  3. #3
    Registered User
    Join Date
    Feb 2019
    Posts
    7
    Quote Originally Posted by Salem View Post
    TBH, I would suggest ditch threads and study the select() system call.
    With select(), you can monitor all your live sockets for activity, then decide what to do with each one.
    select() even has a timeout, so there's no need to worry about blocking yourself.

    You have a single port you listen() on for incoming connections.

    Each accept() generates a new socket, which you can associate with a user+room.

    Your select() loop can tell you which sockets are ready to be processed.

    Perhaps maintain a separate list of each room, and all the sockets connected to that room.

    okay let me make sure I'm understanding this correctly,

    one socket listens for users trying to connect to server?
    when it gets a new accept() it saves the client socket?

    I originally thought of this but wasn't sure enough at the time how to setup the "chat rooms"

    edit: so I just implemented this method it seems to work.

    I will just setup a bash script or something to start multiple processes of the program.
    each program will server on a different port , each process will represent the chat room instead.
    Last edited by frododo1; 02-26-2019 at 02:36 AM.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,811
    There is only ever one port per socket.

    Client-Server Background
    Except for the fork stuff,

    Essentially,
    Code:
    sockfd = socket();  // for your listening port
    bind(sockfd)
    listen(sockfd);
    fd_set masterSet;
    FD_ZERO(&masterSet);
    FD_SET(sockfd,&masterSet);
    int maxFd = sockFd;
    while ( 1 ) {
        fd_set working = masterSet;
        int res = select(maxFd+1,&working,NULL,NULL,NULL);  // wait forever until something happens
        if ( res > 0 ) {
            for ( int i = 0 ; i < maxFd ; i++ ) {
                if ( FD_ISSET(i,&working) ) {
                    if ( i == sockfd ) {
                        newFd = accept();
                        FD_SET(newFd,&masterSet);  // add this to the things to be watched.
                        // calculate a new maxFd;
                    } else {
                        // it's one of your client connections.
                        // Forward message onto all other clients for the same room.
    
                        // If that connection comes back as closed, then you need to do
                        FD_CLR(i,&masterSet);
                        // calculate a new maxFd;
                    }
                }
            }
        }
    }
    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.

  5. #5
    Registered User
    Join Date
    Feb 2019
    Posts
    7
    Quote Originally Posted by Salem View Post
    There is only ever one port per socket.
    Okay, I must have misread the SO post I learned this from.



    Reviewing Code:

    I think I understand most of this for now,

    we use select to monitor all input coming from all chat rooms? and also incoming connections?
    masterset will hold all the file descriptors?
    so then I should also find a way to group the client file descriptors for each room?


    not understanding maxFd = sockFd
    why would we want to set this to sockFd ?


    not really understanding the for loop i to maxFd
    why this range?
    why compare i to sockFd , why would i == sockFd mean we should wait for another accept()
    Last edited by frododo1; 02-26-2019 at 03:39 AM.

  6. #6
    Registered User
    Join Date
    Feb 2019
    Posts
    891
    This is good start: Beej's Network Guide

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,811
    > we use select to monitor all input coming from all chat rooms? and also incoming connections?
    Yes.

    > masterset will hold all the file descriptors?
    Yes, because select() modifies the set, so you need an original one and a copy for the current select() call.

    > so then I should also find a way to group the client file descriptors for each room?
    Yes, you'll need an additional data structure to contain this information.
    Perhaps the first message on each connection is the room the client wants to join.

    > not understanding maxFd = sockFd
    > why would we want to set this to sockFd ?
    Because select() needs to know what the maxFd in the set is (for efficiency I guess).
    And because in an otherwise empty set with only one fd in it, the maxFd is obviously the fd just added.

    > not really understanding the for loop i to maxFd
    Well if you have 10 Fd's in the set, and only two of them have anything to report, you need to find them in some way.

    > why this range?
    Because.... efficiency.
    You could scan all 1024 (or whatever FD_SETSIZE is on your machine), but that's wasteful.

    > why compare i to sockFd , why would i == sockFd mean we should wait for another accept()
    There is no wait for another accept, an accept is ready to happen now.


    > as I am a low-medium level C/Socket programmer.
    I thought you knew all this stuff.
    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.

  8. #8
    Registered User
    Join Date
    Feb 2019
    Posts
    7
    sorry this was old , I figured out the answer to everything and understand this simple example a lot more now.
    I wasn't following along with the select macros very well at first.

    ... I think the tricky part is grouping subsets of the master set to create "chat rooms"

    one idea is to just run multiple processes of this program with a port specified, and then each process is a chat room.

    but I like the idea better of a user connecting to this server through the main listening port and then chooses a port, which will be his chat room. but that seems like the tricky part.


    or maybe just create an fd_set for chat rooms , when I receive a message from an fd just broadcast that message back to all the fd's in the fd_set that the fd belongs to.
    Last edited by frododo1; 02-26-2019 at 04:35 AM.

  9. #9
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    38,811
    I guess it depends on how much interaction there is between threads.

    If the answer is none, then it shouldn't be too difficult.

    But if the answer is some, then you NEED a good design - seriously!.

    Because a random hackery rats-nests of randomly applied mutexes to fix whatever race conditions you've managed to create for yourself will just do your head in.

    If debugging the hardest single-threaded program rates as 10, then debugging the hardest multi-threaded program rates about 1000.
    Heisenbug - Wikipedia
    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
    Registered User
    Join Date
    Feb 2019
    Posts
    7
    haha , thanks a lot for your help. I think threads could be a headache but Im still willing to at least learn a threaded implementation because its good for knowledge.


    But obviously select() makes things a lot easier. thanks for introducing this.


    For now I think I will try the approach

    having a set of listening sockets (for each port)

    then for each socket have a fd_set of client sockets

    so if i == that socket (I get a new connection) I will accept() the new fd and add it to its respective set.

    ... or something similar to this approach
    Last edited by frododo1; 02-26-2019 at 04:45 AM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. MOS 6502 - More Advice on my implementation
    By Syscal in forum C Programming
    Replies: 11
    Last Post: 07-07-2013, 06:31 AM
  2. Sockets tutorial, datagram sockets example not working for me
    By caesius in forum Networking/Device Communication
    Replies: 14
    Last Post: 12-26-2009, 03:40 PM
  3. Semaphores, need advice on implementation.
    By Swerve in forum C++ Programming
    Replies: 2
    Last Post: 01-13-2009, 01:54 AM
  4. Replies: 7
    Last Post: 10-01-2008, 07:45 PM
  5. Opinion about game server implementation (sockets)
    By Rennor in forum Game Programming
    Replies: 3
    Last Post: 08-29-2006, 05:10 PM

Tags for this Thread