Solved

using select() with Berkeley sockets

Posted on 2001-06-20
13
500 Views
Last Modified: 2010-08-05
I'm writing a client/server pair in C. The clients (naturally) connect to the server, but then I want a message sent to the server from any client to be sent to all the clients. It looks like select is supposed to do this, but I'm not seeing how that would exactly play out. I've found a bunch of web examples that look quite similar to the examples I have here in the W Richard Stevens book, and they're not helping me. Like I'm not even sure where to put the select in my program, etc. Also, I'm forking processes, so I don't understand how a forked child process from the server would know that whatever structure maintains the fds in the parent process has changed the list of fds since the new ones would be past the number sent to the child process. Wouldn't a child process only be able to multicast to all the other fds that had joined before it (and not even see any of the ones that joined it)? I'll believe it works without explanation is someone could show me. I'm using TCP (for now, will be switching to UDP eventually). Code would really really really help here. thanks,

mich
0
Comment
Question by:phirephly
  • 8
  • 4
13 Comments
 

Expert Comment

by:MacTruck
ID: 6213062
Are you wanting to write something similar to, say, a chat room?

If so, then you should look at 'man -k pthread'.  Specifically, the section on mutex'es.  You should probably be using LinuxThreads (pthread's) for what you want to do instead of relying on something primitive and not very portable to other platforms/OSes.

MacTruck
0
 
LVL 5

Expert Comment

by:djbusychild
ID: 6213096
If you fork, you cannot share memory other than have their own IPC mechanisms.

you don't HAVE to use select. There are various models of servers. As MacTruck pointed out you can use threads that run in parallel and process incoming messages. On single threaded servers select is good and if you're carefult about structuring your server sometimes you can really get a lot out of just pure select without incurring the overhead of threads. If you're feeling wild, you can try signal driven servers as well. I wouldn't recommend it if you're just starting out with sockets, however.

here's a typical tcp server

get socket descriptor
bind
listen
accept
read

accept is blocking, so it's usually not very desirable to have accpet be blocking when you want to be handling more than one requests at a time. read is also blocking ( although you can do non-blocking reads, that wastes a lot of CPU cycles ). This is where select comes in ( single-threaded scenario ).

you can call select with a timeout of 0. this means select will check if there are any file descriptors awaiting io it'll set the masks for it if not, it'll just fall through. after it falls through you can read from only the
ones that have io waiting, and in the remaining portion of
your loop you can do other processing. you have to really
plan your core loop carefully to make the most out of the use of select.

0
 
LVL 5

Accepted Solution

by:
djbusychild earned 200 total points
ID: 6213109
here's a pseudo code to get you thinking. =0

int fd , next = 0; /* fd is listening descriptor */
int newfd[10]; /* newfd is array of accepted descriptors */
int maxfd; /* this holds the max numbered descriptor */
while (1) {
  fd_set readfds;
  FD_ZERO(&readfds);
  FD_SET(fd, &readfds);
  /* fill in ... use FD_SET to turn on bits for newfds */
  select(maxfd + 1, &readfds, 0 , 0 , 0); /* anybody have anything for me??? */
  if (FD_ISSET(fd, &readfds)) {
    newfd[next++] = accept(fd, ...);
  }
 
  /* do the following for each descriptor newfd[i] */
  if (FD_ISSET(newfd[i],&readfds)) {
    read(newfd[i],buf,sizeof(buf));
    /* process data */
     .
     .
     .
  }
}
0
 

Author Comment

by:phirephly
ID: 6214034
I suppose it could be thought of kinda of like a chat room, because whatever someone says, everyone else should see. Actually what i'm trying to do it write the networking part of a virtual reality simulation. The server will be sitting somewhere, and the clients will connect to it. The clients will tell the server where they are and other useful infomation such as velocities for dead reckoning, but the point is that whatever is in this packet should be broadcast to everyone else somehow. I know that once a process is forked, you can't really easily update the data that it has from the outside (tell it someone else has joined), but maybe I can just be clever that way and have the latest process do the broadcasting. Processes would just assume they are the latest until they see someone else join, then shut up until they see that client leave. I dunno - I'll figure out how to be clever later, I just wanna get select working. Like right now here's what I have that's locking my server:

fd_set readmes, writemes;  /*  global so they don't have to be passed  */

void sock_check(int sock)  {

        int cnt, i;
        FD_SET((unsigned int) sock,&readmes);
        fprintf(stderr, "past the FD_SET\t");
        cnt = select(sock+1,&readmes,NULL,NULL,NULL);
        fprintf(stderr,"cnt: %d\n",cnt);
        return;
}

void add_ints2(int sockfd, int *fds)  {

        int     n, i, sum, nums[MAXSIZE], *ptr;
        char    *message = "Please enter some more integers to add: ";

        while (1)  {

            sum = 0;
            n = read(sockfd, (char *)nums, sizeof(int));

            sum = nums[0];
            sock_check(sockfd);

            if (writen(sockfd, (char *)&sum, sizeof(int)) != sizeof(int))   {
                exit_add_ints(1, fds);
                return;
            }

            if (writen(sockfd, message, strlen(message)) != strlen(message))   {
                exit_add_ints(2, fds);
                return;
            }

        }
        exit_add_ints(0, fds);
        return;
}

void main()  {

        int fd, newfd, clilen, childpid, addrsize, i;
        struct sockaddr_in cli_addr, serv_addr;

        FD_ZERO(&readmes);
        FD_ZERO(&writemes);

        /*  socket, bind, getsockname, listen */

        for ( ; ; )
        {
            clilen = sizeof(cli_addr);
            newfd = accept(fd, (struct sockaddr *) &cli_addr, &clilen);

            if (newfd < 0)   {
                perror("server: accept error");
                return;
            }
            if ( (childpid = fork()) < 0)   {
                perror("server: fork error");
                return;
            }
            else if (childpid == 0)   {
                close(fd);
                FD_SET(newfd, &readmes);
                add_ints2(newfd, &fds);
                return;
            }
            close(newfd);
        }
}

All I want select to do right now is tell me how many fds are readable each time the loop in add_ints2, then I'll work on ISSETing them. Shouldn't this do that? Doesn't select return the number of readables? Is setting the timeout (arg5) to NULL different than setting it to 0? Right now I'm only connecting with one client and the select call is hanging up the server. If I set the timeout to 0, it should just poll all the fds, and give me a count, right? If no one is readable, then it should just return 0, shouldn't it? Even if select just gives me some number (any number) back that I can work with with the ISSETs, that's all I want right now. When I tried declaring a struct timeval timeout in sock_check, then assigning timeout.tv_sec = 0; amd timeout.tv_usec = 0; the compiler kept saying "storage size of 'timeout' isn't known". Which then failed the compile.

Does this help anyone help me?  thanks and ttyl,

mich
0
 

Author Comment

by:phirephly
ID: 6214038
oh yeah, sorry I forgot to address this before. I'm not supposed to use threads. I got in trouble for that yesterday. Since this select is supposed to be so simple, that's the way I'm supposed to go.
0
 

Author Comment

by:phirephly
ID: 6214160
alright... got the timeout size error fixed. needed to add #include <sys/time.h>
0
What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

 

Author Comment

by:phirephly
ID: 6214426
here's what sock_check looks like now:

void sock_check(int sock)  {

        int cnt, i,cnt2;
        struct timeval timeout;

        FD_SET(sock,&readmes);

        memset((char *)&timeout,0,sizeof(timeout));
        timeout.tv_sec = 0;
        timeout.tv_usec = 0;

        cnt = select(sock+1,&readmes,&writemes,NULL,&timeout);

        fprintf(stderr,"sock_passed: %d\t",sock);
        fprintf(stderr,"cnt: %d\t",cnt);
        fprintf(stderr,"read? %d\t",(FD_ISSET(sock,&readmes)));
        fprintf(stderr,"write? %d\t",(FD_ISSET(sock,&writemes)));
        fprintf(stderr,"read2? %d\t",(FD_ISSET(sock+1,&readmes)));
        fprintf(stderr,"write2? %d\n",(FD_ISSET(sock+1,&writemes)));

        return;
}

as you can see from the above, this is called right before I write out to the connected client, so I should see that someone's writeable, shouldn't I? I always seem to get the same thing: sock_passed is 4, and all the rest are 0s. Since the client is waiting to be written to, shouldn't it show up as writeable, and therefore, shouldn't cnt = 1, and write2 = 1 (true)? thanks and ttyl,

mich
0
 

Author Comment

by:phirephly
ID: 6214431
sock_passed: 4  cnt: 0  read? 0 write? 0        read2? 0        write2? 0
0
 

Author Comment

by:phirephly
ID: 6214841
something that might have something to do with it... when I have close(newfd) in main() as shown above, every new socket comes in as 4 when I make multiple connections. This isn't right, is it? If I move it up into the exit_add_ints() function which just prints a string based on the first arg and closes the fd that is now passed to it), then I get sequential sockfds. eg. 4, 5, 6, when multiple connections are made. I want them to number up, right? When I close 4, the next one still comes in at the next number, but that's ok, right? I'm still getting the same output, tho, as shown above, except, the sock matches the one that it's supposed to be. I dunno if this helps at all or even has anything to do with anything, but thought I'd letcha know.
0
 

Author Comment

by:phirephly
ID: 6215279
sock_check now starts like this:

void sock_check(int sock)  {

        int cnt, i,cnt2;
        struct timeval timeout;

        FD_ZERO(&readmes);
        FD_ZERO(&writemes);

        FD_SET(sock,&readmes);
        FD_SET(sock,&writemes);

...

and I get outputs like this:

sum: 6546543    sock_passed: 10 cnt: 1  read? 0 write? 1024     read2? 0        write2? 0

the write val is 2^(sockfd), so no prob there, but I'm not seeing the other sockets showing up in here. the count variable (cnt), is always 1. Shouldn't the other connections always show up as readable or writable?
0
 
LVL 5

Expert Comment

by:djbusychild
ID: 6216067
you're bombarding with new comments... I'll take a look at all of them when I get home tonight. =)
0
 
LVL 5

Expert Comment

by:djbusychild
ID: 6217092
there really are two parts to the server's socket descriptor. the main socket descriptor is what you socket is bound to, and every type of accept a connection a new socket descriptor is returned.

don't worry too much about the highest fd, just set it to something like 32 for the time being

if you're select is returning number bigger than 0, than that simply means you can do an accept on the socket and get a new descriptor to establish a connection.
0
 

Author Comment

by:phirephly
ID: 6219760
pseudo code helped a bunch. thanks!
0

Featured Post

Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
memory mapped I/O query 6 137
why debugging a macro i s difficult 10 26
Adjust Mfcapp 29 155
C hashtable library 3 71
Preface I don't like visual development tools that are supposed to write a program for me. Even if it is Xcode and I can use Interface Builder. Yes, it is a perfect tool and has helped me a lot, mainly, in the beginning, when my programs were small…
Windows programmers of the C/C++ variety, how many of you realise that since Window 9x Microsoft has been lying to you about what constitutes Unicode (http://en.wikipedia.org/wiki/Unicode)? They will have you believe that Unicode requires you to use…
The goal of this video is to provide viewers with basic examples to understand recursion in the C programming language.
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use for-loops in the C programming language.

747 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now