Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Running server and client from the same interpreter?

51 views
Skip to first unread message

jemptymethod

unread,
Feb 25, 2018, 9:27:48 AM2/25/18
to
First of all if the following is considered bad etiquette in this group let me know, but I've posted the meat of this already here: https://stackoverflow.com/q/48971272/34806 I don't know how much crossover there is between here and there, and it's probably gotten as many views there as it will. If however this is frowned on I won't do it anymore

I will give a brief summary, and am wondering something further that I did not ask in that Q&A. I also have ideas for other approaches altogether and would like input as to their viability.

Within the same file I am trying to launch a server and a client with the following code:

set port [tatu::startServer "" "" $tatu::options]

set deskmlRunner {
set viewer [file join $::starkit::topdir .. .. gui bin deskmlviewer.exe]
#puts "$viewer http://localhost:$port"
exec $viewer "http://localhost:$port"
}

after 1 $deskmlRunner
vwait forever

This launches the viewer without however rendering the content that I expect the server to return. When I comment in the puts line though I can run it's output from the command line and the viewer *does* display the expected content.

I tried a couple of minor modifications (e.g. increasing the wait parameter to after) with no success. Now I am wondering a couple of things:

A) Is it possible to launch a server and client from the same interpreter as I'm attempting? The example at the very top of http://wiki.tcl.tk/15315 does not indicate that the server and client are running from the same interpreter. Might it be worth for me then to maybe try something using interp? If at all possible I'd like to keep this all within the starkit (which the reader might have discerned from the reference to "$::starkit::topdir")

B) Alternatively I could run a starkit within a starkit and maybe use an embedded tclkitsh that will launch the starkit also check for a scratch file with the port number that I could have the starkit output (or can a starkit return a value?) But then would I possibly run into the same issue, if it's even the issue, regarding trying to run the server and client from the same interpreter?

I'm starting to confuse myself LOL so I will leave it at the above. Thanks in advance especially if you've read this far.

Rich

unread,
Feb 25, 2018, 11:25:26 AM2/25/18
to
jemptymethod <jempty...@gmail.com> wrote:
> First of all if the following is considered bad etiquette in this
> group let me know, but I've posted the meat of this already here:
> https://stackoverflow.com/q/48971272/34806 I don't know how much
> crossover there is between here and there, and it's probably gotten
> as many views there as it will. If however this is frowned on I
> won't do it anymore

Referencing somewhere else isn't frowned upon. But it is preferable if
your posting here was mostly self contained (then we don't have to go
searching elsewhere for valuable information). In your case, it looks
like you've included just enough details.

> Within the same file I am trying to launch a server and a client with
> the following code:
>
> set port [tatu::startServer "" "" $tatu::options]
>
> set deskmlRunner {
> set viewer [file join $::starkit::topdir .. .. gui bin deskmlviewer.exe]
> #puts "$viewer http://localhost:$port"
> exec $viewer "http://localhost:$port"
> }
>
> after 1 $deskmlRunner
> vwait forever

Your code is sound, but your 'concept' is what has a flaw.

The 'exec' command asks the host OS to execute something. You
reference the ::starkit global above, implying you have packed this
executable into a starkit and are trying to run it from inside the
starkit itself.

This *will not work*. The host OS has no visibility to find or access
any files inside the starkit. This is because all of the files inside
the starkit are visible only because Tcl's file I/O handling takes care
of working out "is inside starkit" vs. "is in host OS filesystem" *for
Tcl code only*. The host OS, however, does not use Tcl's file I/O
handling routines, and so it can not see inside the starkit.

If you want to launch an executable that is stored inside a starkit,
you have to first use Tcl to copy it out of the starkit into a file in
the host OS's filesystem, and then launch that copy in the host OS's
filesystem.

> A) Is it possible to launch a server and client from the same
> interpreter as I'm attempting?

Yes, provided of course that both are event driven. Trying to do this
with one or both of them non-event driven will not work well.

> Might it be worth for me then to maybe try something using interp?

If you want to learn to use multiple interp's yes. But your issue is
not with client and server in same interp, but with a missunderstanding
of how the starkit virtual filesystem works. So multiple interps will
not help the issue you seem to be presenting here now.

> If at all possible I'd like to keep this all within the starkit
> (which the reader might have discerned from the reference to
> "$::starkit::topdir")

You can, but you *must* copy out any executables (and any required
*.dll files) to host OS accessible file(s) before you can launch them
using the host OS.

> B) Alternatively I could run a starkit within a starkit and maybe use
> an embedded tclkitsh that will launch the starkit also check for a
> scratch file with the port number that I could have the starkit
> output (or can a starkit return a value?) But then would I possibly
> run into the same issue, if it's even the issue, regarding trying to
> run the server and client from the same interpreter?

Still won't work, this would still try to ask the host OS to reach
inside the starkit and find a file inside, which it *cannot* do.

Robert Heller

unread,
Feb 25, 2018, 12:22:33 PM2/25/18
to
One "solution": just re-exec the *starkit itself*, with a command like
parameter to run the client side code:

starkit main:

set clientindex [lsearch $argv -client]

if {$clientindex >= 0} {
package require client
incr clientindex
client::main [lindex $argv $clientindex]
} else {
package require server
server::main
}

Presumably, there is a server.tcl like this:

namespace eval server {

proc main {} {

set port [tatu::startServer "" "" $tatu::options]
# I am assuming somewhere in startServer is something like
# socket -server tatu::Server localhost $tatuport
# return $tatuport

after 100 {exec [info nameofexecutable] -client "http://localhost:$port"}
vwait forever
}
}

package provide server 1.0

and there is a client.tcl like this:

namespace eval client {

proc main {url} {
package require Tk
regexp {^http://([^:]+):([[:digit:]])+$} $url => host port
set socket [socket $host $port]
# build the viewer gui, etc.
tatu::viewer $socket
# no need for "vwait forever", since Tk enters the event loop on its own
}
}

package provide client 1.0



>
>

--
Robert Heller -- 978-544-6933
Deepwoods Software -- Custom Software Services
http://www.deepsoft.com/ -- Linux Administration Services
hel...@deepsoft.com -- Webhosting Services

jemptymethod

unread,
Feb 25, 2018, 12:27:53 PM2/25/18
to
Sorry if I was not entirely clear. Two things a) my starkit is currently unwrapped and more importantly b) the executable is actually not inside the starkit, note how the following first navigates two directories up from the starkit's topdir (I've also used a similar technique to serve the content from a directory outside the starkit)

set viewer [file join $::starkit::topdir .. .. gui bin deskmlviewer.exe]

> > A) Is it possible to launch a server and client from the same
> > interpreter as I'm attempting?
>
> Yes, provided of course that both are event driven. Trying to do this
> with one or both of them non-event driven will not work well.

Can you provide more details on the above, because it's pretty obvious my client script is not event driven

Thanks for post and sorry for any confusion...George

Robert Heller

unread,
Feb 25, 2018, 1:33:55 PM2/25/18
to
At Sun, 25 Feb 2018 09:27:45 -0800 (PST) jemptymethod <jempty...@gmail.com> wrote:

>
> On Sunday, February 25, 2018 at 10:25:26 AM UTC-6, Rich wrote:
> > jemptymethod <jempty...@gmail.com> wrote:
> > > First of all if the following is considered bad etiquette in this
> > > group let me know, but I've posted the meat of this already here:
> > > https://stackoverflow.com/q/48971272/34806 I don't know how much
> > > crossover there is between here and there, and it's probably gotten
> > > as many views there as it will. If however this is frowned on I
> > > won't do it anymore
> >=20
> > Referencing somewhere else isn't frowned upon. But it is preferable if
> > your posting here was mostly self contained (then we don't have to go
> > searching elsewhere for valuable information). In your case, it looks
> > like you've included just enough details.
> >=20
> > > Within the same file I am trying to launch a server and a client with
> > > the following code:
> > >=20
> > > set port [tatu::startServer "" "" $tatu::options]
> > >=20
> > > set deskmlRunner {
> > > set viewer [file join $::starkit::topdir .. .. gui bin deskmlviewer.=
> exe]
> > > #puts "$viewer http://localhost:$port"
> > > exec $viewer "http://localhost:$port"
> > > }
> > >=20
> > > after 1 $deskmlRunner
> > > vwait forever
> >=20
> > Your code is sound, but your 'concept' is what has a flaw.
> >=20
> > The 'exec' command asks the host OS to execute something. You
> > reference the ::starkit global above, implying you have packed this
> > executable into a starkit and are trying to run it from inside the
> > starkit itself.
>
> Sorry if I was not entirely clear. Two things a) my starkit is currently u=
> nwrapped and more importantly b) the executable is actually not inside the =
> starkit, note how the following first navigates two directories up from the=
> starkit's topdir (I've also used a similar technique to serve the content =
> from a directory outside the starkit)
>
> set viewer [file join $::starkit::topdir .. .. gui bin deskmlviewer.exe]=20
>

OK, what you really want to do here is:

set viewer [file join [file dirname [info nameofexecutable]] .. .. gui bin deskmlviewer.exe]

I don't think $::starkit::topdir is ever going to give you a real O/S
directory. It is the "mount point" of the virtual FS.

> > > A) Is it possible to launch a server and client from the same
> > > interpreter as I'm attempting?
> >=20
> > Yes, provided of course that both are event driven. Trying to do this
> > with one or both of them non-event driven will not work well.
>
> Can you provide more details on the above, because it's pretty obvious my c=
> lient script is not event driven

Is the client script using Tk? If so it *is* event driven (or at least there
is an event loop being used by the GUI, whether your socket I/O is event
driven or not is a separate issue (if it is using the http Tcl package (eg
package require http), then it is event driven.

>
> Thanks for post and sorry for any confusion...George
>

Rich

unread,
Feb 25, 2018, 2:02:54 PM2/25/18
to
Robert Heller <hel...@deepsoft.com> wrote:
> One "solution": just re-exec the *starkit itself*, with a command like
> parameter to run the client side code:

He's not running tcl code via exec, he's trying to run
"deskmlviewer.exe" via exec, with "deskmlviewer.exe" being packaged
inside the starkit.

jemptymethod

unread,
Feb 25, 2018, 2:07:38 PM2/25/18
to
On Sunday, February 25, 2018 at 12:33:55 PM UTC-6, Robert Heller wrote:
>
> OK, what you really want to do here is:
>
> set viewer [file join [file dirname [info nameofexecutable]] .. .. gui bin deskmlviewer.exe]
>
> I don't think $::starkit::topdir is ever going to give you a real O/S
> directory. It is the "mount point" of the virtual FS.

I think you would be surprised. 5 years ago I modified the Tatu server to serve content from a directory where it would be a sibling to the starkit:

variable root [file join $::starkit::topdir .. htdocs]

This worked even when the starkit was wrapped, and made it possible for users to serve content from a directory outside the starkit without having to continually unwrap and wrap it.

For instance, here is the output from my commented out puts in one of my runs (sorry this is one the details I put in my SO question but not here):

puts "$viewer http://localhost:$port"
C:/opt/dev/dexygen/github/repos/deskml/deskml-win/srv/tatuDeskml.vfs/../../gui/bin/deskmlviewer.exe http://localhost:56077

The issue I'm currently facing is my code actually launches the viewer successfully, but does not display the content at http://localhost:56077 When I copy and paste the above to the command line however the viewer does get launched *and* displays the expected output. It's almost as though some sort of flush of the output needs to occur. Something else I might try to do is go back to the QT/C++ code (something I'm even less of an "expert" with than Tcl) for the viewer and confirm what it receives as the url parameter to be sure that is correct

Rich

unread,
Feb 25, 2018, 2:14:37 PM2/25/18
to
jemptymethod <jempty...@gmail.com> wrote:
> On Sunday, February 25, 2018 at 10:25:26 AM UTC-6, Rich wrote:
>> jemptymethod <jempty...@gmail.com> wrote:
>> > Within the same file I am trying to launch a server and a client
>> > with the following code:
>> >
>> > set port [tatu::startServer "" "" $tatu::options]
>> >
>> > set deskmlRunner {
>> > set viewer [file join $::starkit::topdir .. .. gui bin deskmlviewer.exe]
>> > #puts "$viewer http://localhost:$port"
>> > exec $viewer "http://localhost:$port"
>> > }
>> >
>> > after 1 $deskmlRunner
>> > vwait forever
>>
>> Your code is sound, but your 'concept' is what has a flaw.
>>
>> The 'exec' command asks the host OS to execute something. You
>> reference the ::starkit global above, implying you have packed this
>> executable into a starkit and are trying to run it from inside the
>> starkit itself.
>
> Sorry if I was not entirely clear.

Fine. You'll try to be more clear next time, right?

> Two things a) my starkit is currently unwrapped and more importantly
> b) the executable is actually not inside the starkit, note how the
> following first navigates two directories up from the starkit's
> topdir (I've also used a similar technique to serve the content from
> a directory outside the starkit)
>
> set viewer [file join $::starkit::topdir .. .. gui bin deskmlviewer.exe]

Ah, ok, so the executable is not inside the starkit afterall. Note
that this is fragile in that if someone moves the starkit, they also
have to separately remember to move the other exe as well.

>> > A) Is it possible to launch a server and client from the same
>> > interpreter as I'm attempting?
>>
>> Yes, provided of course that both are event driven. Trying to do
>> this with one or both of them non-event driven will not work well.
>
> Can you provide more details on the above, because it's pretty
> obvious my client script is not event driven

Well, for TCP sockets, the server socket is already 'event driven'
(its -command option is called each time a new connection is accepted
on the socket).

If your 'activity' within the proc called from the server's -command
option is short enough, you can get away with it being non-event
driven, because it would do its thing, return its data, and exit
quickly enough to not be noticed in many situations.

But to event drive it, within the -command callback you'd use
fconfigure (or "chan configure", a different access to the same
plumbing) to toggle the socket handed to you as a parameter to the
-command proc into non-blocking mode, and you'd setup "fileevent" (or
"chan event", same plumbing, different access point) callbacks for
readability and writablility (as needed for your 'server') events
on the socket, then the proc would terminate.

At that point, any 'work' you do would occur within the readable and/or
writable event call backs you setup above.

This might be a reasonable example to look over:
http://wiki.tcl.tk/1757

Rich

unread,
Feb 25, 2018, 2:36:15 PM2/25/18
to
jemptymethod <jempty...@gmail.com> wrote:
> The issue I'm currently facing is my code actually launches the
> viewer successfully, but does not display the content at
> http://localhost:56077

Ah, ok, this small detail is something that would have been helpful to
have made *very* explicit in your first posting.

Looking back, here's how you launch the external viewer:

exec $viewer "http://localhost:$port"

That is synchronous. The 'exec' command waits for the viewer to
terminate before control returns to your Tcl script.

Which is why your viewer retreives no content from the TCP port. The
Tcl script behind the port is paused while the viewer is running.

Add an ampersand (&) to the end of your exec line, like so:

exec $viewer "http://localhost:$port" &

That will launch the viewer in the background, returning control to the
Tcl script as soon as the background launch by the OS is done.

Then your script will be running again to listen to connect attempts on
the TCP port.

> When I copy and paste the above to the command line however the
> viewer does get launched *and* displays the expected output.

Here you are launching your viewer outside the Tcl script, so the
script is not paused waiting for the viewer to exit.

> It's almost as though some sort of flush of the output needs to
> occur.

No, you simply forgot to background the viewer so control would return
to your Tcl script.

blacksqr

unread,
Feb 25, 2018, 2:51:11 PM2/25/18
to
I don't know anything about deskmlviewer, but my guess is that the exec command to start it never returns because you didn't specify that the process should run in the background. Thus your code is stuck at the exec command, so the vwait command is never reached and your program never goes into an event loop. This prevents your server from operating.

Try putting a "&" at the end of the exec command. This will cause the client to run in the background, and exec will return immediately, allowing the rest of your code to run.

jemptymethod

unread,
Feb 25, 2018, 3:54:47 PM2/25/18
to
On Sunday, February 25, 2018 at 1:14:37 PM UTC-6, Rich wrote:
> jemptymethod <jempty...@gmail.com> wrote:
> >
> > set viewer [file join $::starkit::topdir .. .. gui bin deskmlviewer.exe]
>
> Ah, ok, so the executable is not inside the starkit afterall. Note
> that this is fragile in that if someone moves the starkit, they also
> have to separately remember to move the other exe as well.

Good point. I'd thought of running a starkit within a starkit but that will just add complexity. I will probably move the deskmlviewer into a bin directory inside the starkit after I get the original issue solved. About to check your suggested solution....

jemptymethod

unread,
Feb 25, 2018, 3:59:33 PM2/25/18
to
On Sunday, February 25, 2018 at 1:36:15 PM UTC-6, Rich wrote:
> jemptymethod <jempty...@gmail.com> wrote:
> > The issue I'm currently facing is my code actually launches the
> > viewer successfully, but does not display the content at
> > http://localhost:56077
>
> Ah, ok, this small detail is something that would have been helpful to
> have made *very* explicit in your first posting.
>
> Looking back, here's how you launch the external viewer:
>
> exec $viewer "http://localhost:$port"
>
> That is synchronous. The 'exec' command waits for the viewer to
> terminate before control returns to your Tcl script.
>
> Which is why your viewer retreives no content from the TCP port. The
> Tcl script behind the port is paused while the viewer is running.
>
> Add an ampersand (&) to the end of your exec line, like so:
>
> exec $viewer "http://localhost:$port" &
>
> That will launch the viewer in the background, returning control to the
> Tcl script as soon as the background launch by the OS is done.
>
> Then your script will be running again to listen to connect attempts on
> the TCP port.

Worked like a charm! I don't know what my bigger takeaway is from this, your answer/explanation above, or, if I ever cross-post again, do so verbatim.

Thanks so much and hope I haven't wasted too much of your time.

George
0 new messages