PDA

View Full Version : a few questions about sockets



alansandbucket
06-22-2015, 04:12 PM
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

Nominal Animal
06-22-2015, 05:42 PM
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.


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).


does whatever the last thing written to the socket was replace what was there before?
No.


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() (http://man7.org/linux/man-pages/man2/read.2.html), readv() (http://man7.org/linux/man-pages/man2/readv.2.html), recv() (http://man7.org/linux/man-pages/man2/recv.2.html), recvfrom() (http://man7.org/linux/man-pages/man2/recvfrom.2.html), or recvmsg() (http://man7.org/linux/man-pages/man2/recvmsg.2.html) -- 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 (http://man7.org/linux/man-pages/man7/socket.7.html) and man 7 unix (http://man7.org/linux/man-pages/man7/unix.7.html) man pages would help?

alansandbucket
06-23-2015, 07:13 AM
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

Nominal Animal
06-23-2015, 08:29 AM
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) (http://linux.die.net/man/3/connectionnumber) macro: When there are no more X events available, you can use select() (http://man7.org/linux/man-pages/man2/select.2.html) 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.


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.

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() (http://man7.org/linux/man-pages/man2/select_tut.2.html).

alansandbucket
06-23-2015, 08:52 AM
hey there, thanks for setting some things straight. i can get back to opengl things now, thanks :)

Nominal Animal
06-23-2015, 10:58 AM
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.



#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?