started by Theo Verelst
To make use a pipe or as here a general socket [L1 ] [L2 ] to connect a user interface with a progam or to link programs together is a long used and tried solution since at least X windows. Not that that always worked perfectly, but its pretty ok.
Also web servers/browsers, ftp, etc are examples of existing progams making use of socket IPC. The principle is clear enough: one makes a stream connection, and sends messages across with commands and data [L3 ].
In practice, this doesn't work out all too easy, usually. This page presents an example to do most work completely automatically to make a tcl program connect with a C program, and have the C program execute functions under control of the tcl program (alternatively: C compiled image processing on an interactive Bwise canvas).
Also, an example is easily created, where Tk is used to have a stack of buttons for testing this.
On this page I used a recent windows XP running KDE (on an X simulator), and my own compiled tcl/tk 8.4 (but like on linux) in kde windows. Also, I used the cygwin unix-like environment including gcc compiler for windows. Most or all of the materials presented here should run equally well on linux/unix, and probably other os-es, provided they have a C compiler with unix flavour sockets.
Major issues when making a socket link and programming code around it are:
The approach taken for this test version consists of the following steps:
# NOTE this proc is also defined in BWise proc open_text { {n {}} } { global textname if {[winfo exists .tt] == 0} { toplevel .tt set textname $n text .tt.t -width 40 -height 8 frame .tt.f entry .tt.f.e -textvar textname -width 30 button .tt.f.s -text Save -command { global textname; set f [open $textname w]; puts -nonewline $f [.tt.t get 0.0 end]; close $f } button .tt.f.l -text Load -command { global textname; .tt.t del 0.0 end; set f [open $textname r]; while {[eof $f] == 0} { .tt.t insert end "[gets $f]\n" }; close $f } bind .tt.f.e <Double-Button> { set textname [tk_getOpenFile] } pack .tt.t -expand y -fill both pack .tt.f -side bottom -expand n -fill x pack .tt.f.e -side left -expand y -fill x pack .tt.f.s -side right pack .tt.f.l -side right } { set textname $n } if {$textname != {}} { .tt.f.l invoke } } proc soccdecodefunc { } { global socfs .tt.t insert 0.0 {#include<string.h>} .tt.t insert 0.0 "\n#include<stdio.h>\n" # What follows is not an error, it is pre-fab C-code .tt.t insert end { int socdecode(m) char m[]; } .tt.t insert end \{\n set j 0 foreach i $socfs { .tt.t insert end " if \(strcmp\(m,\"$i\"\) == 0) \{$i\(\); return\($j\);\}\n" incr j; } .tt.t insert end "\}\n\n" } proc socconnect { {port {4100}} } { global socsockid if ![info exists socsockid] { set socsockid stdout} if {$socsockid == "stdout"} { catch {set socsockid [socket localhost $port]} if {$socsockid == "stdout"} {puts "connect failed\n"; set socsockid stdout; return} ; fileevent $socsockid readable { if [eof $socsockid] { close $socsockid; set socsockid stdout } { # print incoming lines on stdout puts "[gets $socsockid]" } } } { puts "soc: connect tried while allready connected, ignored\n" } } proc socfuncframe { {n} } { # make a simple C function body for message n in the tt.t text widget global socfs .tt.t insert end "$n\(\)\n{\n printf(\"called:$n\\n\");\n}\n\n" lappend socfs $n } proc socgencframe { {messagenames {message1 message2 message3}} } { # generate C funtions frame in .tt.t text widget # and create window with buttons for each message global socfs .tt.t del 0.0 end ; set socfs $messagenames soccdecodefunc catch {unset socfs}; foreach m $messagenames {socfuncframe $m} ; socgenui } proc socgenui { } { global socfs catch {toplevel .socbuts} foreach i [winfo children .socbuts] {destroy $i} foreach i $socfs { pack [button .socbuts.$i -text $i -command "socsend $i"] -side top -fill x } } proc socsend { {m} } { global socsockid if [eof $socsockid] { close $socsockid; set socsockid stdout} puts $socsockid $m flush $socsockid }
/* serv2.c */ /* server exa */ #include "cygwin/socket.h" #include "cygwin/in.h" /* this is non-checked for now */ #define INMAX 8*1024 #define SERV_TCP_PORT 4100 int sockfd,newsockfd,clilen; struct sockaddr_in cli_addr, serv_addr; char inlin[INMAX]; struct timeval timeout; fd_set fdvar; int do_read() { int rr, n; n = INMAX; FD_ZERO(&fdvar); FD_SET(newsockfd, &fdvar); timeout.tv_sec = 0; timeout.tv_usec = 0; if (select(newsockfd+1,&fdvar,(fd_set *) 0, (fd_set *) 0, (struct timeval *) 0) <= 0) return(0); if (FD_ISSET(newsockfd,&fdvar) == 0) { return(0); } rr = read(newsockfd,inlin,n); if (rr <= 0) { rr = 0; } inlin[rr] = '\0'; return(rr); } int do_write(b,n) char b[]; int n; { int rr; rr = write(newsockfd,b,n); return(rr); } serv_main() { if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) <0 ) { printf("Error: can't open socket.\n"); exit(-1); } bzero( (char *) &serv_addr, sizeof(serv_addr) ); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); serv_addr.sin_port = htons(SERV_TCP_PORT); if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { printf("Error: cannot bind socket.\n"); exit(-1); } printf("waiting for connection ...\n"); listen(sockfd,5); clilen = sizeof(cli_addr); newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen); if (newsockfd < 0) { printf("Error opening new socket.\n"); exit(-1); } FD_ZERO(&fdvar); FD_SET(newsockfd, &fdvar); timeout.tv_sec = 0; timeout.tv_usec = 0; inlin[0] = '\0'; }
/* stub.c */ #include<stdio.h> extern int serv_main(); extern int do_read(); extern int do_write(); extern char inlin[]; extern socdecode(char *m); main() { int l; serv_main(); while (1) { if ((l = do_read()) >0) { printf("C received string:%s",inlin); inlin[l-2] = '\0'; socdecode(inlin); do_write(inlin,l-2); do_write("\n",1); } } }
set l {}; for {set i 0} {$i < 10} {incr i} {lappend l "message$i"} socgencframe $l socconnect gcc -c serv2.c gcc -c stub.c gcc -c test.c gcc -o stub.exe stub.o serv2.o test.o ./stub
Motivations and subsequent work include: