Cookbook/Server Calling the Client

From Kx Wiki
Jump to: navigation, search

The wiki is moving to a new format and this page is no longer maintained. You can find the new page at code.kx.com/q/cookbook/server-calling-client.

The wiki will remain in place until the migration is complete. If you prefer the wiki to the new format, please tell the Librarian why.

This demonstrates how to simulate a C client handling a "get" call from a kdb+ server. The Java interface allows the programmer to emulate a kdb+ server. The C interface does not provide a means to respond to a sync call from the server but async responses (message type 0) can be sent using k(-c,...).

A get call may be desirable when client functions need to be called by the server - as though the client were an extension. This q code shows how a listening kdb+ server can call a kdb+ client (with handle h) using async messaging only:

q)f:{neg[h]({neg[.z.w]value x};x);h[]} 
q)f"1+1"
2

Generally, async "set" messages to the client are preferable because the server has many clients and does not want to be blocked by a slow response from any one client. One application of simulated get from the server is where an extension might have been the solution to a problem, but an out-of-process solution was preferred because:

Essential Example

These programs work when the kdb+ server has only has one client (sc) in the example below.

sc.c defines two functions, home and palindrome that may be called by the server.
When the server is run as q sc.q -p 5001 and then sc is run, sc will connect to `::5001.
In q, .z.po is called when sc connects. .z.po then saves the socket (h) and calls GET` to find the list of functions the client provides.
fs is called to eval a new function definition for home and palindrome. Then in q:

$ q sc.q -p 5001
q)home`
"/home/jack"
q)palindrome home`
"/home/jackkcaj/emoh/"

Here is what sc.q defined when it received the list of functions from the client:

q)home
{GET[(`home;0;x)]}
q)palindrome
{GET[(`palindrome;1;x)]}
GET:{(neg h)x;x:h[];x[1]}
S:string
fs:{{eval parse s,":{GET[(`",(s:S x[0]y),";",(S y),";",(";"sv S x[1;y]#"xyz"),")]}"}[x]each til count x}
.z.po:{h::x;fs GET`}
//sc.c  server calls client with simulated GET.   gcc sc.c -o sc -DKXVER=3 -pthread l64/c.o
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"k.h"
#define A(x)     if(!(x))O("A(%s)@%d\n",#x,__LINE__),exit(*(S)0);  //assert - simplistic error handling

Z K1(home){S s=getenv("HOME");x=ktn(KC,strlen(s));DO(xn,xC[i]=s[i])R x;}
Z K1(palindrome){A(xt==KC);C c,*d;K k=ktn(KC,xn*2);DO(xn,kC(k)[i]=xC[i]);DO(xn,kC(k)[xn+i]=xC[xn-1-i]);R k;}
ZK(*f[])()={home,palindrome,0};ZS n[]={"home","palindrome",0};ZJ a[]={1,1};//exported functions and their arity

Z K1(call)//remote sends atom or (`palindrome;0;x) or (`home;1;)
{K1(d){K k=ktn(KS,0),v=ktn(KJ,0);J i=0;while(f[i])js(&k,ss(n[i])),ja(&v,a+i),i++;R knk(2,k,v);}
 P(0>xt,d(0));A(xt==0);A(xn>1);A(xx->t==-KS);R f[xy->j](xK[2]);
}
ZI sel(I c,F t)
{A(2<c);I r;fd_set f,*p=&f;FD_ZERO(p);FD_SET(c,p);long s=t,v[]={s,1e6*(t-s)};
 A(-1<(r=select(c+1,p,(V*)0,(V*)0,(V*)v)));P(r&&FD_ISSET(c,&f),c)R 0;
}
ZK sr(I c){I t;K x;A(x=k(c,(S)0));R k(-c,"",call(x),(K)0);} //async from q
I main(I n,S*v){I c=khp("",5001);while(1)if(c==sel(c,1e-2))A(sr(c));}

a TUI

Consider a C client (u) that is nothing but a TUI - it exposes ncurses functionality for a kdb+ listener. For fun, Conway's game of Life will play out on u - all drawn by a q program.

$ cat t.c
#include<curses.h>
#include"k.h"

TODO...

see also IPC Cookbooks

Personal tools
Namespaces
Variants
Actions
Navigation
Print/export
Toolbox