Thread: a few questions about sockets

  1. #1
    Registered User
    Join Date
    May 2014
    Posts
    15

    Question a few questions about sockets

    hi, im learning about sockets (more specifically unix domain sockets) so i can create better controls for video games. i want to use the file descriptor for the socket used in x11 to communicate between the server and client. so first things first, do i have to set up a connection between the client and the server manually using socket related functions or can i just immediately after getting the file descriptor start working on that descriptor? another question: does whatever the last thing written to the socket was replace what was there before? and last question: how do i know when i've hit the EOF for the socket when reading?
    thanks alot

  2. #2
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by alansandbucket View Post
    hi, im learning about sockets (more specifically unix domain sockets) so i can create better controls for video games.
    Eh? Perhaps my imagination is faulty, but I cannot see how unix domain sockets would let you create better controls for video games.

    Quote Originally Posted by alansandbucket View Post
    i want to use the file descriptor for the socket used in x11 to communicate between the server and client. so first things first, do i have to set up a connection between the client and the server manually using socket related functions or can i just immediately after getting the file descriptor start working on that descriptor?
    Why? In all sane cases, you use the interfaces provided by the X11 libraries instead. They're certainly good enough for the existing crop of Linux games.

    It's is extremely unlikely that writing your own low-level routines you can get any measurable improvement in latencies, as they are pretty low to begin with.

    In cases where minimum possible latency is required for some input device -- say, reaction time measurements, some kind of simulator, et cetera -- you could always use the device-specific input event devices. However, the devices normally require extra privileges to read (you don't want other users on the same physical machine to read what some other user is typing!), so you'd need to set up suitable udev rules, or use a privileged helper program to act as a middleman (to provide an open file descriptor to the input event device).

    Quote Originally Posted by alansandbucket View Post
    does whatever the last thing written to the socket was replace what was there before?
    No.

    Quote Originally Posted by alansandbucket View Post
    how do i know when i've hit the EOF for the socket when reading?
    The function you use to read from the socket -- read(), readv(), recv(), recvfrom(), or recvmsg() -- returns 0 then, just like when you hit end-of-file when reading from any other socket or file descriptor.

    Perhaps the man 7 socket and man 7 unix man pages would help?

  3. #3
    Registered User
    Join Date
    May 2014
    Posts
    15
    hi, i got pointed to using the socket returned by xconnectionnumber() along with polling to create better controls. glut apparently uses the socket returned by xconnectionnumber() for controls. the abilities for controls using the standard check routines are limited and their is a delay problem between the first press of the button and when it registers that the button is held down and continually generates the keypress for being held down.

    first you didn't answer one of my questions, do i have to set up a connection using special functions for sockets once i have the socket file descriptor from the x11 server or can i just start working with it once i get it from x11?

    ill take a look at the man pages but can you give me a good tutorial for unix domain sockets?

    thanks alot

  4. #4
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Quote Originally Posted by alansandbucket View Post
    hi, i got pointed to using the socket returned by xconnectionnumber() along with polling to create better controls. glut apparently uses the socket returned by xconnectionnumber() for controls. the abilities for controls using the standard check routines are limited and their is a delay problem between the first press of the button and when it registers that the button is held down and continually generates the keypress for being held down.
    You should not rely on getting repeated keypress events to detect a key being held down. That works fine for text input, but not for games, not in any environment.

    Each keypress produces two (or more) XKeyEvents: an XKeyPressedEvent when the key is first pressed down, and an XKeyReleasedEvent when the key is released. If the key is kept depressed, multiple XKeyPressedEvents are generated.

    Instead, your XKeyEvent handler should maintain an array or table or other data structure, of the state of the buttons.

    Your game loop runs synchronized to the wall clock, real time. Each iteration of your game loop moves the characters per elapsed time; you also change the player character action according to the state of the input buttons.

    There is one related use of the socket provided by the connectionnumber(display) macro: When there are no more X events available, you can use select() on the socket descriptor, with a timeout, to wait for new events. If a new event arrives before the timeout elapses, select() will return immediately, and you can immediately process the new events. If there are no new events, select() will wait until the timeout elapses, then returns. You can then e.g. run one more iteration of your wall-clock based game loop.

    The key thing to realize here is that the X11 interface does not provide an XNextEventTimeout(display, &event, &timeout) function. If there was one, it would be much easier to write game main loops -- because using that function you could be sure that you don't wait for the next event for longer than you would otherwise wait before running the next iteration of your main loop. (All these timeouts are in wall clock time, i.e. real time.)

    Instead, we use XCheck*Event() to check if there are new events. When no more events are available, we calculate the wall clock time to the next time we'd like to run the next iteration of the main game loop -- use clock_gettime(CLOCK_REALTIME, &timespec) instead of gettimeofday() for a more precise clock --, and use select() or poll() to wait until then, or until the next event, whichever occurs first.

    Quote Originally Posted by alansandbucket View Post
    do i have to set up a connection using special functions for sockets once i have the socket file descriptor from the x11 server or can i just start working with it once i get it from x11?
    If you intend to just wait for a new event with a timeout as outlined above, the socket descriptor returned by connectionnumber()/xconnectionnumber() is ready for use with select() or poll().

    If you want to do anything else, like send or receive raw data, then we're at a completely different ballpark.
    Quote Originally Posted by alansandbucket View Post
    ill take a look at the man pages but can you give me a good tutorial for unix domain sockets?
    I'm still unsure what you need the unix domain sockets details for. (select() is not at all specific to unix domain sockets, or even sockets: it works for all kinds of file and socket descriptors.)

    If you only want to wait for new X events with a timeout, you just use select() on the file descriptor connectionnumber()/xconnectionnumber() provides. You don't need to read it, write to it, or do anything else to it -- in fact, you don't even need to know it happens to be an Unix domain socket. All you need to know is the basics of select().

  5. #5
    Registered User
    Join Date
    May 2014
    Posts
    15
    hey there, thanks for setting some things straight. i can get back to opengl things now, thanks

  6. #6
    Ticked and off
    Join Date
    Oct 2011
    Location
    La-la land
    Posts
    1,728
    Just for kicks, here is an example of how you can use select() instead of a usleep()/nanosleep(). The real world difference is that during the "waiting" period, this example handles input events immediately; while this example does not burn CPU by spinning when idle (it "sleeps" when able), this is never unresponsive; it will handle all X events as soon as they arrive.

    The example opens up a new (black) window, but draws nothing; you need to add the drawing part yourself. You can close the window normally; it responds to the window manager close atom (i.e. Alt+F4 or clicking the close window button).

    The idea of this example is that the commented section is run once every 10 ms (hundred times a second; configurable in nanoseconds via the LOOP_NS macro), with elapsed_us telling you how many microseconds (1 second = 1,000,000 µs) has elapsed since the last iteration. It also maintains the state of the cursor keys (up, down, left, and right) for you in an easily managed manner.

    Code:
    #define  _POSIX_C_SOURCE 200809L
    /*
     * Compile using
     *
     *      gcc -Wall -Wextra -O2 example.c -lX11 -o example
     *
     * and run
     *
     *      ./example
    */
    #include <stdlib.h>
    #include <sys/select.h>
    #include <stdio.h>
    #include <time.h>
    #include <X11/Xlib.h>
    #include <X11/keysym.h>
    
    /* Main loop at 10 ms = 10,000,000 ns intervals */
    #define   LOOP_NS   (10*1000*1000)
    
    #define   CONTROLS  4
    struct {
        Bool            pressed;
        KeyCode         keycode;
        KeySym          keysym;
    } control[CONTROLS] = {
        { /* [0]: */ False, 0, XK_Up    },
        { /* [1]: */ False, 0, XK_Down  },
        { /* [2]: */ False, 0, XK_Left  },
        { /* [3]: */ False, 0, XK_Right },
    };
    
    int main(void)
    {
        Display *display;
        int      screen;
        Window   window;
        Atom     wm_delete_window;
        XEvent   ev;
        int      connfd;
        long     elapsed_us;
        struct timespec curr, prev, next;
    
        display = XOpenDisplay(NULL);
        if (!display) {
            fprintf(stderr, "No X11 available.\n");
            return EXIT_FAILURE;
        }
    
        screen = DefaultScreen(display);
    
        window = XCreateSimpleWindow(display, RootWindow(display, screen),
                                     10, 10, 640, 360, 1,
                                     BlackPixel(display, screen),
                                     BlackPixel(display, screen));
        XStoreName(display, window, "X11 Input Example");
        XSelectInput(display, window, ExposureMask | KeyPressMask | KeyReleaseMask);
    
        XMapWindow(display, window);
    
        while (1) {
            XNextEvent(display, &ev);
            if (ev.type == Expose)
                break;
            else
            if (ev.type == ClientMessage &&
                ev.xclient.format == 32 &&
                (Atom)ev.xclient.data.l[0] == wm_delete_window) {
                XDestroyWindow(display, window);
                XCloseDisplay(display);
                return EXIT_SUCCESS;
            }
        }
    
        wm_delete_window = XInternAtom(display, "WM_DELETE_WINDOW", False);
        XSetWMProtocols(display, window, &wm_delete_window, 1);
    
        {
            size_t i = CONTROLS;
            while (i-->0)
                control[i].keycode = XKeysymToKeycode(display, control[i].keysym);
        }
    
        connfd = ConnectionNumber(display);
    
        clock_gettime(CLOCK_MONOTONIC, &curr);
    
        while (1) {
    
            prev = curr;
            next = curr;
            next.tv_nsec += (long)LOOP_NS;
            if (next.tv_nsec >= 1000000000L) {
                next.tv_nsec -= 1000000000L;
                next.tv_sec++;
            }
    
            while (1)
                if (XPending(display)) {
                    XNextEvent(display, &ev);
                    if (ev.type == KeyPress) {
                        size_t i = CONTROLS;
                        while (i-->0)
                            if (ev.xkey.keycode == control[i].keycode) {
                                control[i].pressed = True;
                                break;
                            }
                    } else
                    if (ev.type == KeyRelease) {
                        size_t i = CONTROLS;
                        while (i-->0)
                            if (ev.xkey.keycode == control[i].keycode) {
                                control[i].pressed = False;
                                break;
                            }
                    } else
                    if (ev.type == ClientMessage &&
                        ev.xclient.format == 32 &&
                        (Atom)ev.xclient.data.l[0] == wm_delete_window) {
                        XDestroyWindow(display, window);
                        XCloseDisplay(display);
                        return EXIT_SUCCESS;
                    }
                } else {
                    struct timeval  timeout;
                    long            timeout_us;
                    fd_set          set;
    
                    clock_gettime(CLOCK_MONOTONIC, &curr);
                    if (curr.tv_sec > next.tv_sec ||
                        (curr.tv_sec == next.tv_sec && curr.tv_nsec >= next.tv_nsec))
                        break;
    
                    timeout_us = (next.tv_nsec - curr.tv_nsec) / 1000L;
                    if (timeout_us < 0L) {
                        timeout.tv_sec = next.tv_sec - curr.tv_sec - 1;
                        timeout.tv_usec = timeout_us + 1000000L;
                    } else
                    if (timeout_us < 1000000L) {
                        timeout.tv_sec = next.tv_sec - curr.tv_sec;
                        timeout.tv_usec = timeout_us;
                    } else {
                        timeout.tv_sec = next.tv_sec - curr.tv_sec + 1;
                        timeout.tv_usec = timeout_us - 1000000L;
                    }
    
                    FD_ZERO(&set);
                    FD_SET(connfd, &set);
                    select(connfd + 1, &set, NULL, NULL, &timeout);
                }
    
            elapsed_us = (long)(curr.tv_sec - prev.tv_sec) * 1000000L
                       + (curr.tv_nsec - prev.tv_nsec) / 1000L;
    
            /* One iteration of the game loop follows.
             *
             * elapsed_us is the time in microseconds (1,000,000 us = 1 second)
             *            elapsed since the last iteration.
             * control[0].pressed is True if and only if Up key is pressed.
             * control[1].pressed is True if and only if Down key is pressed.
             * control[2].pressed is True if and only if Left key is pressed.
             * control[3].pressed is True if and only if Right key is pressed.
            */
    
        }
    }
    Questions?

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, 03:40 PM
  2. questions....so many questions about random numbers....
    By face_master in forum C++ Programming
    Replies: 2
    Last Post: 07-30-2009, 08:47 AM
  3. sockets: Why does server[buffer] > client[message]? + more questions
    By heras in forum Networking/Device Communication
    Replies: 4
    Last Post: 03-10-2008, 04:19 AM
  4. Raw Sockets and SP2...
    By Devil Panther in forum Networking/Device Communication
    Replies: 11
    Last Post: 08-12-2005, 04:52 AM
  5. Sockets, questions.
    By Vber in forum C Programming
    Replies: 2
    Last Post: 12-26-2002, 12:18 AM