fcopy — 한 채널에서 다른 채널로 데이터 복사하기

fcopy — 한 채널에서 다른 채널로 데이터 복사하기

파일의 내용을 네트워크 소켓으로 보내거나, 한 파일을 다른 파일로 복사해야 할 때가 있어요. 큰 파일을 느린 대상으로 복사할 때 너무 많은 데이터를 메모리에 버퍼링하지 않으면서, 필요하면 비동기로도 처리하고 싶을 때가 많아요. 그때 쓰는 명령이 fcopy예요.

출처: Tcl 공식 문서 — fcopy

본문

시그니처

fcopy inchan outchan ?-size size? ?-command callback?

설명

fcopy 명령은 한 I/O 채널 inchan에서 다른 I/O 채널 outchan으로 데이터를 복사해요. fcopy 명령은 Tcl I/O 시스템의 버퍼링을 활용해 큰 파일을 네트워크 소켓 같은 느린 대상으로 복사할 때 불필요한 복사본을 피하고 주 메모리에 너무 많은 데이터를 버퍼링하지 않게 해 줘요.

fcopy 명령은 파일 끝에 도달하거나 size 바이트 또는 문자만큼 전송될 때까지 inchan의 데이터를 전송해요. size는 두 채널이 같은 인코딩을 쓰면 바이트, 아니면 문자 단위예요. -size 인자를 주지 않으면 파일 끝까지 복사해요. inchan에서 읽은 모든 데이터가 outchan으로 복사돼요. -command 옵션 없이는 fcopy는 복사가 끝날 때까지 차단하고, outchan에 쓰인 바이트·문자 수(-size 옵션과 같은 규칙)를 반환해요.

-command 인자는 fcopy를 백그라운드에서 동작하게 해요. 이 경우 즉시 반환하고, 복사가 완료되면 나중에 콜백이 호출돼요. 콜백은 outchan에 쓰인 바이트 수를 나타내는 추가 인자 하나 또는 둘과 함께 호출돼요. 백그라운드 복사 중 에러가 발생하면 둘째 인자는 그 에러와 연관된 에러 문자열이에요. 백그라운드 복사에서는 inchan·outchan을 비차단 모드로 둘 필요가 없어요. fcopy 명령이 자동으로 처리하기 때문이에요. 다만 vwait 명령이나 Tk를 사용해 이벤트 루프에 들어가는 건 필요해요.

백그라운드 fcopy 동안 inchan으로 다른 입력 연산을 하거나 outchan으로 출력 연산을 해선 안 돼요. 반대는 완전히 합법이에요. 아래의 양방향 fcopy 예제에서 볼 수 있어요.

복사가 진행 중일 때 inchan이나 outchan이 닫히면 현재 복사는 중지되고 명령 콜백은 호출되지 않아요. inchan이 닫히면 outchan으로 이미 큐에 있던 모든 데이터는 쓰여져요.

백그라운드 복사 중 inchan이 읽을 수 있게 될 수 있음에 주의하세요. 백그라운드 복사 중에는 어떤 fileevent 핸들러도 꺼두어야 해요. 그 핸들러들이 복사에 간섭하지 않도록요. 잘못된 쪽 I/O를 시도하면(fileevent 핸들러든 아니든) "channel busy" 에러가 나요.

fcopy는 이 채널들의 -translation 옵션에 따라 inchan·outchan의 줄 끝 시퀀스를 변환해요. -translation 옵션에 대한 자세한 내용은 fconfigure 매뉴얼 항목을 보세요. 변환 때문에 inchan에서 읽은 바이트 수가 outchan에 쓰인 바이트 수와 다를 수 있어요. outchan에 쓰인 바이트 수만 보고돼요 — 동기 fcopy의 반환값으로, 또는 비동기 fcopy의 콜백 인자로요.

fcopy는 채널에 구성된 인코딩과 문자 변환을 따른다. 즉 들어오는 문자는 먼저 내부적으로 UTF-8로, 그다음 fcopy가 쓰는 채널의 인코딩으로 변환돼요. -encoding·-translation 옵션에 대한 자세한 내용은 fconfigure 매뉴얼 항목을 보세요. 두 채널이 모두 인코딩 "binary"로 설정되고 변환이 일치하면 변환은 수행되지 않아요. 출력 채널만 인코딩이 "binary"로 설정되면 시스템은 들어오는 문자의 내부 UTF-8 표현을 써요. 입력 채널만 인코딩이 "binary"로 설정되면 시스템은 들어오는 바이트가 유효한 UTF-8 문자라고 가정하고 출력 인코딩에 따라 변환해요. 이 경우 유효한 UTF-8 문자가 아닌 바이트에 대한 시스템 동작은 정의되지 않아요.

예제

첫 예제는 한 채널의 내용을 다른 채널로 정확히 전송해요. 파일을 다른 파일로 복사할 때는 file copy를 쓰는 게 더 좋다는 점에 주의하세요. 가능하면 파일 메타데이터(예: 파일 접근 권한)도 복사하기 때문이에요.

fconfigure $in -translation binary
fconfigure $out -translation binary
fcopy $in $out

둘째 예제는 콜백이 전송된 바이트 수를 전달받는 방법을 보여줘요. 응용 프로그램을 이벤트 루프에 넣기 위해 vwait도 써요. 물론 이 단순화된 예제는 명령 콜백 없이도 될 수 있어요.

proc Cleanup {in out bytes {error {}}} {
 global total
 set total $bytes
 close $in
 close $out
 if {[string length $error] != 0} {
 # error occurred during the copy
 }
}
set in [open $file1]
set out [socket $server $port]
fcopy $in $out -command [list Cleanup $in $out]
vwait total

셋째 예제는 청크로 복사하고 명령 콜백에서 파일 끝을 테스트해요.

proc CopyMore {in out chunk bytes {error {}}} {
 global total done
 incr total $bytes
 if {([string length $error] != 0) || [eof $in]} {
 set done $total
 close $in
 close $out
 } else {
 fcopy $in $out -size $chunk \
 -command [list CopyMore $in $out $chunk]
 }
}
set in [open $file1]
set out [socket $server $port]
set chunk 1024
set total 0
fcopy $in $out -size $chunk \
 -command [list CopyMore $in $out $chunk]
vwait done

넷째 예제는 두 소켓 사이의 비동기·양방향 fcopy를 시작해요. 그것들은 두 개의 [open "|hal 9000" r+] 파이프일 수도 있어요(네 fileevent 슬롯이 모두 사용 중이라 그들의 대화는 스크립트에 비밀로 남겠지만요).

set flows 2
proc Done {dir args} {
 global flows done
 puts "$dir is over."
 incr flows -1
 if {$flows<=0} {set done 1}
}
fcopy $sok1 $sok2 -command [list Done UP]
fcopy $sok2 $sok1 -command [list Done DOWN]
vwait done

더 알아보기

  • eof, fblocked, fconfigure, file
  • 키워드: blocking, channel, end of line, end of file, nonblocking, read, translation