Sorry for ugly formatting. I do not know what is happened. Source of example in attachment.
От: Vladimir Отправлено: 1 февраля 2023 г. в 21:44 Кому: xotcl@alice.wu.ac.at Тема: object method as (fileevent) callback
Hello, I have a question.
It is a simple Server example TclOO implementaiton below. I mark with yellow two lines, where I can use mymethod command to set private methods of class as callbacks. Both of this callbacks execute in class instance context. Is it possible do same thing with nx? I want incapsulate server logic inside nx::Class and set private method as callback to accept connection and reading data.
#!/usr/bin/env tclsh
package require oo::util ;# for 'mymethod' command package require logger
logger::initNamespace proj::Server debug
oo::class create proj::mock::Server {
constructor { port } { my variable projVersion_ my variable port_ my variable socket_
set projVersion_ "0.1" set port_ $port
::logger::tree::proj::Server::debug "Init mock Server $projVersion_"
# it works! set socket_ [socket -server [mymethod Accept_clb] $port] } ;# constructor
method Accept_clb { sock addr port } { my variable clients_
::logger::tree::proj::Server::debug "Accepting connection with $addr $port" set clients_(addr,$sock) [list $addr $port]
fconfigure $sock -translation binary -buffering none fileevent $sock readable [mymethod Echo_clb $sock] ;# it works too! ::logger::tree::proj::Server::debug "Setting callback private method" } ;# Accept_clb
method Echo_clb { sock } { my variable clients_
::logger::tree::proj::Server::debug "Readable callback call"
if { [eof $sock] } { ::logger::tree::proj::Server::debug "No more data in socket"
close $sock unset clients_(addr,$sock) } else { set binary_msg [read $sock 2] binary scan $binary_msg "H*" string_msg
::logger::tree::proj::Server::debug "Readed 2 bytes"
if [string length $string_msg] { ::logger::tree::proj::Server::debug "Sending 2 bytes left"
puts -nonewline $sock $binary_msg } } } ;# Echo_clb
destructor { my variable socket_ close $socket_
::logger::tree::proj::Server::debug "Destroing Server" } ;# destructor
} ;# dms::mock::server
set s [proj::mock::Server new 1950]
vwait forever
Dear Vladimir,
On 01.02.23 19:47, Vladimir wrote:
Sorry for ugly formatting. I do not know what is happened. Source of example in attachment.
The formatting was not so bad, but the yellow markup was missing. Below is an implementation of the logic of your echo server in nx.
The callback methods have to be public, since these are called from the Tcl event loop, i.e. outside of the server object scope. Therefore the callback methods "accept" and "echo" are required to be public.
Probably, it would be possible to implement a variant of ":callback "without this requirement, using some internal nsf magic, but i am not sure this would be good.
Hope this helps!
-gn
====================================================================== #!/usr/bin/env tclsh package require nx::trait
namespace eval ::proj {
nx::Class create Server { set :version 0.1
:property port:integer :require trait nx::trait::callback
:method log {msg} { puts stderr "[namespace current]::Server ([self]): $msg" }
:method init {} { :log "Init mock Server [set [current class]::version]" set :listen [socket -server [:callback accept] ${:port}] }
:method destroy {} { close ${:listen} next }
:public method accept {sock addr localport} { :log "accept $sock $addr $localport" fconfigure $sock -translation binary -buffering none fileevent $sock readable [:callback echo $sock] }
:public method echo {sock} { :log "... $sock echo" if { [eof $sock] } { close $sock :log "... $sock close" } else { set binary_msg [read $sock 2] binary scan $binary_msg "H*" string_msg :log "... $sock read 2 bytes 0x$string_msg" if {[string length $string_msg] > 0} { :log "... $sock echo" puts -nonewline $sock $binary_msg } } } } }
set s [proj::Server new -port 1950] vwait forever ======================================================================