tags:

views:

99

answers:

3

I have written a little irc bot application in C. It just writes out the server messages to the screen. This is just accomplished with a while loop that reads from the server filedescriptor. How can I concurrently accept input? I would like to now extend it to accept user input so that it can be used as a client application. I am new to C development so I am not sure how this is accomplished. Can someone provide me with an example or point me in the direction to some documentation?

I basically would like to mimic a telnet application. The stdout is updated and the user can provide server commands at the console.

Any help/advise would be greatly appreciated.

EDIT

I am developing in a Unix environment.

Thanks

+2  A: 

You want to monitor both socket, and stdin. If thats right, have a look at the select() system call here: http://beej.us/guide/bgnet/output/html/singlepage/bgnet.html#select

Aman Jain
IIRC, `select` doesn't work with stdio on Windows, due to sockets and stdio streams being different behind the scenes.
cHao
@cHao good point but I am working in Unix. I updated my question.
Nick
@Nick: did you find the answer to your question yet?
Aman Jain
@Aman Jain - `select` is a very old syscall and the interface is pretty ugly. IMO new code should use `poll`. Even that has replacements these days, eg. `epoll`. @cHao On Windows you can use `WSAEventSelect`, then your `select`-style events can go into `WaitForMultipleObjects` along with any other sort of waitable handle.
asveikau
@asveikau: That's my point -- that the `select` solution mentioned in the answer is OS-specific. As is any method that seeks to treat a socket and a stream equally. The two are quite different, and the only solution that might work cross-platform would be threading. (Yes, that's probably overkill here. But `select`, or `WSAEventSelect` + `WaitForMultipleObjects`, or however some other OS does it, would be specific to that OS -- or at least, to that family of OSes.)
cHao
@asveikau: `epoll` is Linux-specific. The BSD's have `kqueue`, Solaris has `/dev/poll`. It may be better to use a wrapping library such as `libevent`.
ninjalj
@cHao: And how exactly are you going to do multithreading portably? Using some Win32 pthreads port?
ninjalj
@ninjalj: Yeah, that (or some other cross-platform threading library). But then, it'd be better to skip that part altogether if you're only using threading to fudge a select, and use a library like libevent as you mentioned, if it can wait on a stream and a socket without issues.
cHao
@ninjalj I know that `epoll` is Linux specific, this is why I wrote "eg." instead of "i.e." and "replacements" instead of "a replacement". :-) Good call on listing the various incompatible interfaces, and `libevent`.
asveikau
A: 

There is a little thing called multi-threading. Multithreading (in C++) depends entirely on either the operating system, or an external library.

If you are using windows, then you could use beginthread() and endthread() defined in "Windows.h".

It is fairly easy to use.

Alexander Rafferty
I am aware of that little thing. I am not aware of how it is implemented in C. Not C++
Nick
@Nick - Check out `pthread_create` on POSIX or `CreateThread` on Windows.
asveikau
@Alexander: before saying that multithreading is fairly easy to use, you should say that in threads almost everything is shared, and shared data should be protected by mutexes.
ninjalj
@ninjalj: And that "shared" data that isn't really shared doesn't need a mutex. And that mutexes should always be acquired in the same order to minimize deadlocks. And a bunch of other issues that make stable multithreading anything but easy. @Alexander: Personally, i don't think i'd recommend multithreading as the first and/or best solution -- it's definitely *not* easy to use, for someone that would just be starting to learn about concurrency and the whole class of problems that go along with it.
cHao
+2  A: 

Avoid multiprocess and multi-threaded programming if you can. That road leads to pain. Use event driven programming. For the type of thing you are wanting to do, event driven programming is much easier, and will perform just as well. The two main ways in C to do event driven programming (related to I/O) are select and poll.

Here is working example of using select:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>

int
main(void)
{
    fd_set rfds;
    struct timeval tv;
    int retval, len;
    char buf[4096];

    while (1) {
        /* Watch stdin (fd 0) to see when it has input. */
        FD_ZERO(&rfds);
        FD_SET(0, &rfds);

        /* Wait up to five seconds. */
        tv.tv_sec = 5;
        tv.tv_usec = 0;

        retval = select(1, &rfds, NULL, NULL, &tv);
        /* Don't rely on the value of tv now! */

        if (retval == -1) {
            perror("select()");
            exit(EIO);
        } else if (retval) {
            printf("Data is available now.\n");
        } else {
            printf("No data within five seconds.\n");
            continue;
        }
        if (FD_ISSET(0, &rfds)) {
            len = read(0, buf, 4096);
            if (len > 0) {
                buf[len] = 0;
                printf("Got data on stdin: %s\n", buf);
            } else {
                // fd closed
                perror("read()");
                exit(EIO);
            }

        }
    }
}

FD_SET is used to create the list of file-descriptors you want to select on (get events from). After select returns successfully (meaning there is an event to process), you use FD_ISSET to find the file-descriptors that caused events. In your case you are going to have an open socket file-descriptor that you will add to the set and process appropriately.

Useful documentation include the following man pages:

  • man 2 select
  • man 2 poll
  • man 3 read
  • man 3 open
kanaka
I agree. I am going to try to implement this. Thank you.
Nick