fileevent — 채널이 읽거나 쓸 수 있게 될 때 스크립트 실행하기
fileevent — 채널이 읽거나 쓸 수 있게 될 때 스크립트 실행하기
네트워크나 다른 프로세스에서 데이터를 받는 동안에도 사용자와 계속 상호작용하고 싶을 때가 있어요. 데이터가 도착할 때를 알 수 있다면 그때만 읽으면 되죠. 그렇게 채널이 읽거나 쓸 수 있게 되는 이벤트에 스크립트를 묶어주는 명령이 fileevent예요.
본문
시그니처
fileevent channelId readable ?script?
fileevent channelId writable ?script?
설명
이 명령은 파일 이벤트 핸들러를 만드는 데 쓰여요. 파일 이벤트 핸들러는 채널과 스크립트 사이의 바인딩으로, 채널이 읽거나 쓸 수 있게 될 때마다 스크립트가 평가돼요. 파일 이벤트 핸들러는 보통 다른 프로세스에서 데이터를 이벤트 중심으로 받게 해, 수신자가 데이터가 도착하는 동안 사용자와 계속 상호작용할 수 있게 하는 데 쓰여요. 응용 프로그램이 입력 데이터가 없을 때 차단 채널에서 gets·read를 호출하면 프로세스는 차단돼요. 입력 데이터가 도착할 때까지 다른 이벤트를 서비스할 수 없어 사용자에게는 "멈춘 것"처럼 보여요. fileevent가 있으면 프로세스는 데이터가 있는 때를 알 수 있고, 차단되지 않을 때만 gets·read`를 호출할 수 있어요.
fileevent의 channelId 인자는 표준 채널(stdin, stdout, stderr), open·socket 호출의 반환값, 또는 Tcl 확장이 제공한 채널 생성 명령의 결과 같은 열려 있는 채널을 가리켜요.
script 인자가 지정되면 fileevent는 새 이벤트 핸들러를 만들어요. 채널이 읽거나 쓸 수 있게 될 때마다(fileevent의 둘째 인자에 따라) script가 평가돼요. 이 경우 fileevent는 빈 문자열을 반환해요. 파일의 readable·writable 이벤트 핸들러는 독립적이고, 각각 따로 만들고 삭제할 수 있어요. 다만 주어진 인터프리터에서 주어진 시점에 파일당 readable 핸들러 하나·writable 핸들러 하나가 최대예요. 호출하는 인터프리터에 지정 핸들러가 이미 존재할 때 fileevent를 호출하면 새 스크립트가 옛 것을 대체해요.
script 인자를 지정하지 않으면 fileevent는 channelId의 현재 스크립트를 반환하고, 없으면 빈 문자열을 반환해요. script 인자를 빈 문자열로 지정하면 이벤트 핸들러는 삭제되어 어떤 스크립트도 호출되지 않아요. 파일 이벤트 핸들러는 채널이 닫히거나 인터프리터가 삭제될 때도 자동으로 삭제돼요.
채널은 기본 장치에 읽지 않은 데이터가 있으면 읽을 수 있는 것으로 간주돼요. 또한 입력 버퍼에 읽지 않은 데이터가 있으면 읽을 수 있는 것으로 간주되는데, 채널에서 읽으려던 가장 최근 시도가 입력 버퍼에서 완전한 줄을 찾지 못한 gets 호출이었던 특별한 경우는 제외해요. 이 기능 덕분에 비차단 모드에서 이벤트를 사용해 파일을 한 줄씩 읽을 수 있어요. 또한 기본 파일·장치에 파일 끝이나 에러 조건이 있으면 채널은 읽을 수 있는 것으로 간주돼요. 이런 조건을 script가 확인해 적절히 처리하는 것이 중요해요. 예를 들어 파일 끝에 대한 특별한 확인이 없으면, script가 데이터를 읽지 않고 반환해 즉시 다시 호출되는 무한 루프가 발생할 수 있어요.
채널은 차단 없이 기본 파일·장치에 바이트 하나 이상을 쓸 수 있거나, 기본 파일·장치에 에러 조건이 있으면 쓸 수 있는 것으로 간주돼요.
이벤트 중심 I/O는 fconfigure 명령으로 비차단 모드에 둔 채널에서 가장 잘 동작해요. 차단 모드에서는 기본 파일·장치가 받아들일 수 있는 것보다 많은 데이터를 주면 puts 명령이 차단할 수 있고, 준비된 것보다 많은 데이터를 읽으려 하면 gets·read 명령이 차단할 수 있어요. 읽을 수 있는 기본 파일·장치도 차단 [read 1]이 성공할 것을 보장하지 못할 수 있어요(반례: 멀티바이트 인코딩, 압축·암호화 변환). 이 모든 경우에 명령이 차단되는 동안에는 이벤트가 전혀 처리되지 않아요.
비차단 모드에서 puts, read, gets는 절대 차단하지 않아요. 개별 명령들이 차단·비차단 채널을 어떻게 처리하는지에 대한 정보는 각 명령의 문서를 보세요.
파일 끝 조건 테스트는 채널 데이터를 읽으려는 어떤 시도 후에 해야 해요. 데이터 끝을 읽으려는 시도가 발생한 후에 eof 플래그가 설정되고, 그 읽기 전에 테스트하면 추가 이벤트가 발생해야 해요.
파일 이벤트의 스크립트는 fileevent 명령이 호출된 인터프리터의 전역 수준(Tcl 프로시저의 컨텍스트 밖)에서 실행돼요. 스크립트 실행 중 에러가 발생하면 interp bgerror에 등록된 명령이 에러를 보고하는 데 쓰여요. 게다가 파일 이벤트 핸들러는 에러를 반환하면 삭제되는데, 버그 있는 핸들러로 인한 무한 루프를 막기 위해서예요.
예제
이 구성에서 $chan이 읽을 수 있게 될 때마다 GetData가 채널을 인자로 호출돼요. read 호출은 현재 사용 가능한 이진 데이터를 차단 없이 읽어요. 여기서는 파일 끝이 발생하면 반복 호출을 피하려고 채널에서 fileevent를 제거해요(위 참고). 대안으로 이 조건에서 채널을 닫을 수도 있어요.
proc GetData {chan} {
set data [read $chan]
puts "[string length $data] $data"
if {[eof $chan]} {
fileevent $chan readable {}
}
}
fconfigure $chan -blocking 0 -translation binary
fileevent $chan readable [list GetData $chan]
다음 예제는 줄 지향 데이터를 읽기 위해 gets를 쓰는 것을 보여줘요.
proc GetData {chan} {
if {[gets $chan line] >= 0} {
puts $line
}
if {[eof $chan]} {
close $chan
}
}
fconfigure $chan -blocking 0 -buffering line -translation crlf
fileevent $chan readable [list GetData $chan]
크레딧
fileevent는 Mark Diekhans가 만든 addinput 명령에 기반해요.
더 알아보기
fconfigure,gets,interp,puts,read,Tcl_StandardChannels- 키워드:
asynchronous I/O,blocking,channel,event handler,nonblocking,readable,script,writable