Thread: Help about Select()

  1. #1
    irregularly symmetrical n3v's Avatar
    Join Date
    Mar 2006
    Location
    Finland
    Posts
    67

    Help about Select()

    i see many people while trying to figure out winsock and networking often find basic blocking sockets easy to comprehend, but then get hopelessly stuck when it comes to using select(). now, this source code i wrote might not answer all the questions, but it at least has everything needed in a select server, and tries to explain what's happening. this program is a chat program, where multiple people can telnet in and see eachothers' messages:
    Code:
    //select example
    // by Steven Mills
    // [email protected]
    // Tested and compiled on dev c++. Compatibility with other compliers
    // can't be guaranteed.
    // use library ws2_32.lib with this project
    
    #include <iostream>
    #include <winsock2.h>
    
    using namespace std;
    // *** DECLARATIONS *** //
    
       const int PORT = 80; // this is the port the server runs on
       const int ireqver = 2; // required version of winsock on the machine running the server
       fd_set master;  // master file descriptor list
        fd_set read_fds; // temp file descriptor list for select()
        struct sockaddr_in myaddr; //local machine's ip
        struct sockaddr_in remoteaddr; //client ip
        int fdmax; // maximum file descriptor number
        int newfd; //newly accept()ed socket discriptor
        char buf[256]; //buffer for client data
        int nbytes;
        int addrlen;
        int i, j;
        WSADATA wsaData;
        char welcome[] = {"Welcome! \n Chat program 1.0 by Steven Mills \n"};
        
    /// *** MAIN PROGRAM *** ///
    
    int main(int argc, char *argv[])
    {
        
        FD_ZERO(&master); // clear the master 
        FD_ZERO(&read_fds); // and temp sets
        cout << "initiating winsock...";
        if (WSAStartup(MAKEWORD(ireqver, 0), &wsaData) == 0) // checking winsock version
        {
              if (LOBYTE(wsaData.wVersion) >= ireqver) // is version good enough?
              {
        cout << "initiated." << endl; // yes
              }
        }
        else {
             cout << "requested version not available." << endl; // no
    }
    
    //set up the listening socket!
    cout << "setting up listening socket...";
    SOCKET listener; // listening socket
    listener = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); // defining the socket
    
    // PF_INET is the domain, or Protocol Family i selected.  
    // You can also use AF_INET (Adress Family) for some reason,
    // the program works with both values, anyway, just remember
    // that you have to have this, because in these families is where 
    // the TCP/IP and UDP connections are.
    
    // SOCK_STREAM indicates that we are using streaming sockets (TCP), and not a 
    // datagram (UDP, or in the code it would be SOCK_DGRAM)
    // type of socket, which means that the bits in our SOCK_STREAM socket will get there
    // in the same order that they left.  Datagram sockets are not suitable, for this
    // program, as they are for speed and not accuracy.  They are better used in things like
    // streaming audio and video.
    
    // IPPROTO_TCP, or TCP/IP Protocol.  see it?
    
    myaddr.sin_family = AF_INET; // assigning address family
    myaddr.sin_addr.s_addr = INADDR_ANY; // telling the server to accept any ip
    myaddr.sin_port = htons(PORT); // but only on this particular port
    memset(&(myaddr.sin_zero), '\0', 8);
    if (listener == SOCKET_ERROR) { // check for errors
                cout << "failed." << endl;
    }
    else if  (bind(listener, (struct sockaddr *) &myaddr, sizeof(myaddr)) != 0)
    { cout << "failed" << endl;
    }
    // starting the listening!
    if (listen(listener, 10) != 0) { // a value of non-zero indicates an error
       cout << "failed";
    }
    else
    { cout << "success." << endl;
    }
    //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) { // select will handle
    // the list of connections in the file descriptor
    cout << "error with select"; // error checking
    }
                                
            // this following loop goes through the existing connections
            // looking for data to read, or new connections.
            for (i = 0; i <= fdmax; i++) {
                if (FD_ISSET(i, &read_fds)) { // NEW CONNECTION
                if (i == listener) {
                      // handle new connection
                addrlen = sizeof(remoteaddr);
                
    if ((newfd = accept(listener, (struct sockaddr *)&remoteaddr,
    &addrlen)) == -1) { // accepts new connection
    cout << "error processing new connection"; // error checking
    }
                else {
                FD_SET(newfd, &master); //add to master set
                if (newfd > fdmax) {// keeping track of maximum
                fdmax = newfd;
                }
                cout << "New connection from: " << inet_ntoa(remoteaddr.sin_addr) << " on socket "
                << newfd << "." << endl; // this identifies the connection
                if (send(newfd, welcome, sizeof(welcome), 0) == -1) { // sends the user
                // a welcome message when they connect to the server
                cout << "error while sending welcome message" << endl; // error checking
                }
                }
                }
                else { // handle data from a client
                if ((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0) {
                            // error or connection terminated by client
                            if (nbytes == 0) { //indicates that the client disconnected
                                       //connection closed
                            cout << "socket " << i << " disconnected" << endl;
                            } else {
                                   cout << "error recieving data" << endl; // error checking
                                   }
                                   closesocket(i); // socket closed
                                   FD_CLR(i, &master); // remove from master set
                                   }
                                   else { // real data from client
                                   for (j = 0; j <= fdmax; j++) {
                                       // send to every client
                                       if (FD_ISSET(j, &master)) {
                                       // except the listener and ourselves
                                       if (j != listener && j != i) {
                                             if (send(j, buf, nbytes, 0) == -1) {
                                                         cout << "error while sending" << endl;  // error checking
                                                         }
                                                    }
                                             }
                                       }
                            }
                            }
                            }
                            }
                            }
    if (WSACleanup() != 0) { // have to clean up your winsock mess!
                     cout << "cleanup failed." << endl; // error checking
                     }
        system("PAUSE"); // you can use these last two lines if you want
        return EXIT_SUCCESS; 
    }
    by the way, if anyone has any suggestions on improving the code, feel free.
    Last edited by n3v; 06-23-2006 at 06:27 AM.
    If you make a man a fire,
    he will be warm for a day.
    If you set a man on fire,
    he will be warm for the rest of his life.

  2. #2
    Registered User
    Join Date
    Nov 2001
    Posts
    1,348
    in this example select blocking i/o

    nonblocking requires multithreading

    Kuphryn

  3. #3
    int x = *((int *) NULL); Cactus_Hugger's Avatar
    Join Date
    Jul 2003
    Location
    Banks of the River Styx
    Posts
    902
    Nonblocking in no way requires multithreading. (And isn't a common use of non-blocking to avoid multithreading with each thread working a blocking socket?) I've nonetheless done several projects with a single thread using non-blocking sockets.

    There is no indentation in your code, making it difficult to read for a person who has used select(). Not only that, your code exhibits flaws that come up time and again on this board, such as checking send() or recv()'s return value to ensure that the entire buffer was actually sent.

    You use port 80. Port 80 is for HTTP... which your program is not. Show good style and choose another port.
    Code:
        int yes=1; //for setsockopt () SO_REUSEADDR, below
    Nope, not seeing anything below that...
    long time; /* know C? */
    Unprecedented performance: Nothing ever ran this slow before.
    Any sufficiently advanced bug is indistinguishable from a feature.
    Real Programmers confuse Halloween and Christmas, because dec 25 == oct 31.
    The best way to accelerate an IBM is at 9.8 m/s/s.
    recursion (re - cur' - zhun) n. 1. (see recursion)

  4. #4
    irregularly symmetrical n3v's Avatar
    Join Date
    Mar 2006
    Location
    Finland
    Posts
    67
    You use port 80. Port 80 is for HTTP... which your program is not. Show good style and choose another port.
    It's perfectly possible to use another port, and i did, but in testing i found that firewalls can prevent the program from working on other ports for connecting users, so i decided to use a port that's always open.

    There is no indentation in your code, making it difficult to read for a person who has used select(). Not only that, your code exhibits flaws that come up time and again on this board, such as checking send() or recv()'s return value to ensure that the entire buffer was actually sent.
    difficult to read for a person who has used select? what do you mean?
    my code indentions got messed up when i posted it, but i think it's still fairly readable.
    what's wrong with my send()ing and recv()ing? the error checking works, at least as far as I've seen.

    edit: i edited my code from saying non-blocking server to "select()" server, because it's not the only non-blocking I/O model.
    It is still a non-blocking program, regardless of the abscence of multi-threading.. right?

    by the way, i'm at the point where i'm getting into multi-threading. could someone explain how it works, perhaps some example lines of code, and when it's useful? thanks.
    Last edited by n3v; 06-23-2006 at 10:03 AM.
    If you make a man a fire,
    he will be warm for a day.
    If you set a man on fire,
    he will be warm for the rest of his life.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. temperature sensors
    By danko in forum C Programming
    Replies: 22
    Last Post: 07-10-2007, 07:26 PM
  2. brace-enclosed error
    By jdc18 in forum C++ Programming
    Replies: 53
    Last Post: 05-03-2007, 05:49 PM
  3. Directional Keys - Useing in Console
    By RoD in forum C++ Programming
    Replies: 38
    Last Post: 10-06-2002, 04:42 PM
  4. FAQ: Directional Keys - Useing in Console
    By RoD in forum FAQ Board
    Replies: 38
    Last Post: 10-06-2002, 04:42 PM