Висячие окна при использовании сокетов TCL

Я использую команду сокета TCL для связи между двумя приложениями TCL/Tk, скажем, A и B, где оба имеют связанный GUI. Сервер на B в основном принимает команды от A и возвращает результат выполнения. Проблема в том, что графический интерфейс для B зависает, как только выполнение завершено. Есть ли способ, позволяющий двум графическим интерфейсам работать независимо друг от друга? Я использую следующие сценарии для настройки соединения между двумя приложениями:

Запустите приложение A с помощью следующего сценария TCL client.tcl

proc execute { cmd } {
    set cid [socket localhost 9900]
    puts $cid $cmd
    while { [gets $cid line] >= 0 } {
        puts $line
    } 
    close $cid
}

set pid [exec B server.tcl &]

execute {puts HelloWorld}

где server.tcl настраивает сервер через приложение B

proc server { cid addr port } {
     set cmd [gets $cid]
     catch $cmd result
     puts $cid result
     close $cid
}

socket -server server 9900

vwait forever

Цель состоит в том, чтобы позволить GUI для B быть активным, пока пользователь продолжает работать с GUI для A. Таким образом, пользователь может переключаться между двумя GUI по мере необходимости. И A, и B предоставляют разные функциональные возможности для работы с одними и теми же данными, которые должны быть доступны одновременно.

1 ответ

Решение

Код, который вы опубликовали, имеет две ключевые проблемы:

  1. Клиент не сбрасывает свой сокет после записи в него части работы (т. Е. Команды для выполнения), потому что каналы по умолчанию (не stdin, stdout, stderr) полностью буферизируются по умолчанию из соображений производительности. Это может привести к тому, что сервер никогда не получит команду.

  2. Сервер не обрабатывает принятый сокет в неблокирующем режиме через fileeventсценарий.

Кроме того, у вас есть небольшая проблема в том, что если вы когда-нибудь захотите отправить многострочные данные, ваш протокол окажется неадекватным. Вы можете или не можете заботиться об этом, и есть несколько различных способов исправить это.

Ключевая проблема 1

Лучший способ справиться с большой проблемой клиента - это использовать

fconfigure $cid -buffering none

сразу после открытия розетки. Это говорит Tcl, что он всегда должен отправлять вам данные puts к каналу на розетке немедленно. (В качестве альтернативы используйте line вместо none получить буферизацию строки; это будет работать достаточно хорошо и в этом случае.) Вы также можете сделать явный сброс:

flush $cid

Это будет идти после puts,

Ключевая проблема 2

В соответствии с передовой практикой серверный сценарий должен быть гораздо более похожим на это: одна процедура предназначена для настройки обслуживания принятого соединения с сокетом, а другая - для обработки чтения и записи сообщений. Процедура принятия (server) отключает блокировку (и часто также регулирует буферизацию) и порядок работы (readLine) использует eof а также fblocked работать, если gets удалось или не удалось.

proc server { cid addr port } {
    fconfigure $cid -blocking 0
    fileevent $cid readable "readLine $cid"
}

proc readLine { cid } {
    set cmd [gets $cid]
    if { [eof $cid] } {
        close $cid
    } elseif { ![fblocked $cid] } {
        catch $cmd result
        puts $cid $result
        close $cid
    }
}

socket -server server 9900
vwait forever

Другая проблема

Другая большая проблема, если вы делаете это по-настоящему, состоит в том, что многострочные сообщения полезны. Либо вам нужно сделать readLine выше накапливать строки, пока не получится целая команда (append а также info complete помогите здесь) или вам понадобится какой-то другой механизм для обработки данных. В производственном коде становится проще делегировать пакет, такой как…

Другие вопросы по тегам