我很抱歉问了另一个关于 Tcl 的奇怪问题。如果用户要打开多个 Tcl 本地桌面程序实例,我一直在努力弄清楚如何处理某些项目。该程序设置一个本地服务器并连接到少量本地 SQLite 数据库(其中一些附加内存数据库,显然这些数据库不能在 Tcl 程序实例之间共享,因为它们是单独的进程)并使用浏览器作为用户界面。
无论用户可能打开多少个实例,是否都无法仅运行一台本地服务器?例如,如果使用另一个数据库文件或常规文件,则当程序启动时,它会查找该文件,如果不存在,则创建该文件并在启动服务器后写入 Tcl 选择的端口号。如果该文件确实存在,它将从该文件中读取端口号并尝试作为客户端进行连接并传递请求。如果它得到预期的响应,表明服务器已经在运行,那么它会关闭文件和客户端连接,并继续启动而不启动另一个服务器。
在此设置中,所有 Tcl 程序实例共享相同的数据库连接,包括对相同内存数据库的访问,因为所有这些都归单个共享服务器所有。
我的问题是,这是一个可行的选择吗?如果是,是否有更好的方法来测试较早的实例是否已启动服务器? (当然,我会看看它是否有效;但我的问题是,即使它看起来确实有效,但出于某种原因,这是一个不好的方法吗?)
感谢您考虑我的问题。
我不知道这对其他人是否有用(我以我不是专业人士并且不会推荐我编码的任何内容作为序言),但是,在搞乱了一番之后,我意识到我的问题实际上并不是如何让多个 Tcl 实例共享一个服务器,而是强制额外尝试打开另一个实例来打开另一个浏览器实例并将其定向到已经打开的 Tcl 服务器(如果这有意义的话)。
这个例子已经可以运行了;当它确定服务器已经在运行时,它会在后台打开浏览器并将其传递给正在运行的服务器并关闭。我不知道这是一种好还是坏的方法,但是,让一台服务器与每个数据库有一个连接,会导致多个浏览器实例“知道”用户可能输入的内容,但尚未在不同的实例中保存到磁盘,因为在单个进程的内存数据库中跟踪它。
if { [catch {
set fdPort [open server_data.txt {RDWR CREAT}]
chan seek $fdPort 0
set data [chan get $fdPort] } result catchDict] } {
chan puts stdout {Failed to read/create server_data.txt.}
chan puts stdout "Error: $result $catchDict"
chan close $fdPort
exit
}
# If the file was just created, it will be empty;
# and if length != 2, it is bad/incomplete data.
if { [llength $data] == 2 } {
set addr [lindex $data 0]
set port [lindex $data 1]
if { [catch { set sock [socket $addr $port] } result catchDict] } {
# The address:port does not exist or refused
# to accept a connection, which must indicate
# it is not running the Tcl server.
chan puts stdout {Failed to connect to a server using stored data.\
Will start one.}
chan puts stdout "Error: $result $catchDict"
} else {
# Now that the socket has been opened, pass
# the fake serverping and see if get a pong.
chan configure $sock -buffering full -encoding iso8859-1 -translation crlf
chan puts $sock "GET /serverping HTTP/1.1"
chan puts $sock ""
chan flush $sock
chan close $sock write
# WARNING Need to open the socket as -async
# or time out after a very short interval
# to avoid blocking.
if { [chan gets $sock] eq {serverpong} } {
# Open the browser directed to the url and port
# instructing it to requesst the log-in page from
# the already running server; and, then, exit this
# Tcl script. NOTE Could exit and not permit more
# than one connection. Or, could track connections
# in an in-memory database by user name and limit
# those connections to one per user name.
# set pids [exec firefox -P "ToolTest" -no-remote -new-instance -offline \
# -private -url http://${addr}:${port}/login &]
# puts "-------------------------------/n$pids"
exec /usr/lib/brave-browser/brave --args --new-window\
--window-size=3000,1800 --app=http://${addr}:${port}/login &
chan close $sock
chan close $fdPort
exit
}
# Otherwise, no pong from the server and, therefore,
# must not be the Tcl server on this port.
chan puts stdout {Server did not respond; will start one.}
}
}
# Source main.tcl which will return the data
# to write to server_data.txt.
lassign [source main.tcl] addr port
chan seek $fdPort 0
chan puts $fdPort "$addr $port"
chan close $fdPort
如果程序也使用 Tk,那么您可以在 Linux 上轻松完成。使用
tk appname
为您的应用程序设置一个唯一的名称(您通常可以使用主脚本的基本名称或包含它的目录之类的名称;正确的选择往往是显而易见的)。然后使用 winfo interps
列出具有该基本名称的启用 Tk 的解释器,以查找第一个定义的解释器(通常没有数字后缀)。最后,如果您不是领导应用程序,则只需使用 send
要求领导为您做事。
package require Tk
set name myScript
tk appname $name
if {$name ne [tk appname] && $name in [winfo interps]} {
send $name [list openFiles {*}$argv]
exit
}