Like Tree1Likes
  • 1 Post By anduril462

sockets example

This is a discussion on sockets example within the C Programming forums, part of the General Programming Boards category; Hi there! I'm newbie in sockets programming nd tried to understand how they registered in system. Tried example 3.3 from ...

  1. #1
    Registered User
    Join Date
    Dec 2010
    Posts
    51

    sockets example

    Hi there! I'm newbie in sockets programming nd tried to understand how they registered in system. Tried example 3.3 from here
    and found that at least under my system (kubuntu 12.10) program prints nothing. "List open files" doesn't print too. What changes since then?
    And yeah, i've changed grep argument to righ one, namely a.out and even stop program under gdb onthat system call to find footprints of registered that way socket, but still nothing.
    For your convience just duplicate code:
    Code:
    /* inetaddr.c:
     *
     * Example using inet_addr(3):
     */
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    /*
     * This function reports the error and
     * exits back to the shell:
     */
    static void
    bail(const char *on_what) 
    {
      fputs(on_what,stderr);
      fputc('\n',stderr);
      exit(1);
    }
    
    int main(int argc,char **argv)
    {
      int z;
      struct sockaddr_in adr_inet;/* AF_INET */
      int len_inet;                /* length  */
      int sck_inet;                 /* Socket */
      /* Create a Socket */
      sck_inet = socket(AF_INET,SOCK_STREAM,0);
      if ( sck_inet == -1 )
        bail("socket()");
      /* Establish address */
      memset(&adr_inet,0,sizeof adr_inet);
      adr_inet.sin_family = AF_INET;
      adr_inet.sin_port = htons(9000);
      adr_inet.sin_addr.s_addr =inet_addr("127.0.0.95");
      if ( adr_inet.sin_addr.s_addr == INADDR_NONE )
        bail("bad address.");
      len_inet = sizeof adr_inet;
      /* Bind it to the socket */
      z = bind(sck_inet, (struct sockaddr *)&adr_inet, len_inet);
      if ( z == -1 )
        bail("bind()");
      /* Display our socket address */
      system("netstat -pa --tcp 2>/dev/null | grep a.out");
      return 0;

  2. #2
    Registered User
    Join Date
    Oct 2011
    Posts
    838
    It's just a very poor socket example, because it relies on an external command, netstat, to output anything meaningful. On my system, it does not. (lsof -i does, but takes a few seconds to run.)

    Here is a full-featured listening socket example I'd recommend you study:
    Code:
    #include <stdlib.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <netdb.h>
    #include <signal.h>
    #include <stdarg.h>
    #include <string.h>
    #include <errno.h>
    
    /* Number of connections buffered by the kernel. Suggestion only.
    */
    #ifndef   BACKLOG
    #define   BACKLOG  50
    #endif /* BACKLOG */
    
    /* Constants useful for converting socket addresses and ports to strings.
    */
    #ifndef   NI_MAXHOST
    #define   NI_MAXHOST  1040
    #endif
    #ifndef   NI_MAXSERV
    #define   NI_MAXSERV  64
    #endif
    
    /* Helper function: write to a file descriptor.
     *                  Returns 0 if successful, errno error code otherwise.
    */
    static int writefd(const int descriptor, const void *const data, size_t const size)
    {
        const unsigned char        *head = (const unsigned char *)data;
        const unsigned char *const  tail = size + (const unsigned char *)data;
        ssize_t                     bytes;
    
        while (head < tail) {
    
            bytes = write(descriptor, head, (size_t)(tail - head));
            if (bytes > (ssize_t)0)
                head += bytes;
    
            else
            if (bytes != (ssize_t)-1)
                return errno = EIO;
    
            else
            if (errno != EINTR)
                return errno;
    
        }
    
        return 0;
    }
    
    /* Helper function: Write strings to standard error.
     *                  Returns 0 if successful, errno error code otherwise.
     *                  Keeps errno unchanged.
    */
    static int wrerr(const size_t strings, ...)
    {
        va_list  args;
        size_t   i;
        int      saved_errno;
    
        saved_errno = errno;
    
        va_start(args, strings);
    
        for (i = 0; i < strings; i++) {
            const char *const string = va_arg(args, const char *);
            const size_t      length = (string) ? strlen(string) : 0;
            int               result;
    
            if (length < 1)
                continue;
    
            result = writefd(STDERR_FILENO, string, length);
            if (result) {
                va_end(args);
                errno = saved_errno;
                return result;
            }
        }
    
        va_end(args);
    
        errno = saved_errno;
        return 0;
    }
    
    /* Helper function: Close descriptor.
     *                  Returns 0 if success, errno otherwise.
    */
    static int closefd(const int descriptor)
    {
        int result;
    
        do {
            result = close(descriptor);
        } while (result == -1 && errno == EINTR);
    
        if (result == -1)
            return errno;
        else
            return 0;
    }
    
    /* Child process reaper; triggered by SIGCHLD.
    */
    static void reap(int signum __attribute__((unused)))
    {
        pid_t  p;
    
        do {
            p = waitpid((pid_t)-1, NULL, WNOHANG);
        } while (p != (pid_t)0 && p != (pid_t)-1);
    }
    
    static int install_reaper(void)
    {
        struct sigaction  act;
    
        sigemptyset(&act.sa_mask);
        act.sa_handler = reap;
        act.sa_flags = 0;
    
        if (sigaction(SIGCHLD, &act, NULL) == -1)
            return errno;
        else
            return 0;
    }
    
    /* Interrupt signal handler.
    */
    static volatile sig_atomic_t  interrupted = 0;
    
    static void interrupt_handler(int signum)
    {
        if (!interrupted)
            interrupted = signum;
    }
    
    static int install_interrupt_handler(const int signum)
    {
        struct sigaction  act;
    
        sigemptyset(&act.sa_mask);
        act.sa_handler = interrupt_handler;
        act.sa_flags = 0;
    
        if (sigaction(signum, &act, NULL) == -1)
            return errno;
        else
            return 0;
    }
    
    int main(int argc, char *argv[])
    {
        char                hostname[NI_MAXHOST], portname[NI_MAXSERV];
    
        const char         *requested_host, *requested_port;
        struct addrinfo     hints, *list, *curr;
        int                 result, socketfd;
    
        char                client_host[NI_MAXHOST];
        char                client_port[NI_MAXSERV];
        struct sockaddr_in6 client_addr;
        socklen_t           client_addrlen;
        int                 connfd;
    
        pid_t               child;
    
        /* No parameters? Help requested? */
        if (argc < 4 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
            wrerr(1, "\n");
            wrerr(3, "Usage: ", argv[0], " [ -h | --help ]\n");
            wrerr(3, "       ", argv[0], " ADDRESS PORT COMMAND [ ARGS .. ]\n");
            wrerr(1, "\n");
            wrerr(1, "This program will create a listening socket\n");
            wrerr(1, "on the specified address and port.\n");
            wrerr(1, "\n");
            wrerr(1, "When an incoming connection is established, a child\n");
            wrerr(1, "process will be spawned, with standard input and output\n");
            wrerr(1, "redirected to the incoming connection.\n");
            wrerr(1, "\n");
            wrerr(1, "Send a SIGINT (Ctrl+C) or SIGHUP signal to the process\n");
            wrerr(1, "to stop it listening to new incoming connections.\n");
            wrerr(1, "\n");
            return 1;
        }
    
        /* Install interrupt handler on SIGINT and SIGHUP,
         * and child process reaper on SIGCHLD. */
        if (install_interrupt_handler(SIGINT) ||
            install_interrupt_handler(SIGHUP) ||
            install_reaper()) {
            wrerr(3, "Cannot install signal handlers: ", strerror(errno), ".\n");
            return 1;
        }
    
        /* Pick out host and port from command line parameters. */
        requested_host = argv[1];
        requested_port = argv[2];
    
        /* NULL or empty or "*" or ":" or "." means wildcard address. */
        if (!requested_host ||
            !*requested_host ||
            !strcmp(requested_host, "*") ||
            !strcmp(requested_host, ":") ||
            !strcmp(requested_host, "."))
            requested_host = NULL;
    
        /* Define address info hints; what kind of sockets we allow. */
        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC;        /* Both IPv4 and IPv6 are accepted. */
        hints.ai_socktype = SOCK_STREAM;    /* TCP sockets. */
        hints.ai_flags = AI_PASSIVE;        /* NULL will match wildcard address. */
        hints.ai_protocol = 0;              /* Any protocol. */
        hints.ai_canonname = NULL;
        hints.ai_addr = NULL;
        hints.ai_next = NULL;
    
        /* Get the chain of possible sockets. */
        result = getaddrinfo(requested_host, requested_port, &hints, &list);
        if (result) {
            const char *const errmsg = gai_strerror(result);
            const char *const req_host = (requested_host) ? requested_host : "*";
            wrerr(6, req_host, " ", requested_port, ": ", errmsg, ".\n");
            return 1;
        }
    
        /* Try sockets one by one to see which one works. */
        socketfd = -1;
        for (curr = list; curr != NULL; curr = curr->ai_next) {
    
            /* Create the socket. */
            socketfd = socket(curr->ai_family, curr->ai_socktype, curr->ai_protocol);
            if (socketfd == -1)
                continue;
    
            /* Bind to the socket. */
            if (bind(socketfd, curr->ai_addr, curr->ai_addrlen) == -1) {
                /* Cannot bind. */
                closefd(socketfd);
                /* Try next. */
                continue;
            }
    
            /* Listen for incoming connections on the socket. */
            if (listen(socketfd, BACKLOG) == -1) {
                /* Failed. */
                closefd(socketfd);
                /* Try next. */
                continue;
            }
    
            /* This is a good socket. */
            break;
        }
    
        /* Failed to bind to a socket? */
        if (!curr) {
            const char *const req_host = (requested_host) ? requested_host : "*";
            freeaddrinfo(list);
            wrerr(4, req_host, " ", requested_port, ": Could not find a suitable socket to bind to.\n");
            return 1;
        }
    
        /* Convert the actual bound socket address and port to strings. */
        result = getnameinfo(curr->ai_addr, curr->ai_addrlen,
                             hostname, sizeof hostname,
                             portname, sizeof portname,
                             NI_NUMERICHOST | NI_NUMERICSERV);
        if (result) {
            const char *const errmsg = gai_strerror(result);
            freeaddrinfo(list);
            closefd(socketfd);
            wrerr(3, "Cannot get bound socket address and port: ", errmsg, ".\n");
            return 1;
        }
    
        /* Discard the socket info chain. */
        freeaddrinfo(list);
        list = NULL; curr = NULL;                
    
        /* Let the user know we are listening. */
        wrerr(5, "Listening for incoming connections to ", hostname, " port ", portname, ".\n");
    
        /* Incoming connection loop. */
        while (!interrupted) {
    
            /* Opportunistically reap dead children.
             * If two or more exit at the same time,
             * we may only get one signal. */
            reap(0);
    
            /* Accept an incoming connection. */
            client_addrlen = sizeof client_addr;
            connfd = accept(socketfd, (struct sockaddr *)&client_addr, &client_addrlen);
    
            /* Not an incoming connection? */
            if (connfd == -1) {
    
                /* Need to retry? (Possibly interrupted.) */
                if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
                    continue;
    
                /* No, it was a real error. */
                break;
            }
    
            /* Get the client address and port as strings. */
            result = getnameinfo((struct sockaddr *)&client_addr, client_addrlen,
                                 client_host, sizeof client_host,
                                 client_port, sizeof client_port,
                                 NI_NUMERICHOST | NI_NUMERICSERV);
            if (result) {
                const char *const errmsg = gai_strerror(result);
                wrerr(2, errmsg, ".\n");
                closefd(connfd);
                continue;
            }
    
            /* Let the user know about the connection. */
            wrerr(5, "Connection from ", client_host, " port ", client_port, ".\n");
    
            /* Fork the child process. */
            child = fork();
            if (child == (pid_t)-1) {
                const char *const errmsg = strerror(errno);
                wrerr(3, "Cannot fork child process: ", errmsg, ".\n");
                closefd(connfd);
                continue;
            }
    
            /* Parent process will simply close the connection descriptor,
             * and wait for a new connection. */
            if (child) {
                closefd(connfd);
                continue;
            }
    
            /* This is the child process. */
    
            /* Close the original socket. */
            closefd(socketfd);
    
            /* Duplicate the connection to standard input. */
            do {
                result = dup2(connfd, STDIN_FILENO);
            } while (result == -1 && errno == EINTR);
            if (result == -1) {
                const char *const errmsg = strerror(errno);
                wrerr(3, "Cannot redirect connection to standard input of child: ", errmsg, ".\n");
                exit(127);
            }
    
            /* Duplicate the connection to standard output. */
            do {
                result = dup2(connfd, STDOUT_FILENO);
            } while (result == -1 && errno == EINTR);
            if (result == -1) {
                const char *const errmsg = strerror(errno);
                wrerr(3, "Cannot redirect connection to standard output of child: ", errmsg, ".\n");
                exit(127);
            }
    
            /* Close the extra descriptor. */
            if (connfd != STDIN_FILENO && connfd != STDOUT_FILENO)
                closefd(connfd);
    
            /* Execute the desired command. */
            execvp(argv[3], argv + 3);
    
            /* FAILED! */
            wrerr(5, "Failed to execute ", argv[3], ": ", strerror(errno), ".\n");
            closefd(STDIN_FILENO);
            closefd(STDOUT_FILENO);
            exit(127);
        }
    
        if (interrupted)
            wrerr(3, "Caught ", strsignal(interrupted), ", exiting.\n");
        else
            wrerr(3, "Error accepting an incoming connection: ", strerror(errno), ".\n");
    
        /* Close the socket. */
        closefd(socketfd);
    
        return 0;
    }
    In particular, it
    • Uses getaddrinfo() and [URL=http://www.kernel.org/doc/man-pages/online/pages/man3/getnameinfo.3.html]getnameinfo()[/FONT] to convert addresses and services/ports from strings to socket addresses and vice versa
    • Creates a child process to handle each incoming connection
    • Uses signals for reaping dead child processes
    • Uses low-level I/O as defined in <unistd.h> insted of <stdio.h>
      In particular, check the writefd() and closefd() helper functions to see how the interruptions caused by signal delivery is properly handled


    If you save the above source as server.c, you can compile it using
    Code:
    gcc -W -Wall -O3 server.c -o server
    Run ./server without parameters to see the usage.

    One way to test it would be to run for example
    Code:
    ./server 127.1.1.1 1111 date -u
    in one terminal window, and for example
    Code:
    telnet 127.1.1.1 1111
    in another. You can stop the server by sending it a HUP or INT signal. The easiest way to send INT is to press Ctrl+C in that terminal window.

    Another fun test would be to run
    Code:
    ./server 127.2.2.2 2222 dd bs=1 conv=ucase
    in one terminal window, and for example
    Code:
    nc 127.2.2.2 2222
    in another. (Press CTRL-D at the start of a line in the latter terminal window to close the connection.) The server will respond by converting everything you send to it to uppercase.

    You can use plain Bash to send single-line messages and responses, too:
    Code:
    bash -c 'REQUEST="Hello" ; echo "Request:  $REQUEST" ; exec 3<>/dev/tcp/127.2.2.2/2222 ; echo "$REQUEST" >&3 ; read -u 3 RESPONSE ; exec 3<&- ; echo "Response: $RESPONSE"'
    After you get that working, you can write a client program, or start modifying the above one to suit your personal taste.

    Questions?

  3. #3
    Registered User
    Join Date
    Dec 2010
    Posts
    51

    RE: poor example

    My question related to registering sockets in system and not to functionality of program. I've tried BOTH
    netstat -pa --tcp
    and
    lsof -i tcp
    and obtain no results. Do you obtain any in your machine? Maybe its a feature/bug of kubuntu?

  4. #4
    a_capitalist_story
    Join Date
    Dec 2007
    Posts
    2,650
    Out of curiosity, if you run the program as root, does it produce output?

  5. #5
    Registered User
    Join Date
    Dec 2010
    Posts
    51
    Quote Originally Posted by rags_to_riches View Post
    Out of curiosity, if you run the program as root, does it produce output?
    No! Cannot find any situation to produce one. So i just asked whether someone under kubuntu has same? I guess there is anyone else on forum who has kubuntu and not lazy enought to check code. And problem is not in system call at all. I just tried to stop under gdb in that line and tried to output in any form i guessed in other terminal but i cannot find any record with my program, however new instance returns error with code "bind()" (and actually it's "Address already in use"). BTW, i don't see sockets with "CLOSED" status at all, so i rather think it's feature or smth like that.

    I actualy also don't understan why ALL ip's on from network
    127.0.0.0/255.0.0.0 are registered to my host. E.g:
    ping 127.9.9.8
    PING 127.9.9.8 (127.9.9.8) 56(84) bytes of data.
    64 bytes from 127.9.9.8: icmp_req=1 ttl=64 time=0.055 ms
    64 bytes from 127.9.9.8: icmp_req=2 ttl=64 time=0.024 ms
    64 bytes from 127.9.9.8: icmp_req=3 ttl=64 time=0.040 ms
    64 bytes from 127.9.9.8: icmp_req=4 ttl=64 time=0.038 ms
    64 bytes from 127.9.9.8: icmp_req=5 ttl=64 time=0.052 ms
    wtf going on?
    Last edited by icegood; 12-12-2012 at 08:28 AM. Reason: addit

  6. #6
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,468
    Quote Originally Posted by icegood View Post
    No! Cannot find any situation to produce one. So i just asked whether someone under kubuntu has same? I guess there is anyone else on forum who has kubuntu and not lazy enough to check code.
    You're awfully curt with those who are trying to help you. We're not lazy, we're trying to tell you that you are working with a crappy example. There are many reasons that your code might not be working the way you expect. Getting bad code to do what you want is a pain in the ass, and it's usually more trouble than it's worth. Perhaps netstat, when run from your program encountered an error, but you don't know because you pipe stderr to /dev/null and you don't check the return value of system. It's like asking us to help you build a working car, when all you have is a toy car for a model, and some popsicle sticks and glue to build with. It's highly unlikely there is a bug in kubuntu, it's much most likely that you are missing a step or made an error. You should always look to your code and your knowledge for problems first, before you assume something is "wrong" with the implementation you are using. I don't mean this as an insult or affront to you personally, and it's not just because you are new to this. After 10+ years of programming in C, I'm still usually the guilty party when it comes to problems with my programs, it's very rarely the library I'm using.


    So what exactly are you expecting to see in your output? What state do you expect your socket to be in (ESTABLISHED, LISTEN, UNKNOWN, etc)? Check the man page for netstat to read up on what all the possible states are and what they mean, then tell us which state you expect to see your socket in. As it happens, I added a few short lines near the end of your program and bam! there's the output you're looking for.
    Code:
    $ ./a.out
    tcp        0      0 127.0.0.95:9000         *:*                     LISTEN      14252/a.out
    Hint, the answer lies in the output I posted. Working from a good example or good tutorial would show you all the steps needed to get a socket up and running, explain them all and you would see what you missed. I recommed Beej's guide to network programming (Google it, it's easy to find) as a good starter.


    As for your ping issues, where 127.0.0.0/255.0.0.0 is for your computer, read this article: localhost - Wikipedia, the free encyclopedia.
    rags_to_riches likes this.

  7. #7
    Registered User
    Join Date
    Dec 2010
    Posts
    51
    Quote Originally Posted by anduril462 View Post
    You're awfully curt with those who are trying to help you. We're not lazy...
    Why you think those words was addressed to you (and actually anyone who was there)? I appreciate that you try to help me. Moreover, "laziness" have no negative meaning. Just take it easy. Let back on track.

    Quote Originally Posted by anduril462 View Post
    What state do you expect your socket to be in?
    CLOSED
    Quote Originally Posted by anduril462 View Post
    Check the man page for netstat
    Checked: "CLOSE The socket is not being used."

    Quote Originally Posted by anduril462 View Post
    As for your ping issues, where 127.0.0.0/255.0.0.0 is for your computer, read this article: localhost - Wikipedia, the free encyclopedia.
    Clear.
    "so that packets destined to any address from the 127.0.0.0/8 block would be routed internally to the network loopback device", so it's not loopback issue but rather smth else.

  8. #8
    a_capitalist_story
    Join Date
    Dec 2007
    Posts
    2,650
    Also, I hope you didn't skip over this part of anduril462's post.

    Perhaps netstat, when run from your program encountered an error, but you don't know because you pipe stderr to /dev/null and you don't check the return value of system.

  9. #9
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,468
    Quote Originally Posted by icegood View Post
    Why you think those words was addressed to you (and actually anyone who was there)? I appreciate that you try to help me. Moreover, "laziness" have no negative meaning. Just take it easy. Let back on track.
    I did not think they were addressed to me. It did seem however, when I read the thread, that you were implying Nominal Animal and rags_to_riches were too lazy to just "do what you want and give you the answer you wanted". Perhaps your exact meaning was lost in translation, since English does not appear to be your first language. As it happens, there are many people who come to this forum asking for help, and are incredibly rude to those who volunteer to help them. If you meant no harm, then that's quite alright with me. Indeed, we will now get back on track.

    CLOSED

    Checked: "CLOSE The socket is not being used."
    I can see how that can be a bit misleading. You wont see any states in netstat until you listen for a connection, accept it or close it.

    In all my Googling for info about TCP states, it appears that they use the term "closed" for any sockets that are not in use, including those that have never had connections established and those that have been established then closed. It seems netstat does not seem to follow quite the same convention, reserving CLOSE state for connections that have been established then closed. This would take me a lot more digging into exactly how netstat determines it's output, before I could give a conclusive answer. I think it's safe to assume that this is not some sort of bug/feature of kubuntu. Whether this is related to how netstat determines output from the info it has (i.e. chooses to ignore any non-listening, closed socket), or related to whether that data is even available from kernel's socket implementation at that point in your code, I don't know. That would require much more research and I don't have time at the moment.

  10. #10
    Captain Crash brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,239
    By default, netstat will attempt to resolve IP addresses into host names. This is the most common cause of a "hang" when running that utility. Try passing "-n" as another argument to netstat.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  11. #11
    Registered User
    Join Date
    Dec 2010
    Posts
    51
    as i told i run netstat (and other commands) from other terminal while breaking my program under gdb, so you miss too.

  12. #12
    Registered User
    Join Date
    Dec 2010
    Posts
    51
    Partially solved. All those commands treated socket to be tcp one. But lsof without tcp filtering show:
    COMMAND PID TID USER FD TYPE DEVICE SIZE/OFF NODE NAME
    a.out 19439 ice 5u sock 0,7 0t0 918693 can't identify protocol
    checked via gdb
    (gdb) r
    Starting program: /home/ice/src/sockets/a.out

    Breakpoint 1, main (argc=1, argv=0x7fffffffe078) at ip_bind.c:48
    48 system("netstat -pa --tcp 2>/dev/null | grep a.out");
    (gdb) p sck_inet
    $1 = 5
    Yest, that fifth! "can't identify protocol" - protocol supposed to be system default but problem rather lies in that example too old: because of multiprotocol cases being possible now.
    Thanks to all for taking participiation.

  13. #13
    Registered User
    Join Date
    Mar 2012
    Location
    the c - side
    Posts
    243
    however new instance returns error with code "bind()" (and actually it's "Address already in use").
    btw setsockopt() is generally used with the SO_REUSEADDR option in fault-finding stages.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Sockets tutorial, datagram sockets example not working for me
    By caesius in forum Networking/Device Communication
    Replies: 14
    Last Post: 12-26-2009, 02:40 PM
  2. Sockets
    By gavra in forum C Programming
    Replies: 12
    Last Post: 07-11-2008, 09:23 AM
  3. Sockets again :)
    By G'n'R in forum Networking/Device Communication
    Replies: 4
    Last Post: 09-23-2003, 10:27 AM
  4. Sockets....
    By G'n'R in forum Networking/Device Communication
    Replies: 15
    Last Post: 09-19-2003, 04:14 AM
  5. Sockets
    By devour89 in forum C++ Programming
    Replies: 10
    Last Post: 05-30-2003, 11:09 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21