우수신기술 지정ㅗ지원사업

최종보고서






실시간 멀티미디어 통신프로토콜 기술






개발 기관 : 큰사람컴퓨터(주)


















정보통신부


제 출 문

정보통신부 장관 귀하

본 보고서를 우수신기술 지정ㅗ지정지원사업 실시간 멀티미디어 통신프로토콜 기술 과제의 최종보고서로 제출합니다.

1999년 4원 19일

개발 기관 : 큰사람컴퓨터(주)

개발책임자 : 이 영 상

참여연구원 : 이 준 규

김 옥 구


요 약 문

1. 제목

우수신기술 지정ㅗ지원사업 실시간 멀티미디어 통신프로토콜 기술 과제

2. 연구개발의 목적 및 중요성

가. 연구개발의 목적

본 연구 개발의 목적은 21C 지식기반경제사회에서 Network를 통한 지식의 흐름에서 핵심적인 역할을 할 멀티미디어 데이터를 실시간으로 송/수신할 수 있는 통신규약(Protocol)을 개발하고 다양한 목적으로 활용할 수 있는 방안을 제시하는 것이다,

(1) 기반 핵심 기술의 기반

(가) Server/Client 송/수신 프로토콜 개발

Server(Windows NT 또는 Unix 호환 기종)쪽에서 Client쪽으로 데이터를 보낼 수 있는 실시간 데이터 송신 프로토콜을 개발하고, Client(Windows 95 or 98)쪽에서 데이터를 실시간으로 수신하여 처리 할 수 있는 수신 프로토콜을 개발한다.

(나) 실시간 데이터의 생성과 재생

Server에서 실시간으로 화상과 음성 등의 멀티미디어 데이터를 생성하여 Client쪽으로 전송하면 실시간으로 수신하여 재생할 수 있는 프로토콜을 개발한다.

(2) 초고속통신에 적합한 프로토콜

(가) 멀티미디어 시대

다가오는 21C의 정보는 대부분 Network을 통해 다양한 멀티미디어 데이터의 형태로 전달될 것이기 때문에 멀티미디어 데이터를 효과적으로 처리할 수 있는 통신프로토콜을 개발한다.

(나) 초고속 통신 시대

정부에서는 지식기반 경제사회에 대비하여 국가경쟁력 확보를 위해 초고속통신망 구축작업을 추진하고 있는데, 이러한 초고속 Network 환경에서는 기존의 멀티미디어 프로토콜이 비효율적이기 때문에 이를 대체할 통신프로토콜을 개발한다.

(3) 동영상, 음성, 화상 데이터 실시간 송/수신

외국 기술에 의존하지 아니하고 동영상, 사진, 그림, 음성이나 음악 데이터를 실시간으로 송/수신하여 바로 처리할 수 있는 통신프로토콜을 개발한다.

나. 연구 개발의 중요성

(1) 핵심 기술의 종속 방지

현재까지 프로토콜(Protocol) 기술은 자체개발은 어려우면서도 쉽게 외국기업으로부터 License 해서 사용할 수 있기 때문에 개발의 중요성을 인식하는 업체는 거의 없으나, 21C 지식기반 경제사회에서 Network를 통한 지식의 흐름이 프로토콜을 통해 이루어지기 때문에 국제표준으로 인증되지 않더라도 반드시 개발해야 할 기술이다.

(2) 프로토콜 기술력 입증

개발된 프로토콜(실시간 멀티미디어 통신프로토콜)은 외국의 제품과 비교하여 약 1.5배에서 2배 이상 빠른 전송속도와 송/수신 성공률도 뛰어나기 때문에 외국 기업이 지적소유권 등을 내세워 프로토콜 기술에 대하여 배타적인 계약을 요구할 때에 방패 역할을 할 수 있으며, 나아가 다양한 Application으로 개발하여 수출할 수도 있다.

3. 연구개발의 내용 및 범위

가. 연구개발의 내용

(1) 통신 프로토콜

통신의 기초가 되는 전송프로토콜(Transfer Protocol)을 개발

(2) S/W CODEC 사용 및 응용

멀티미디어 데이터를 처리하기 위한 S/W CODEC 사용 및 응용기술 개발

(3) 실시간 처리 기술

실시간으로 데이터를 처리하여 송/수신할 수 있는 기술 개발

나. 연구개발의 범위

(1) Server 관련 개발

Server 쪽의 송신 프로토콜 설계 및 개발

(2) Client 관련 개발

Client 쪽의 수신 프로토콜 설계 및 개발

(3) S/W CODEC 응용 기술

실시간 CODEC 응용 기술 개발

4. 시제품 개발 결과 및 활용에 대한 건의

가. 시제품 개발 결과

(1) Server 송신

매우 안정되고 빠른 송신 프로토콜 개발, 낮은 CPU 점유율

(2) Client 수신

타 Program 보다 뛰어난 성능 구현, 낮은 CPU 점유율, 안정된 수신

(3) Server 실시간 멀티미디어 생성

실시간 멀티미디어 생성 및 송신

(4) Client 실시간 멀티미디어 재생

실시간 멀티미디어 수신 및 재생

나. 시제품 활용에 대한 건의

(1) 동화상 회의

56Kbps 이상에서 상대방의 얼굴을 보며 동화상으로 회의를 할 수 있다.

이때 기반이 되는 실시간 멀티미디어 통신프로토콜 사용하면 된다.

(2) 온라인 교육, 가상 학교

가정이나 특정 장소에서 교육을 할 수 있다.

특히, 가상학교를 구축하여 강의를 듣거나 VOD(Video On Demand)처럼 지난 강의 데이터를 다시 볼 수 있으므로, 교육 목적에 활용할 수 있다.

(3) 전자 상거래

쇼핑 몰에서 상품을 다양하게 볼 수 있으며, 시각적 청각적 효과를 가지면서 상거래를 할 수 있다.

(4) 최첨단 멀티미디어 통신 서비스

다양한 멀티미디어 통신 서비스를 할 수 있으므로, 통신 서비스의 질이 높아진다.

(5) 음성 메일, 동화상 메일

상대방에게 음성이나 동화상 메일을 보낼 수 있다. 특히, 음성의 경우 압축률이 매우 높으므로 가정에서도 쉽게 음성메일을 보내고 받을 수 있다.

5. 기대 효과

가. 수입 대체 효과

현재에는 대부분의 통신프로토콜이 비교적 저렴하게 License 해서 사용할 수 있고, 일부는 외국업체의 표준화 전략에 따라 무료로 사용할 수 있기 때문에 수입대체 효과는 적지만 향후 외국업체가 표준으로 정착된 단계에서 배타적 계약을 요구해 올 경우에는 100% 수입대체 효과를 기대할 수 있다.

나. 기술 수출

현 단계에서 바로 국제표준으로 정착시키고 판매하는 것은 어려우나, 고속 Network에 적합한 통신프로토콜임과 성능의 우수성을 부각시키면서 인터넷을 통해 보급한다면 국제표준으로 자리매김 할 수 있을 것으로 예상하며, 표준으로 정착시킨 후에는 License를 통해 막대한 수출을 기대할 수 있다.

다. 다양한 Application 개발

국제표준으로 개발된 프로토콜이 정착되지 않더라도 Server/Client 환경으로 서비스를 개발하는 업체에 응용기술을 License해 주거나, 다양한 Application으로 판매할 수 있다.


목 차

제 출 문

요 약 문

제 1 장 실시간 멀티미디어 통신프로토콜 기술

제 1 절 기술 개발의 목적

제 2 절 기술의 내용과 소개

1. 기반 핵심 기술의 개발

2. 멀티미디어 시대에 맞는 프로토콜

3. 동영상, 음성, 화상 데이터 실시간 송/수신

제 3 절 기술의 중요성 및 활용

1. 핵심 기술의 종속 방지

2. 프로토콜 기술력 입증

3. 멀티미디어 데이터 처리

4. 실시간 멀티미디어 프로토콜 개요

제 5 절 시제품 개발 결과 및 활용

가. 시제품 개발 결과

나. 시제품 활용

제 6 절 기대 효과

가. 수입 대체 효과

나. 기술 수출

다. 다양한 Application 개발

제 2 장 실시간 멀티미디어 통신프로토콜 기술의 내용

제 1 절 실시간 멀티미디어 통신프로토콜 규격

1. 실시간 멀티미디어 프로토콜이란?

2. 실시간 멀티미디어 통신프로토콜(Protocol)규격

3. 실시간 멀티미디어 통신프로토콜 구현 Server 쪽 원시 Code

부록

부록 1. vi 에디터 사용법

부록 2. Remote Login

가. telnet

나. rlogin

제 1 장 실시간 멀티미디어 통신프로토콜 기술

제 1 절 기술 개발의 목적

본 연구 개발의 목적은 21C 지식기반경제사회에서 Network를 통한 지식의 흐름에서 핵심적인 역할을 할 멀티미디어 데이터를 실시간으로 송/수신할 수 있는 통신규약(Protocol)을 개발하고 다양한 목적으로 활용할 수 있는 방안을 제시하는 것이다.

제 2 절 기술의 내용과 소개

1. 기반 핵심 기술의 개발

국내 PC통신의 멀티미디어 표준 프로토콜로 널리 사용되는 K-modem의 구조를 기본으로 Z-modem을 비롯한 다양한 프로토콜의 분석을 통해 얻은 결과를 참조하여 송/수신 알고리즘의 최적화를 수행하였으며, 실시간으로 데이터를 처리할 수 있도록 중간처리 루틴을 개발하였다.

가. Server 쪽의 송신 프로토콜

Server(Windows NT 또는 Unix 호환 기종)에서 사용되는 송신모듈을 개발함에 있어 역점을 둔 사항은 Server의 부하를 최소화하면서도 전송효율을 보장할 수 있는 알고리즘의 최적화와 다양한 통신환경에서 최적의 전송 성공률을 보장할 수 있도록 Error 검출 및 정정 알고리즘을 보강하는 것이었으며, 이를 검증하기 위해 임의의 전송환경 및 Server/Client 환경을 만들어 상당한 기간동안에 테스트와 구조변경 작업을 수행하였다.

나. Client 쪽의 수신 프로토콜

Client(Windows 95 or 98)의 수신모듈은 가능한 CPU time를 적게 사용하는 방향으로 개발하였으며, 다양한 사용자의 컴퓨팅 환경에서 요구하는 System 사양을 낮춤으로서 보다 넓은 Client 대역폭을 확보함과 동시에 한정된 자원을 Share하여 여러 작업을 수행하는 Multi-Tasking 환경에서도 정상적으로 데이터를 수신하여 처리할 수 있다.

다. 실시간 멀티미디어 데이터 생성

기존의 K-modem은 이미 binary 저장된 한정된 데이터만 처리할 수 있었기 때문에 이번에 새롭게 개발한 부분으로 실시간으로 화상과 음성 등의 멀티미디어 데이터를 생성하여 실시간으로 송신할 수 있도록 설계하여 개발하였다.

라. 실시간 멀티미디어 데이터 재생

Client의 수신모듈에서 CPU time(혹은 점유율)을 최소화하고, 최대의 전송속도를 보장하기 위해 데이터를 수신하는 부분과 처리하는 부분으로 알고리즘을 나누어 개발하였으며, 개발된 결과로는 약 5% 이상의 전송효율과 10% 이상의 CPU time 이익을 얻었다.

2. 멀티미디어 시대에 맞는 프로토콜

2000년대에는 일반인들도 기본적으로 10Based-T 이상의 속도로 Network를 이용할 것으로 예상되기 때문에 설계된 프로토콜의 tuning 작업에서 기존에 지원하던 여러 멀티미디어 데이터가 안정적으로 송/수신 될 수 있도록 많은 노력을 하였다.

가. 멀티미디어 시대

바야흐로 2000년대는 멀티미디어 시대이다. 멀티미디어 시대에 맞는 통신프로토콜을 개발해야만 멀티미디어 데이터를 빠르고 정확하게 송/수신 할 수 있다.

나. 초고속 통신 시대

정부에서도 초고속 통신망을 2002년까지 1단계로 구축하려는 사업을 벌이고 있다. 이러한 초고속 통신망 시대가 열리면 이에 맞는 통신 프로토콜이 개발되어야 한다. 특히 실시간 멀티미디어 통신프로토콜은 프로토콜의 기반이 되는 기술을 모두 갖추고 있으므로 앞으로 초고속 통신시대에 꼭 필요한 핵심기술이 될 것이다.

3. 동영상, 음성, 화상 데이터 실시간 송/수신

목적하는 통신프로토콜이 다양한 멀티미디어 데이터를 처리하지 못하고 단순히 전송속도와 안정성만을 개선한다면 현재 여건에 부합하지 않아 특수한 용도로 밖에 사용될 수 없기 때문에 다양한 멀티미디어 데이터의 포맷을 분석하여 지원할 수 있도록 개발하는데 역점을 두었다.

가. 동영상 데이터 송/수신

다양한 Client 환경에서 동영상을 지원하기 위해 AVI 규격을 기본으로 실시간으로 데이터를 생성하고, 전송효율을 높이기 위해 별도의 처리루틴을 추가하여 개발된 송신모듈에서 동영상 데이터를 Client로 전송하면 Client는 이를 규약에 따라 실시간으로 Decoding하여 복원 해 준다.

나. 음성 데이터 송/수신

음성과 음악은 저속에서도 동작할 수 있도록 휴대폰에서 주로 사용하는 GSM 방식을 참조하여 설계하였으나, Code 충돌이 발생하여 별도의 처리루틴을 추가하여 문제를 해결하였으며, 다양하게 활용할 수 있도록 전송하는 데이터의 규격을 새로 설계하였다.

다. 화상 데이터 송/수신

K-modem으로 송/수신할 때에 Type을 검사하기 위해 소요되는 대기시간을 줄었으며, 데이터를 처리하는 알고리즘을 개선하여 루틴 중간에 쉽게 빠질 수 있도록 하였으며, 지원하는 화상 규격을 추가하였다.

제 3 절 기술의 중요성 및 활용

1. 핵심 기술의 종속 방지

프로토콜(Protocol) 기술은 개발이 어렵기 때문에 마치 선진국에서만 개발하는 기술로 여기고 국내에서는 거의 개발하지 않았으며, 현재 사용되고 있는 프로토콜 중에 K-modem을 제외한 기술이다.

현재까지는 쉽게 License해서 사용할 수 있고, 경우에 따라 공짜로 사용할 수 있기 때문에 프로토콜 기술 개발의 중요성을 인식하는 업체나 사람들이 많지 않지만, 농업 기반에 무너진 후에 고가로 식량을 판매하는 선진국의 전략을 감안할 때 반드시 확보해 두어야 할 기술이다.

또한 도래할 21C 지식기반경제사회(Knowledge-driven Economy)에서는 토지, 노동, 자본에 이어 지식이 제4의 생산요소이며, 원활한 지식의 흐름이야말로 국가 경쟁력을 좌우하는 가장 중요한 요소이다.

이러한 지식의 흐름은 초고속통신망으로 멀티미디어 통신프로토콜이라는 통신언어를 통해 순환될 것이고, 자체 기술로 구현할 수 없을 경우에는 지식의 흐름자체가 종속될 수 있기 때문에 반드시 개발해 둘 필요가 있을 것이다.

2. 프로토콜 기술력 입증

개발한 프로토콜(실시간 멀티미디어 통신프로토콜)은 가장 대표적인 전송 프로토콜(File Transfer Protocol)이라고 할 수 있는 Z-modem과 Bench marking한 결과, 약 1.5배에서 2배 이상 빠른 전송속도와 송/수신 성공률도 상당히 우수한 것으로 나타났다.

[표 1] 10Based-T 환경에서 속도 비교


현재의 표준화된 통신프로토콜을 개발한 외국회사에서 지적소유권 등을 내세워 사용하고 있는 업체들(국내 모든 SP/IP/ISP 등)에게 해당 구현기술에 대하여 배타적 계약을 요구할 때에 실시간 멀티미디어 통신 프로토콜은 방패 역할을 할 수 있으며, 반대로 개발된 실기간 멀티미디어 통신프로토콜을 국제 표준으로 정착시킬 경우에는 막대한 외화를 License를 통해 벌어들일 수 있다.

[표 2] 2D-ISDN 환경에서 속도 비교


* 비교기준

System : Windows 95가 설치된 Pentium 200, 32 RAM

프로토콜을 포함한 Client S/W를 통해 간접적인 성능비교 단위는 KCPS 임.(CPS는 초당 전송할 수 있는 글자수)

3. 멀티미디어 데이터 처리

멀티미디어 데이터인 화상 및 음성율 Server에서는 실시간으로는 Encoding하여 실시간 멀티미디어 통신프로토콜을 이용하여 송신하고, 이를 다시 Client에서 프로토콜로 수신한 다음 이를 실시간으로 Decoding하여 복원해 주는 방식은 순수 우리 기술로 구현한 것으로 구현된 기술을 이용하여 특화 된 Application 개발한 경우에 다양한 Network Solution을 쉽게 개발할 수 있다.

제 4 절 연구개발의 내용 및 범위

1. 개발 환경

다양한 Server 환경(Unix, Linux, SOC, Windows NT 등)에서 동일한 성능으로 실시간에 멀티미디어 데이터를 생성하여 송신할 수 있도록 C 언어로 작성하였으며, 특히 공통적인 표준함수 위주로 개발하여 이식성을 높이는데 주력하였다.

또한 PC Server급에서 많이 사용되고 있는 Windows NT를 지원하기 위해 외부 전문가를 통해 기술자문을 받았으며, Client는 이야기에 포팅하여 다양한 Client 환경 테스트를 바로 할 수 있도록 하였다.

사용한 Library 중에 Server 환경에 따라 약간씩 다르게 동작하는 함수가 있어 별도로 개발하여 포함시켰으며, K-modem과 호환성을 유지하기 위해 K-modem에서 지원하는 멀티미디어 데이터 규격을 수용하여 개발하였다.

가. 개발 언어 및 운영체제

MS Visual C++ 5.0, Windows 98 & Windows NT 4.0

나. Library

vfw32.lib - Image capture, compress, decompress 관련 library

comct132.lib - win32 common control library

winmm.lib - window multimedia 관련 library

wsock32.lib - window socket 관련 library

외 기타 기본 Library

다. 연구기자재 사용현황

NO

구분

수량

비고

1

MSDN

3 Copy

Develop Kit

2

개발용 PC

3 대

개발자 System

3

테스트용 PC

2 대

테스트용 Windows System

4

테스트용 Server

2 대

Unix, NT를 설치한 PC Server

5

Router

1 대

Internet 연결 테스트용

6

HUB

1 대

LAN

7

Digital Camera

2 대

동영상 모션 Capture


2. 연구개발의 내용

가. 통신 프로토콜

통신의 기반이 되는 핵심 프로토콜(Protocol)을 개발

나. S/W CODEC 사용 및 응용

동영상, 음성 등의 실시간 송/수신을 위한 S/W CODEC을 사용 및 응용하는 기술 개발

다. 실시간 처리 기술

실시간으로 데이터를 처리할 수 있는 기술 개발

3. 연구개발의 범위

가. Server 관련 개발

Server 쪽의 송신 프로토콜 개발

나. Client 관련 개발

Client 쪽의 수신 프로토콜 개발

다. S/W CODEC 응용 기술

실시간 CODEC 응용 기술 개발

4. 실시간 멀티미디어 프로토콜 개요

가. 송신부


나. 수신부


다. Image CODEC 선택

이 program은 window CODEC을 사용하는데, window CODEC 중에는 dib Image에 대한 압축과 풀기를 할 수 없는 것이 있기 때문에 많은 CODEC들 중에는 Dib Image 압축이 가능한 것을 선택하고, 또한 windows를 설치할 때에 default로 설치되는 CODEC 중에서 압축 효율이 좋은 것 4가지를 선택했다.

1. Intel indeo(R) video R5.0

2. Intel indeo(R) video R3.2

3. Microsoft video 1

4. Cinepak CODEC by radius

이들 중 가장 효율이 좋은 것부터 차례로 CODEC이 설치되어 있는지 확인하여 Program이 시작할 때에 CODEC handle을 얻는다.

라. Image 압축전송 과정

callback 함수를 만들고 이를 capture window와 연결시켜 주면 주기적으로 camera에서 Motion pictured의 순수한 Image data를 얻을 수 있다. Image data에 미리 정의해 둔 Image header를 붙여서 DIB Image를 만든다.

DIB Image는 기존의 bitbit 함수로는 화면에 뿌릴 수 없고, 다른 Image 함수들을 사용하면 high color 미만에서는 Program Error를 일으키게 된다.

따라서 이 문제를 해결하기 위해 DIB Image 함수를 사용해야 하며, DIB Image를 draw할 때는

DrawDibOpen

DrawDibBegin

DrawDibDraw

DrawDibEnd

DrawDibClose

함수들의 호출순서로 이루어지는데, DrawDibOpen, DrawDibClose 함수는 DIB Image를 Display하고자 하는 window에 대해 create와 destroy할 때 한번씩 호출 해 주면 되고, DrawDibBegin, DrawDibDraw 함수는 Image를 draw할 때마다 호출해 주어야 한다.

DrawDibRealize는 256 color 이하의 Windows에서 팔레트를 Dib Image에 맞도록 조종해 준다.

[참고] 그릴 때마다 DrawDibOpend를 부른 후 DrawDibBegin을 하면 그리는 속도가 떨어져서 시스템의 CPU 점유도가 높아지므로 DrawDibOpen은 윈도우가 생성될 때만 하고 DrawDibBegin은 그림의 크기가 바뀔 때만 부른다.(단, DrawDibBegin과 DrawDibEnd는 반드시 쌍으로 맞추어 주어야 한다.)

DrawDibOpen 함수는 DrawDib Library를 사용할 수 있도록 열고 화면을 그리는데 사용할 DC를 생성한다.

DrawDibBegin은 그리기 전에 DC의 크기 색상 팔레트 등을 초기화한다.

DrawDibDraw는 실제로 화면상에 Image를 그린다.

DrawDibEnd는 사용한 DC의 플래그와 Setting 등을 초기화한다.

DrawDibClose는 사용한 DC를 없애고 리소스를 원래대로 돌려준다.

Dib Image가 압축되어 있으면 DrawDibDraw 함수는 이것의 압축을 풀어 화면에 표시해 준다.

처음 Capture한 Image는 대부분 압축이 되지 않은 16bit 또는 24bit Image인데, 이것을 압축하여 새로운 DIB IMAGE로 변환해야 한다.

압축은 윈도우의 ICM(Image 압축 모듈)을 이용한다.

사용할 CODEC은 사용자가 고를 수도 있고, Program이 알아서 선택할 수도 있는데, 사용자가 적당한 CODEC을 고르는 것은 어려움이 있을 것이므로 Program이 CODEC을 찾아서 Setting하는 방식을 택하였다.

[참고] CODEC을 사용자가 선택하게 하려면 ICCompressorChoose 함수를 사용하면 된다.

고른 CODEC을 활성화시키려면 IC Open 함수를 쓰면 된다.

Image를 사용하여 Capture된 16bit 또는 24bit Image를 압축된 Image로 변환한다. 이 때 주의 할 점은 Capture 장치에 따라서 이미 압축된 형태의 Image를 넘겨주는 경우가 있는데, 이 경우에는 ICImageCompress 함수가 동작을 하지 않거나 Error를 내게 된다. 특히 ICM 쪽의 드라이버는 민감해서 잘못하면 시스템이 다운되는 수도 있으므로 압축 함수를 부리기 전에 미리 Capture된 Image를 검사하여 이미 압축이 되어있는 Image이면 압축을 하지 않고 바로 전송 큐로 집어넣어야 한다.

[주의] Capture 장치에서 미리 압축된 Image를 넘기는 경우는 대부분 윈도우와 호환되지 않는 포맷으로 data를 만드는 경우가 많아서 송신부와 수신부가 같은 종류의 Capture 장치를 쓰지 않으면 복원이 안 되는 수가 많다, 고화질을 원하는 특수한 경우 외에는 윈도우 자체의 압축 CODEC을 사용하는 것이 호환성을 위해서 유리하다.

압축된 Image의 크기는 대부분 2 Kbytes에서 15 Kbytes 사이로 이 data를 Packet size에 맞게 잘라서 보내야 한다.

ICImageComprss로 압축한 Image는 ICimageDecompress 함수를 이용하여 압축을 풀 수 있는데, 이때 압축을 풀지 않고 바로 DrawDibDraw 함수로 넘겨도 CODEC이 깔려있다면 윈도우에서 알아서 압축을 푼 후 화면상에 그려준다. CODEC에 따라서는 이렇게 하는 것이 더 빠른 경우도 있으므로 속도를 테스트 한 후 적절한 방법을 택한다. 'Intel indeo(R) video R5.0'의 경우는 압축을 풀지 않고 바로 넘기는 방법이 빨랐다.

다만, 주의 할 점은 윈도우가 256 Color 미만일 경우는 화면상에 출력이 되지 않을 경우가 있을 수도 있으므로 윈도우의 상태에 따라 화면출력루틴을 분리하여 작성하여야 한다.

라. 음성(Voice) 압축전송 과정

음성 CODEC은 윈도우 CODEC을 쓰거나, 자체 개발한 CODEC을 사용하는데, 윈도우 CODEC은 대부분 프레임 단위의 압축이 잘 되지 않고 파일 단위의 압축이 되는 경우가 많으므로 CODEC을 고를 때 주의해서 골라야 한다.

현재 실시간 전송에 많이 쓰이는 음성 CODEC은 True Speech와 GSM이 있는데, True Speech의 경우는 License 문제가 있어서 GSM을 참조하여 음성압축을 하였다.

GSM은 14Kbps이상의 Modem에서는 실시간 전송이 가능하므로 현재의 통신상황에서는 실용성이 있을 것으로 생각된다.

음성을 실시간으로 전송하기 위해서 음성을 녹음할 때 프레임의 크기는 너무 크지 않게 해야 한다.

GSM은 한 프레임을 크기가 33 byte이고 음성지연시간이 20 ms 정도이므로 거의 실 시간으로 전송이 된다. 하지만 윈도우 드라이버의 제한으로 인해 송/수신부 간에 약 1초 정도의 지연이 생기게 된다. 이 점은 윈도우 NT에서는 조금 낫기는 하지만 약 0.5초의 지연이 생기게 된다.

즉, 양방향 대화에서는 서로 말이 1초 뒤에 들리는 문제점이 있다.

하지만 한 쪽이 일방적으로 방송하는 경우에는 별 문제가 되지 않을 것으로 여기며, 지연 문제는 하드웨어를 바꾸면 해결되는 문제이므로 통신을 통한 음성전송이 활성화되면 이런 문제가 해결된 장비가 나올 것으로 생각된다.

Capture한 음성을 프레임 크기만큼 잘라서 압축된다.

압축된 data를 여러 개 모아서 Packet을 만든다.

Packet을 송신 큐에 넣는다.

마. 송신 큐 관리모듈(Module)

큐에 들어있는 data를 뽑아내어 음성과 화상을 Packet으로 만들어 송신한다. 일반적인 송신모듈과는 달리 Error가 나더라도 재전송을 하지 않고 Error Packet을 무시하는 방식을 취한다.

만약 Error가 난다고 해서 계속 재전송을 하게 되면 Sync가 맞지 않는 문제가 생기게 된다.

음성의 경우는 프레임 단위로 전송하므로 그 Packet만 버리면 되고, 화상은 하나의 DIB Image를 여러 Packet으로 나누어 보내므로 Packet 중에 하나라도 error가 나면 그 화면에 해당되는 전체 Packet을 무시해야 한다.

Packet을 송신 큐에 넣는다.

바. 전송 Packet 관리모듈(Control Module)

Packet의 구조는 Modem 등에서도 잘 전송될 수 있도록 충돌 가능성이 있는 Code는 Coding을 해야 한다. 즉 XON, XOF 등의 흐름제어 문자나 Packet Coding에 사용되는 header 등은 Coding을 한다.

Coding된 문자는 header 등과 중복되지 않도록 해야 하므로 header 문자는 Coding된 문자와 완벽하게 구분되도록 만들어야 한다.

Packet의 구조는 header, 종류, 오프셋, Data, 터미네이터, CRC로 구성한다.

종류는 일반 데이터인지 음성인지 화상인지를 구분하는 Code이다.

음성이나 화상의 경우 오프셋은 프레임의 순서를 나타낸다.

CRC는 수신부에서 받았을 때 그 Packet을 사용할 수 있는지 검사하는데 사용한다,

실시간 멀티미디어 통신프로토콜은 LAN 환경보다는 server의 Service용으로 사용될 것이므로 Error 검사와 복구에 신경을 써야 한다.

LAN 상에서 들어가는 대부분의 프로토콜은 CRC 검사와 같은 부가적인 data를 넣지 않아도 TCP/IP의 하부 층에서 이런 과정을 해주므로, 프레임의 순서 등의 간단한 header만 넣고, data도 Coding을 하지 않은 상태로 전송해도 별 문제도 없다.

하지만 Modem 환경에서는 Error가 빈번히 발생하므로 CRC 검사와 data Coding이 필요하다 .

예를 들어 HTTP 프로토콜이나 FTP 프로토콜의 경우에 data Coding을 하지 않으며, Packet 단위로 구분하지도 않고 binary data를 그냥 전송한다.

만약 Modem Line으로 이렇게 전송한다면 대부분의 data는 전송이 되면서 Error가 발생할 것이다.

실시간 멀티미디어 통신프로토콜은 실시간 음성, 화상뿐만 아니라 미리 만들어진 이미지나 동화상의 전송도 고려해야 하므로 프로토콜 이 두 가지의 동작모드로 구분된다.

첫째는 일반적인 파일 전송과 같이 Error가 발생했을 때 재전송 복구를 해야 하는 파일 전송모드이고,

둘째는 실시간으로 Packet을 조립하여 음성이나 화상을 전송하는 실시간 전송모드로 파일 전송모드의 Packet 구조를 그대로 사용하되 Error가 날 경우에 재전송을 하는 대신에 Packet을 무시하도록 루틴을 만들었다. 그리고 실시간 전송의 경우 보낼 크기를 미리 알 수 없으므로 Packet의 오프셋은 의미가 없으므로 이것을 프레임번호로 사용한다.

제 5 절 시제품 개발 결과 및 활용

가. 시제품 개발 결과

전송속도와 안정성 및 다양한 멀티미디어 data를 처리할 수 있는 통신 프로토콜로 충분한 경쟁력을 확보한 것으로 자체 평가를 하였으며, 현재 국내 멀티미디어 표준프로토콜인 K-modem을 2000년까지 대체할 수 있을 것으로 기대한다.

(1) Server 송신

낮은 CPU 점유율을 유지하기 위해 Data를 처리하는 루프의 최적화를 통해 기존의 Z-modem보다 약 7-10%가량 CPU 점유율을 낮추었으며, Unix 계열뿐만 아니라 Windows NT에서도 사용할 수 있도록 개발하였다.

또한 Server에 많은 부하가 걸려 정상적으로 동작하지 못할 것에 대비한 루틴도 포함시켜 System의 한계로 인한 Error를 최소화했으며, Z-modem을 분석하여 얻은 Error 검출/수정 방법을 나름대로 구현하여 기존의 Z-modem보다 월등히 효율을 높였다.

(2) Client 수신

Server의 송신모듈은 그 자체가 하나의 Program으로 사용되지만 Client는 프로토콜 자체가 Application Program으로 사용되는 예는 거의 없다. 따라서 프로토콜의 기본적인 수신성능도 중요하지만 실제로 프로토콜을 구현할 때에는 수신된 data를 처리하는 루틴에 더 비중을 두었다.

기본적인 성능을 보장하기 위해 알고리즘 최적화를 통해 유사한 프로토콜이 파일을 수신 받을 때보다 30∼500%가량 CPU 점유율을 낮추어 다중작업에 적합하도록 하였다.

(3) Server 실시간 멀티미디어 생성

멀티미디어 data를 실시간으로 처리하는 것은 위에도 언급을 하였지만 system 상에서 Program으로 나타나는 결과는 정확한 실시간은 아니다. 다만 순차적으로 처리하고, Server에서 일방적으로 전송하기 때문에 Client 입장에서 보면 연속적으로 data를 수신하여 처리하는 것이기 때문에 실시간으로 처리로 볼 수 있는 것이다.

따라서 실시간 멀티미디어 생성과 실시간 멀티미디어 처리는 엄밀히 말해 약간 다르며, 실시간 멀티미디어 통신프로토콜에서는 이점에 착안하여 실시간으로 멀티미디어 data를 생성하는 부분과 생성된 멀티미디어 data를 처리하여 전송해 주는 부분으로 나뉘어 개발하였다.

현재 개발한 실시간 멀티미디어 통신프로토콜의 S/W 방식에 의한 멀티미디어 data 생성부분은 실제로 MPEG Board 등과 같은 H/W로 멀티미디어 data를 처리하는 것보다 느리기 때문에 많은 멀티미디어 data를 실시간으로 생성하고, 전송해야 하는 경우라면 멀티미디어 data를 H/W로 생성하고, 이를 바로 실시간 멀티미디어 통신프로토콜로 처리하여 전송한다면 성능을 조금 향상시킬 수 있다.

하지만 실시간 멀티미디어 통신프로토콜은 자체적으로도 멀티미디어 Data를 S/W적으로 CODEC을 이용하여 실시간으로 처리할 수 있기 때문에 위와 같은 경우가 아니라면 충분히 Service용으로 사용할 수 있다.

(4) Client 실시간 멀티미디어 재생

개발된 Client 쪽 프로토콜(수신용)은 향상된 전송프로토콜과 강화된 멀티미디어 data 처리 능력으로 나누어 설명한다.

전송효율은 주어진 상황에서 최상의 성능을 보여주어야 만 높다고 볼 수 있기 때문에 Client 모듈을 개발할 때에 System의 자원을 가능한 적게 사용하고, 일괄처리를 유도함으로써 성능을 향상시켰다.

멀티미디어 data의 처리는 decoding하여 자체적으로 처리할 수 있는 data는 옵션에 따라 처리하고, 별도의 Processor를 호출해 data를 반환해 주어야 하는 경우에는 프로토콜을 호출한 Processor의 환경에 따라 처리한다.

어느 경우에라도 실제적인 성능은 System에 많은 영향을 받지만 특수한 목적으로 data처리를 강화 - 빠른 image 처리를 위해 Assembler를 사용하거나 Device를 직접 제어하는 등 - 하지 않은 일반적인 Application Program보다는 우수하게 처리한다.(자체적인 시험을 통해 성능이 우수한 CODEC를 선택했다.)

나. 시제품 활용

이번에 개발한 실시간 멀티미디어 통신프로토콜은 Network를 통해 다양한 멀티미디어 data를 Service할 수 있는 통신프로토콜로 대형 SP(Service Provider)나 ISP(Internet Service Provider)가 채용할 경우에 현재 System으로 더 많은 이용자에게 멀티미디어 data 서비스가 가능하기 때문에 비용절감효과를 가져올 수 있으며, IP(Information Provider나 CP(Contents Provider)도 Infoshop 등에서 독립적인 멀티미디어 data 서비스를 손쉽게 할 수 있다.

실제로 SP나 ISP를 통해 제공되는 멀티미디어 data 서비스를 이용하기 위해서는 별도의 통신 Client 프로그램이 필요하고, 해당 Client Program에서 멀티미디어 data를 수신하여 처리할 수 있는 기능이 있어야 하기 때문에 SP나 ISP는 전용 에뮬레이터를 개발하여 보급하고 있으나, 많이 사용되고 있지는 않다.

현재 판매되는 통신 Program의 90%이상(1999. 12 기준) 시장점유율을 점유하고 있는 이야기에 탑재된 K-modem과 Motion Picture와 특정 포맷을 제유하고는 호환되기 때문에 실시간 멀티미디어 통신프로토콜을 이용하여 서비스를 개발할 경우 매우 쉽게 멀티미디어 data 서비스를 할 수 있다.

이외에 쉽게 생각할 수 있는 것이 화상회의 및 원격교육, 전자상거래에서 인터넷 판매점이랄 수 있는 쇼핑몰 상품 안내 등에 활용하는 것이며, 은행들이 추진하는 PC 뱅킹 서비스에도 활용할 수 있다, 현재 Network를 기반으로 개발되고 있는 대부분의 서비스에 다양한 멀티미디어 서비스의 기반으로 활용할 수 있다.

(1) 동화상 회의

56K 정도에서 상대방의 얼굴을 보며 동화상으로 회의를 할 수 있으며, 128K 이상에서는 Full Motion의 동화상 회의가 가능하다, 따라서 화상회의 Solution을 개발하는 업체에서는 추가적인 핵심기술의 개발 없이도 쉽게 화상회의 Solution을 개발할 수 있다.

(2) 온라인 교육, 가상 학교

동화상 회의와 구성면에서 거의 유사한 가정이나 특정 장소에서 원격교육을 할 수 있다. 특히, Internet에 가상학교를 구축하여 실시간으로 강의를 하거나, 강의 data를 저장했다가 VOD(Video On Demand)처럼 항상 볼 수 있기 때문에 원격교육의 핵심 기술로 사용될 수 있다.

(3) 전자 상거래

고속 Network 환경에서는 HTTP 프로토콜을 이용한 Internet 쇼핑몰이 효과적인 상품을 선전 할 수 있는 수단으로 사용될 수 있지만 128K 이하의 Networking 환경에서는 부적절한 면이 많다.

K-stream은 그 기반으로 Telnet이기 때문에 HTTP 프로토콜보다 기본적으로 약 30%가량 효율이 우수하기 때문에 저속환경에서 다양한 멀티미디어 data를 통해 상품을 안내할 수 있다.

요즘 추세가 각각의 업체마다 전용 에뮬레이터를 보급하여 배타적인 서비스를 시도하고 있는데, 이러한 Program에서 K-stream을 기본 통신용 프로토콜로 사용할 경우에 많은 이익을 얻을 수 있다.

(4) 최첨단 멀티미디어 통신 서비스

현재 보안 문제로 인해 많은 서비스들이 인터넷을 통해 서비스되고 있지 못하며, 이러한 서비스들은 멀티미디어 서비스로 구현될 때에 이용자들에게 편리함을 주고 성공할 수 있다.

K-stream을 이용하면 현재 안고 있는 문제점들을 보완하면서도 멀티미디어 서비스를 제공할 수 있기 때문에 서비스 경쟁력을 높이는데 큰 도움이 될 수 있다.

(5) 음성 메일, 동화상 메일

1997년 현재 Internet traffic의 약 38%가 E-mail에서 발생하는 사실로 많은 사람들이 전자우편을 이용하고 있다는 것을 알 수 있다. 현재 Text가 아닌 음성이나 동화상으로 자신의 감정이나 모습을 전송하기에는 여간 번거로운 것이 아니기 때문에 실제로 음성이나 동영상을 통해 E-mail을 보내는 사람은 그리 많지 않은 것으로 생각된다.

실시간 멀티미디어 통신프로토콜은 통신프로토콜인 동시에 Client 모듈에서 음성이나, 동영상을 처리할 수 있는 기능이 추가되어 있기 때문에 현재보다 상당히 쉽게 이용자들이 음성이나 동영상을 통해 상대방에게 음성이나 동화상 메일을 보낼 수 있으며, 음성의 경우 압축률이 매우 높고 저속에서도 안정적으로 data를 실시간으로 처리할 수 있기 때문에 PC통신이나 Internet의 대화방 서비스에 활용하여 음성 대화방 서비스도 가능하다.

제 6 절 기대 효과

현재에는 대부분의 통신프로토콜이 비교적 저렴하게 License해서 사용할 수 있고, 일부는 외국업체의 표준화 전략에 따라 무료로 사용할 수 있기 때문에 직접적인 수입대체 효과는 크지 않지만, 우리나라가 밀 생산기반이 무너진 시점에서 밀가루를 비싼 가격에 수입해야 했다는 사실로 비추어 볼 때에 향후 외국업체가 개발한 통신프로토콜이 국제표준으로 정착된 단계에서 국내에 프로토콜 기반기술이 없다면 아주 비싼 대가를 지불해야 할 것이다.

통신프로토콜은 일반 S/W와 달리 우수한 성능과 가격에 따라 시장을 주도하는 것이 아니라 먼저 표준으로 정착되어 많은 사람들에 의해 널리 쓰여져야 만 효과를 나타내기 때문에 개발된 실시간 멀티미디어 프로토콜은 현재 국내 표준 멀티미디어 프로토콜인 K-modem 시장을 2000년까지 대체하여 국내 표준으로 정착시키는 한편, 인터넷을 통해 보급함으로써 국제표준화를 시도할 것이다.

또한, Network를 기반으로 다양한 Application에서 쉽게 활용할 수 있도록 맞춤형 Library를 구축하여 제공할 예정이며, 보다 많은 업체에서 개발된 실시간 멀티미디어 통신프로토콜을 활용한 Solution을 개발할 수 있도록 지원함으로써 국제표준화와 특수한 틈새시장을 공략하여 수출의 길을 열 것이다.

가. 수입 대체 효과

현재로서는 수입 대체 효과가 그리 크지는 않지만 21C 지식기반경제사회에서 외국업체에서 사용하고 있는 통신프로토콜에 대하여 배타적 계약을 요구해 올 때에 실시간 멀티미디어 통신프로토콜은 방패구실을 100% 훌륭히 소화해 낼 것이다.

나. 기술 수출

개발된 실시간 멀티미디어 통신프로토콜은 직접적인 성능면에서는 현재 표준으로 정착된 프로토콜보다 여러 면에서 우수한 성능을 보여주고 있다. 따라서 이러한 성능과 쉽게 서비스나 Application에 적용할 수 있도록 맞춤형 Library를 구축하여 보급할 예정이며, 성능이 중요시되는 특수한 환경에 이식성과 안정성을 내세워 능동적인 Internet Site Marketing을 통해 판매한다면 최소한 연간 1억원 이상의 수출을 할 수 있을 것으로 기대한다.

또한 실시간 멀티미디어 통신프로토콜을 개발하면서 멀티미디어 data를 처리하여 효과적으로 처리하는 기술을 개발하였으며, 이 기술은 향후 개발될 새로운 통신프로토콜에 활용될 수 있다.

Internet Ⅱ가 표준으로 정착되어 가는 현시점에서 개발된 기술은 ATM 등의 고속 Network 환경에서도 멀티미디어 Data를 안정적으로 처리 할 수 있기 때문에 실리콘밸리 한국소프트웨어지원쎈터(KSI)에 입주해 있는 지사(2 West Santa Clara st.#311, san Jose CA. USA)를 통해 Network 업체들을 대상으로 Promotion 할 것이다.

다. 다양한 Application 개발

현재 국내에서 멀티미디어 통신프로토콜을 자체적으로 개발하여 사용하는 업체는 없는데, 이는 프로토콜 기술이 다양한 환경변수와 오랜 개발기간을 요구하기 때문이다. 따라서 회사는 자체적으로 응용 Program까지 모두 개발하는 것이 아니라 쉽게 Include 하거나 호출하여 사용할 수 있도록 하는 한편 개발한 실시간 멀티미디어 통신프로토콜의 규격을 표준화하는 데에 더 큰 비중을 둔 개발을 진행할 것이다.

제 2 장 K-Stream 기술의 내용

제 1 절 실시간 멀티미디어 통신프로토콜 규격

1. 실시간 멀티미디어 통신프로토콜이란?

실시간 멀티미디어 프로토콜(Real-Time Multimedia Protocol)인 실시간 멀티미디어 통신프로토콜은 동화상(Animation), 화상(Image)이나 음성(Voice), 음악(Music), 기타 데이터 등을 신속하고 정확하게 주고받을 수 있도록, '큰사람'에서 자체적으로 개발한 프로토콜이다.

이 프로토콜의 장점은 통신상에서 온라인(On-line) 송/수신을 통하여 멀티미디어 Data를 송/수신 할 수 있으며, 실시간(Real time)으로 멀티미디어 데이터와 일반 Data를 처리할 수 있도록 할 수 있도록 할 수 있는 장점이 있으므로, 각종 자료를 이 프로토콜로 전송할 경우 호스트(Host), 통신 에뮬레이터(Emulator), 단말기 등에서 많은 부하가 걸리지 않고 원하는 목적을 달성할 수 잇다.

또한, 기존에 나와 있는 여러 가지 프로토콜의 장점과 단점을 분석하여 시대에 맞게 새롭게 제작되었으므로, Error 검출능력이 뛰어나고 전송 효율도 뛰어나므로, 일반 파일 송/수신에도 사용할 수 있다.

이 프로토콜은 대한민국(Korea)과 개발 회사인 '큰사람'의 공통 영문 문자의 첫 자 K를 따서 '실시간 멀티미디어 통신프로토콜'라 부르고, 이를 '큰사람 프로토콜 규격 Ⅱ'라고도 명명하였다.

2. 실시간 멀티미디어 통신프로토콜 프로토콜(Protocol)규격

가. 일반적인 프로토콜과 공통으로 가져야 하는 기능

일반 프로토콜이 지원하는 기능

1) 파일에 대한 정보를 Server인 Sender쪽으로 Client인 Receiver에게 초기 연결 시에 알려주는 기능 : 파일의 정보란 파일의 길이(Length), 특성(Attribute), 날짜(Date, Time), 기타 Server와 Client의 제약 사항 등에 관한 정보들이다.

2) 파일을 전송하는 기능

3) 전송된 Data의 Error를 검사하는 기능 : 커밋(Kermit)의 경우 block 크기가 96 이하이므로 체크섬(Checksum)만 해도 충분하나 체크섬 방식은 Error가 심할 경우, 거의 실효성이 없으므로 가장 많이 사용되고 있는 CRC-302 방식을 많이 쓴다.

4) 잘못 전송된 Data를 재 전송하는 기능 : 일반적으로 많이 사용되는 Z-modem처럼 ZRPOS(파일의 offset)로 할 수도 있고, Kermit처럼 Packet 번호와 ACK를 이용해서 복구할 수도 있다. Packet 번호로 할 경우 Sender 쪽이 복잡해질 가능성이 있으므로 파일 위치로 하는 것이 좋다.

5) Sender에서 Receiver로 ACK를 보내는 기능 : 연속 윈도우일 경우 각 Packet의 끝에 연속 Terminator와 CRC를 붙인다. Terminator는 커밋과 같이 하나만 정해서 쓰면 END Packet 임을 알려주기 힘들기 때문에 여러 가지의 TYPE를 가진다. Z-MODEM의 경우 4가지(ZCRCG ZCRCQ ZCRCW ZCRCE)이다. Terminator Code는 2바이트 이상으로 해야 Error가 날 확률이 준다. 프로토콜의 속도를 위해 연속 Packet도 가능하게 하면 좋다.

6) Data 전송시 통신에서 충돌이 나는 Code를 피하는 기능 충돌될 Code는 Encode 한다.

나. 멀티미디어 전용 프로토콜 실시간 멀티미디어 통신프로토콜이 반드시 갖춰야 할 점들 : 실시간 멀티미디어 Data에 맞게 프로토콜이 지원해야 하는 사항들

1) 전송되는 Packet의 모양은 Packet의 종류와 상관없이 통일되어 있으면 좋다. 왜냐하면 이 프로토콜은 getc나 fread 등을 대치하기 위해 쓰기 때문에 모양이 일정하지 않으면 사용하기 힘들다. 그러므로, 여러 가지 다른 Data를 전송할 때에도 같은 Packet의 모양을 유지하게 되어, 새로운 멀티미디어 Data가 나오더라도 호스트 쪽의 수정이 적게 쉽게 새로운 형식을 추가할 수 있다.

2) 압축된 그래픽 파일의 경우 되도록 이면 Data Packet 크기가 한 단위이면 좋다. Gif와 같이 압축된 화상(Image) 파일의 경우 한 줄 단위로 Packet이 오면 에뮬레이터 쪽에서 구현하기에 편하다. 하지만, 꼭 이럴 필요는 없으므로 Packet 단위만 한 단위로 자른다.

3) 임의의 위치로 파일위치를 옮길 수 있는 기능이 구현되면 좋다.

이 기능은 TIF나 PCX 파일처럼 필요한 Data가 파일위치 순으로 되지 않은 경우에 필요하다. 또한 중간에 Error가 난 Packet을 버리고 특정 위치부터 다시 받는 기능들은 실시간 멀티미디어 프로토콜에는 필수 규격이다. 특히, PCX 파일처럼 팔레트(Palette)가 파일의 뒤쪽에 위치해 있는 경우에도 필요하므로 이 기능은 실시간 멀티미디어 프로토콜에 있어야 하는 기능이다. 예를 들면, 화상통신을 하는 도중에 화상에 Error가 발생하면 예전의 사람얼굴이나 동작은 현재 필요하지 않으므로, 예전 위치는 버리고 현재 생성된 새로운 데이터 위치부터 처리해 줄 수 있는 위치 보정 기능이 필요하다.

4) 위 3)항의 기능과 연계하여, 한 Packet 블록의 크기를 정할 수 있어야 한다. 예를 들어, 파일 위치 옵셋(Offset) 100에서 2000바이트 (Byte)를 읽어야 할 경우 Packet 크기가 1024바이트로 일정하다면, 첫째 Packet은 1024바이트를 보내고 둘째 Packet은 (2000-1024) 바이트 만큼을 보내야 하는 데도 불구하고 1024바이트를 보내야 하므로, 대단히 비 효율적이다. 따라서, Packet 크기는 Server 쪽인 Sender가 검사해서 보낼 수 있도록 설계되어야 한다. 만약, 파일전체를 모두 순차적으로 보낼 필요가 있다면, Packet 블록은 파일전체의 크기로 지정하면 된다.

다. 실시간 멀티미디어 프로토콜의 제한사항

1) 파일의 특성에 따라 저장이 될 경우와 불가능 할 경우가 있다.

衁) 저장이 불가능한 경우 : 읽을 때 파일을 순차적 읽지 않은 경우 저장이 불가능하다.

遁) 저장이 가능하기는 하지만 구현하기에 힘든 경우 : 파일을 순차적으로 보내주지만 fseek 명령과 같이 파일의 위치를 옮겨서, 파일의 중간에 건너뛰는 부분이 있는 경우 구현하기가 힘들다.

2) 전송시 사용되는 Data Code는 통신의 특수 Code를 피해야 한다.

衁) 사용해서는 안 되는 Code : XON, XOFF와 Packet header(Header)용 특수 Code, 기타 Server에서 받아들이지 못하는 Code 등은, 상대방에서 인식할 수 있는 다른 Code로 바꾸어서 전송해야만 한다. 위의 Code들은 Server에서 특수한 목적으로 사용되는 경우가 많으므로 이러한 Code들은 미리 충돌 나지 않도록 Encoding을 해서 보내야만 한다.

遁) 예 : Z-modem 전송방식의 경우, 이것을 ZESC_7bit ZESC_Control 등의 Code를 시작 Packet의 header에 포함시켜, 이것을 기준으로 상호 전송시 사용할 Code를 지정한다.

3) 이어받기는 안 되는 것으로 한다.(단, Packet 블록이 전체 파일인 경우는 가능하도록 할 수 있다.)

실시간 멀티미디어 프로토콜은 실시간이라는 특성상 예전에 받았던 위치에서 다시 이어받기를 할 필요성이 없다. 일반적인 Z-modem 프로토콜은 일단 파일이 대부분이므로 파일을 받다가 연결이 끊어진 경우, 예전에 받았던 위치로 다시 복귀하여 파일을 받을 수 있으나 실시간의 경우에는 이렇게 하면 오히려 효율면에서 떨어지므로, 구현은 가능하나 제외시키기로 한다.

4) 멀티미디어 프로토콜의 메모리 사용

멀티미디어 프로토콜은 프로토콜 이외에 멀티미디어를 보거나 실행하기 위한 다른 모듈이 메모리 상에서 프로토콜과 동시에 돌아가므로, 메모리는 되도록 적게 사용해야만 한다. 즉, 간단한 프로토콜 방식을 채택해야만 한다. 또한, 실시시간의 경우 Client 쪽에서 제 시간에 처리하지 못하면 바로 메모리가 Overflow가 생기므로 이런 점을 유의해서 설계하여야 한다.

5) 파일 전송시 파일위치를 자주 바꾸지 않도록 설계하는 것이 좋다.

파일의 위치가 바뀔 때 Error(Error)가 날 경우 Error를 복구하기 힘들고, 복구한다 하더라도 시간이 많이 소요된다. 특히 Modem(Modem)의 경우 데이터 송신의 실시간성을 보장받기 힘들게 되므로 주의하여야 한다.

6) Packet 크기 지정

위에서 언급한 대로 Packet의 크기는 가변적인 것이 좋긴 하지만, 특정크기(32, 64, 128, 256, 512, 1024)로 전송 되게 하는 것이 Error검사에 유리하다. Packet 크기는 header에 포함하는 것보다 Terminator Code를 전송하여 받는 쪽에서 검사할 수 있게 하는 것이 더 좋다.

라. 블록형태

: 블록의 형태는 아래와 같이 3가지의 형태를 가진다.

1) 완전 PACKET BLOCK

한 Packet이 꽉 찬 형태, 즉 1024 Byte나 512 Byte의 사이즈를 가진 형태

2) 불완전 PACKET BLOCK

Packet의 크기가 가변인 블록, 이런 형태는 파일의 특정위치에서 작은 Packet은 반드시 ACK를 받아야 한다, 그렇지 않을 경우 Sender는 이전의 파일위치를 여러 개 기억하고 있어야 하므로 구현하기 힘들다.

3) END BLOCK

마지막 Packet인 경우 블록의 크기는 임의의 크기가 되므로, 이 블록은 반드시 ACK를 기다려야 한다.

마. Packet의 종류

: 전송 Packet에는 아래와 같은 종류가 있다.

1) 연속 Packet

상대방에서 Error를 보내기 이전까지는 제대로 전송된 것으로 간주하는 Packet인데, Z-MODEM의 ZCRCG가 이런 종류에 속한다.

2) 응답요구 Packet

한 블록을 보낸 후 ACK를 받아야만 다음 Packet을 보내는 방식인데, 이 방식은 전송속도가 느려질 수 있고, 특히 전송지연(Line delay)이 클 경우 불리하다, 하지만, 위의 연속 Packet과 혼용하여 사용할 경우는 유용하다. 예를 들어, 연속 Packet을 계속하여 받다가 연속Packet의 마지막 Packet은 응답을 받는 Packet으로 할 경우, Error검사나 제대로 받았는지 여부를 확인 할 수 있게 된다.

바. Header의 형태

1) 첫째 정보

header의 첫 번째 바이트는 Data의 일부와 구별되어야 하므로, 이점에 유의하여 정한다. 이 header 부분은 Error가 날 경우 얼마나 복구를 빨리 할 수 있는가와 연관되어 있으므로, 주의하여 정해야한다. 이것을 한 바이트로 할 경우 쉽게 구분가지 않으므로 두 바이트로 하는 것이 좋다.

2) 둘째 정보

header에는 파일의 위치정보가 기록되거나 Packet의 번호가 기록되어야 한다. 이것은 연속 Packet을 사용할 경우 꼭 필요하다.

3) 셋째 정보

Packet의 형태를 기록해야 한다.

4) 넷째 정보

Packet의 CRC를 Data의 뒤쪽에 기록한다.

5) 기타

EOL은 CRC의 앞에 기록할 수도 있고 뒤에 기록할 수도 있는데, 이것의 위치는 Error검사시 CRC값에 EOL Code가 합산되는지 안 되는지의 차이이므로 적당히 정한다.

사. 실시간 멀티미디어 통신프로토콜 규격

아래에 사용되는 KDLE는 CAN과 같은 ASCⅡ 24번 Code로서 Ctrl-X와 같은 Code이다. 이 Code는 Z-Modem과 같은 구분자로서 예전 K-Modem의 DLE와 바뀐 점 중의 하나이다.

1) 반드시 Encoding 해야 하는 Data

XON KDLE

위의 Data 이외에도 Server 쪽에서 통신망과 장비 등을 통과할 때 특정 목적으로 사용된 수 있는 데이터들은 미리 Encoding을 하여서 내려보내야 한다.

그 Encoding은 아래에 정한 방식대로 한다.

2) Encoding 방식

DATA -> KDLE + DATA ^ Ox40h

송신('r'), 수신('s') Packet(packet) OFFSET 영역의 첫 번째 Byte에 위의 정보를 실어보낸다.

bit 6 set: Control Character는 모두 Encoding

Control Character: C0 C1

bit 5 set: NUL 7Fh FFh 만 Encoding

위의 두 bit 중 하나도 set 되어있지 않으면 위에서 반드시 Encoding 해야 한다고 하는 것만 하면 된다.

3) Packet의 형태

KDLE + 'k' + HEAD_TYPE + OFFSET + DATA + TERMINATOR + CRC

----------- 1byte encoded 4bytes encoded n bytes 2bytes encoded 2bytes

Header Introduce

HEAD_TYPE

's' Sender Info

'r' Receiver Info

'i' File Info

'b' Block Region That 1 Need(Start Position + End Position)

'd' Data Block

'p' File Position

'f' Flow Control Packet

'y' ACK

'n' NAK

'c' Block Close(No More Data Need)

'e' Protocol End

TERMINATOR_TYPE

KDLE 'g' 연속 Packet

KDLE 'w' 일반 Packet(ACK 또는 필요한 Packet)

KDLE 'q' Header 없는 일반 Packet(ACK가 필요)

4) Encoding 방법

衁) Data가 XON, XOF, KDLE 중 하나 일 때

KDLE + (Data ^ 0x40)

遁) Control 문자의 Coding이 필요할 때

이것은 Sender Info와 Receiver Info를 주고받을 때, 서로 교환하여 Coding할지를 정한다.

예) 위의 衁)과 동일한 방법

CRC : CCITT에서 정한 CRC-32를 기준으로 하고 초기 값은 0xFFFFFFFF(-1)를 사용한다.

CRC32 = 0xFFFFFFFL;

CRC32 = CalcCRC32(CRC32, Data);

KDLE + 'k' + HEAD_TYPE + OFFSET + DATA + TERMINATOR + CRC

----------- 1byte encoded 4bytes encoded n bytes 2bytes encoded 4bytes

Header Introduce







CRC 검사할 Data들

또는

DLE + 'I' + HEAD_TYPE + OFFSET + DATA + TERMINATOR + TextCRC

----------- 1byte encoded 4bytes encoded n bytes 2bytes text 8bytes

Header Introduce







CRC 검사할 Data들

Encode 하는 Data는 Encode되기 전의 원래 값으로 검사한다.

TERMINATOR는 검사시 g-type이면 'g'를 w-type이면 'w'를 넣는다.

KDLE 'g' -> 'g'만 포함

KDLE 'w' -> 'w'만 포함

KDLE 'q' -> 'q'만 포함

아. 멀티미디어 프로토콜의 구현 방법

1) Escape Sequence

ESC & M 파일 확장자 * : 호스트가 통신 풀그림에 이미지프로토콜을 지원하는지 묻는 것.

그 이외에 화면상태 등을 이미지에 맞도록 전환한다.

위의 파일 확장자는 AVI, JPG, WAV 등의 이름이며 뒤에는 '*'(별표) Code가 온다.

ESC & M 1 CR : Client 쪽의 응답

지원하는 파일이면 ASC II '1'의 글자가 오며, 지원하지 않는 파일이면 '0'이 온다.

2) 프로토콜 시작 Code

기본적인 Form : ESC(0x1b) % K 파일 확장자 * (별표)

마지막에는 '*'(별표) Code가 오며 파일 확장자는 멀티미디어 파일의 확장자가 된다.

지원되는 파일의 종류

ESC % K AVI* : 동화상 파일 AVI

ESC % K JPG* : MultiMedia용 JPG

ESC % K GIF* : MultiMedia용 GIF

ESC % K PCX* : MultiMedia용 PCX

ESC % K TIF* : MultiMedia용 TIF

ESC % K IMS* : MultiMedia용 IMS

ESC % K MID* : MultiMedia용 MID(One track midi)

ESC % K WAV* : MultiMedia용 VOC, WAV, KSV (음성파일)

ESC % K 기타파일 확장자* : 일반파일 전송시

자. 실시간 멀티미디어 통신프로토콜 프로토콜의 흐름도

** 97-39-0049.gif

** 97-39-0050.gif

차. TERMINATOR Type에 따른 Data Packet의 형태

1) g type을 사용할 경우 : 속도면에서 유리하다.

KDLE + 'k' + 'd' + OFFSET + DATA + g-type + CRC

or

KDLE + 'I' + 'd' + OFFSET + DATA + g-type + CRC

위의 두 가지 Packet 방식에서

KDLE + 'k' packet의 경우 CRC는 Binary로 4Byte를 받고

KDLE + 'I' packet의 경우 CRC는 Text로 8Byte로 풀어서 보낸다.

이는 처음 Negotiation 시에 Binary로 보내면 충돌이 나는 경우가 생길 것을 대비하여 8Byte의 text로 주고 받는다.

DATA + g-type + CRC

끝 Packet만 제외하고 전부 g-

type DATA + w-type + CRC

으로함

Wait ACK

2) w type만을 사용할 경우 : Error 검사면에서 유리하다.

KDLE + 'k' + 'd' + OFFSET + DATA + w-type + CRC

Wait ACK

KDLE + 'k' + 'd' + OFFSET + DATA + w-type + CRC

Wait ACK

KDLE + 'k' + 'd' + OFFSET + DATA + w-type + CRC

Wait ACK

3) 적당한 크기로 갈라서 보내는 방법

4Packet이나 8Packet 정도마다 한번씩 w-type을 쓴다.

위에서 OFFSET은 보내는 Packet의 파일 시작에서부터 위치 OFFSET이다.

정상적인 경우 다음 OFFSET의 이전 Packet의 OFFSET에 Data 블록의 길이를 합한 것과 같아야 한다. 틀리다면 전송시 Error가 난 경우이다.

위의 g-type 경우는 OFFSET 부분이 없으므로 다음 Packet의 OFFSET은 앞 Packet의 OFFSET에 블록 길이를 합해서 계산한다. CRCError가 나면 Position Change Packet을 보낸다.

카. Packet header에 따른 Data 규격

1) Packet header 종류

's' : 우선 Offset이 들어가는 4byte를 이용해서 Control 캐릭터 Encoing 여부

최대 Data Packet 길이 등을 전송한다.

IMAGE 정보일 경우 시작위치를 Data Block에 정수 4byte로 전송한다.

Packet 길이는 512바이트 단위로 512 1024 1536 2048 등으로 정한다.

Packet 길이가 0으로 지정되어 있으면 1024로 한다.

'r' : 위의 's'와 같다.

'i' : 파일의 정보를 보내는 Packet, OFFSET 자리에 파일의 길이를 기록

파일이름은 Data 블록을 이용해서 보낸다. 지금은 이름만 넣지만 파일의 날짜 속성 등을 보낼 수도 있다.

'b' : 받는 쪽에서 받기를 원하는 영역을 보낼 때 쓴다.

OFFSET에 우선 영역의 시작 OFFSET을 넣고 Data 블록에 끝 OFFSET을 넣는다.

'd' : Data Packet 위(3.)에서 정한 것과 같다.

'y' : Data를 정상적으로 받았음을 알리는 Packet

'n' : NAK

'f' : 받는 쪽에서 w-type으로 받기를 요청

보내는 쪽에서 너무 빨리 보내거나, 받는 쪽의 처리속도가 느릴 때 보낸다.

'p' : Data가 틀린 경우 다시 받기를 요청하는 것

'x' : 받는 쪽이 더 필요한 Data가 없다고 Sender에 알리는 Packet

'e' : 프로토콜이 종료되었음을 알리는 Packet

파일이 여러 개일 경우 다시 'i' Packet을 보낸다.

'o' : 전송 옵션(Option)을 Negotiation 하는 Packet

초기에 한번만 서로의 정보를 알기 위해서 Client에서 Server로 보낸다.

2) 파일이 여러 개인 경우 'i' Packet을 받았을 때

이때 받는 쪽에서는 자동으로 받고 있는 파일을 닫아준 다음, 이 파일을 다시 열도록 한다.

이는 실시간 멀티미디어 프로토콜에서는 의미가 없다. 하지만, 프로토콜에서 여러 개의 파일 송/수신을 지원해야 하므로 프로토콜 기능을 지원하였다.

앞에서 설명한 내용은 실시간 멀티미디어 프로토콜(Real-Time Multimedia Protocol)인 실시간 멀티미디어 통신프로토콜에 관한 설명이었다. 이 프로토콜을 응용하여 다양한 멀티미디어 파일을 송/수신할 수 있다.

3. 실시간 멀티미디어 통신프로토콜 구현 Server 쪽 원시 Code

아래 부분은 실시간 멀티미디어 통신프로토콜의 원시 Code(Source code)이며, UNIX 계열에서 실제 사용되는 Code 이다. Code를 기종에 맞게 Porting 한다면, 실시간 멀티미디어 프로토콜로 사용할 수 있다.

/*

Real-Time Multi-media Protocol

실시간 멀티미디어 통신프로토콜

Copyright (c) 큰사람 컴퓨터(주) 1998, 1999

Great Human Software co., Ltd.

*/

/*

모토로라 계열일 경우 아래의 _MOTO_를 선언해 준다.

x86계열일 경우 아래의 _MOTO_를 코멘트 해야 한다.

*/

/*

#define _MOTO_

*/

/*

#define IOCTL_SUPPORT

*/

/*

#define WAIT_START

*/

/* Hardware flow control 지정 */

#define EXIST_RTSCTS

#define KSR_UNIX /* for Unix machine */

/*

#define _SYS_V_

*/

/* 특정 SUN 계열에서는 아래의 _SVID3를 선언해야 한다. */

/*

#define _SVID3

*/

/********************

Header file include

********************/

#include <stdio.h>

#include <fcntl.h>

#include <setjmp.h>

#include <time.h>

#ifdef DOS

#include <bios.h>

#include <dos.h>

#include <stdlib.h>

#include <string.h>

#include <conio.h>

#include <io.h>

#endif

#ifdef KSR_UNIX /* Unix define */

#include <string.h>

#include <unistd.h>

#include <termio.h>

#include <sys/ioctl.h>

#include <sys/stat.h>

#ifdef _SYS_V_

#define _IEXTEN IEXTEN

#endif

#endif

/********************

Type & structure define

********************/

typedef struct protocol_param multi_proto;

struct protocol_param

{

long byte_count;

long file_length;

long blockstartoffset;

long blockendoffset;

short curblocksize;

union {

unsigned char headbyte[4];

long packet_offset;

} head;

unsigned char *buffer;

short mybufsize;

short hisbufsize;

short packet_type;

short skip_header;

short terminator_type;

short escape_control;

short escape_nul;

short cancel_count;

unsigned char name[256];

};

/********************

Defines

********************/

#define TRUE 1

#define FALSE 0

#define NUL 0

#define DLE 16

#define CR 13

#define LF 10

#define XON 17

#define XOF 19

#define CAN 24

#define ESC 27

#define CANDLE 1

#ifdef CANDLE

#define KDLE CAN

#else

#define KDLE DLE

#endif

#define READ_OPEN 1

#define WRITE_OPEN 2

/* Packet Header Type define */

#define KOPTINIT 'o'

#define KSINIT 's'

#define KRINIT 'r'

#define KBLOCKNEED 'b'

#define KFILEINFO 'i'

#define KBLOCKDATA 'd'

#define KRPOS 'p'

#define KACK 'y'

#define KFLOW 'f'

#define KBLOCKEND 'c'

#define KFILEEND 'e'

#define KFAIL 'x'

#define KNAK 'n'

#define TIMEOUT -8

#define CRC_ERROR -9

#define USER_CANCEL -10

#define R_GET_DLE 0

#define R_GET_K 1

#define R_GET_HEAD 2

#define R_GET_OFFSET 3

#define R_GET_DATA 4

#define R_GET_CRC 5

#define R_SEND_ACK 6

#define R_TIMED_OUT 7

#define R_SUCCESS 8

#define R_CRC_ERROR 9

#define R_USER_CANCEL 10

#define MAX_ERRORS 10

#define GOTW (256+'w')

#define GOTG (256+'g')

#define GOTQ (256+'q')

#define GOTUNUSE (256+'u')

#define GOTPAD (256+'k')

#define GOTLONGPAD (256+'l')

#define GOTCAN (256+CAN)

#define MAX_WAIT_TIME (3000L)

#define MAX_START_WAIT_TIME (5000L)

/********************

Function proto types

********************/

#ifdef KSP_UNIX /* Unix */

char *strupr(char *);

int TerminalOpen(int);

void TerminalClose( );

short ConvDosShort(short);

long ConvDosLong(long);

long filelength(FILE *);

void AsyncSend(unsigned char);

void WriteBuffer(char *, short);

short GetAsync( );

short TxBufferFull( );

short RxBufferReady( );

void ClearTxBuffer( );

unsigned short CalculateCharacterCRC16( );

void SendCancelCode( );

short WaitSendChar( );

short WaitReadChar(long milliseconds );

short_WaitReadChar(long milliseconds );

void SendEncodedByte(unsigned char);

short InsertEncodeByte(unsigned char, char*);

short ReadEncodedByte( );

short ReadPacket( );

void SendPacket(multi_proto *mpro, short skip_head,

short terminator);

void SendDataPacket(multi_proto *mpro,

short skip_head,

short terminator,

FILE *handle);

short KStreamSendFile(char *name, short startx, short starty,

short resolution);

#else

void far AsyncSend(unsigned char ch);

void WriteBuffer(char *, short);

short far GetAsync(void);

short far TxBufferFull(void);

short far RxBufferReady(void);

void ClearTxBuffer(void);

/*Calculate CRC16 from one character */

unsigned short CalculateCharacterCRC16(unsigned short crc, unsigned char c);

/*Send Cancel code */

void SendCancelCode(void);

/*Write one character to Stream */

short WaitSendChar(unsigned char data);

/* Wait and read one character from Stream */

short WaitReadChar(long milliseconds);

short _WaitReadChar(long milliseconds);

/*Encode one byte and send it */

void SendEncodedByte(unsigned char data);

short InsertEncodeByte(unsigned char, char *);

/* Read Encoded byte */

short ReadEncodedByte(multi_proto *mpro);

/* Read one packet */

short ReadPacket(multi_proto *mpro);

/* Send one Packet */

void SendPacket(multi_proto *mpro, short skip_head,

short terminator);

/* Send Data Packet */

void SendDataPacket(muti_proto *mpro, short skip_head,

short terminator, FILE *handle);

/* KStream main procedure */

short KStreamSendFile(unsigned char *name);

#endif

/********************

Variable define

********************/

int forcefilemode;

int forceackmode;

int forceescapemode;

int forcenulmode;

char writebuf[2500];

/*

1024byte가 전부 Encoding 되었을 때 2048

그리고 header 등의 추가Data가 들어갈

여유분의 버퍼

*/

int windex;

FILE *tty;

int cachedch;

jmp_buf BitmapJump;

static unsigned char databuf[1025];

/*

Encode 해야하는 것은 1로 지정

안해도 되는 것은 0으로 지정

*/

static unsigned char encodeflag[256];

/** ccitt crc 32 table CRC Error 검사를 위한 CRC32 Table **/

static unsigned long ccitt_32[256] =

{

0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL,

0x076dc419L, 0x706af48fL, 0xe963a535L, 0x9e6495a3L,

0x0edb8832L, 0x79dcb8a4L, 0xe0d5e91eL, 0x97d2d988L,

0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 0x90bf1d91L,

0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,

0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L,

0x136c9856L, 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL,

0x14015c4fL, 0x63066cd9L, 0xfa0f3d63L, 0x8d0d0df5L,

0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 0xa2677172L,

0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,

0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L,

0x32d86ce3L, 0x45df5c75L, 0xdcd60dcfL, 0xabd06116L,

0x26d930acL, 0x51de003aL, 0xc8d75180L, 0xbfd06116L,

0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 0xb8bda50fL,

0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,

0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL,

0x76dc4190L, 0x01db7106L, 0x98d220bcL, 0xefd5102aL,

0x71b18589L, 0x60b6b51fL, 0x9fbfe4a5L, 0xe8b8d433L,

0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 0xe10e9818L,

0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,

0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL,

0x6c0695edL, 0x1bo1a57bL, 0x8208f4c1L, 0xf50fc457L,

0x65b0d9c6L, 0x12b7e950L, 0x8bbeb8eaL, 0xfcb9887cL,

0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 0xfbd44c65L,

0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,

0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL,

0x4369e96aL, 0x346ed9fcL, 0xad678846L, 0xda60b8d0L,

0x44042d73L, 0x33031de5L, 0xaa0a4c5fL, 0xdd0d7cc9L,

0x5005713cL, 0x27024laaL, 0xbe0b1010L, 0xc90c2086L,

0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,

0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L,

0x59b33d17L, 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL,

0xedb88320L, 0x9abfb3b6L, 0x03b6e20cL, 0x74bld29aL,

0xead54739L, 0x9dd277afL, 0x04db2615L, 0x73dc1683L,

0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,

0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L,

0xf00f9344L, 0x870a3d2L, 0x1e01f268L, 0x6906c2feL,

0xf762575dL, 0x806567cbL, 0x196c3671L, 0x6e6b06e7L,

0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 0x6edd4accL,

0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60bo8ed5L,

0xd6d6a3e8L, 0xald1937eL, 0x38d8c2c4L, 0x4fdff252L,

0xd1bb67f1L, 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL,

0xd80d2bdaL, 0xaf0a1b4cL, 0x36034af6L, 0x41047a60L,

0xdf60efc3cL, 0xa867df55L, 0x316e8eefL, 0x4669be79L,

0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,

0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL,

0xc5ba3bbeL, 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L,

0xc2d7ffa7L, 0xb5d0cf31L, 0x2cd99e8bL, 0x5bdeae1dL,

0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 0x026d930aL,

0x9c0906a9L, 0xeboe363fL, 0x72076785L, 0x05005713L,

0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L,

0x92d28e9bL, 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L,

0x86d3d2d4L, 0xf1d4e242L, 0x68ddb3f8L, 0x1fda836eL,

0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 0x18b74777L,

0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,

0x8f659effL, 0x862ae69L, 0x616bffd3L, 0x166ccf45L,

0xa00ae278L, 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L,

0xa7672661L, 0xd06016f7L, 0x4969474dL, 0x3e6e77dbL,

0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 0x37d83bf0L,

0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,

0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L,

0xbad03605L, 0xcdd70693L, 0x54de5729L, 0x23d967bfL,

0xb3667a2eL, 0xc4614ad8L, 0x5d681b02L, 0x2a6f2b94L,

0xb40bbe37L, 0xc30c8ealL, 0x5a05df1bL, 0x2d02ef8dL,

};

static short EndProtocol=0;

static short EndSet=0;

static long nextblockstart;

static long nextblockend;

static long file_length;

static multi_proto ksrproto;

/******************************************

Function : CalculateCharacterCRC32

CRC32 값을 만들어 가는 함수

Returns : CRC32 만들어진 값

******************************************/

/*

*unsigned long CalculateCharacterCRC32(unsigned long crc,

* unsigned char c)

*DESCRIPTION

*This routine is used to update a crc after a new character has been

*read in.

*It calculates the cumulative CRC for the entire block so far.

*RETURNS

*A new crc value.

*/

unsigned long CalculateCharacterCRC32(crc32, c)

unsigned long crc32;

unsigned char c;

{

long rtv;

rtv = (ccitt_32[((short) crc32^c) & 0xff]

^((crc32>>8) & 0x00ffffffL));

return rtv;

]

/******************************************

Function : SendCancelCode

Cancel Code 값을 보내는 함수, 사용자의 갑작스런 종료 또는

기타 상황 발생시 프로토콜을 끝내기 위해 사용함

Returns : 없음

******************************************/

/*Send Cancel code */

void SendCancelCode()

{

long endoffset;

EndSet=0;

ksrproto.packet_type=KFILEEND;

ksrproto.head.packet_offset=ksrproto.file_length;

ksrproto.curblocksize=0;

ksrproto.buffer=(unsigned char *)&endoffset;

ksrproto.skip_header=0;

SendPacket(&ksrproto, 0, 'w');

}

/******************************************

Function : Long20ctal

Returns : 없음

******************************************/

void Long20ctal(unsigned long 1, char *0)

{

int i, j;

int data;

char str[14];

str[11]= '\0';

j=11;

for ( i =10; i >=0; I--)

{

data=1&0x7L;

1>>=3;

if( data==0 && I==0 )

break;

str[i] = (char)('0' + data);

j=i;

}

if(j==11) (

str[10]='0';

j--;

}

strcpy(0, &str[j]);

}

/******************************************

Function : WaitSendChar

1자를 보내는데, Send buffer가 Full 또는 pending이 걸려 있으면

그 기능이 해제 될 때까지 기다렸다가 보낸다.

Returns : 항상 0

******************************************/

/*Write one character to Stream */

short WaitSendChar(data)

unsigned char data;

{

long timeout;

short key;

time_t now;

/*if not buffer full, Above Comm buffer size */

if(!TxBufferFull())

{

AsyncSend(data);

return 0;

}

time(&now);

timeout = now +20; /*wait 20 Sec */

while(TxBufferFull())

{

time(&now);

if(timeout <= now)

{

return -8,

}

}

AsyncSend(data);

return 0;

}

/******************************************

Function : _WaitReadCar

지정된 시간동안 글자를 읽어낸다.

Returns : 읽은 값

******************************************/

/*Wait and read one character from Stream */

short _WaitReadChar(long milliseconds)

{

short key;

while(1)

{

if(RxBufferReady())

key=GetAsync();

else

key=-1;

switch(key)

{

case XON :

case XOF :

break ;

default

if( key>=0)

{

return key;

}

}

}

}

/******************************************

Function : WaitReadCar

지정된 시간동안 글자를 읽어낸다.

Returns : 읽은 글자 값

******************************************/

/*Wait and read one character from Stream */

short WaitReadChar(long milliseconds)

{

long timeout;

short key;

time_t now;

time(&now);

timeout = now + milliseconds/1000;

while(1)

{

if(RxBufferReady())

key=GetAsync();

else

{

key=-1;

time(&now);

if(timeout<=now)

return -8;

}

switch(key)

{

case XON :

case XOF :

break;

default :

if(key>=0)

{

return key;

}

}

}

}

/******************************************

Function : SendEncodedByte

Encode 된 글자를 보낸다.

Returns : 없음

******************************************/

/* Encode one byte and send if */

void SendEncodedByte(unsigned char data)

{

if(encodeflag[data])

{

data^=0x40;

WaitSendChar(KDLE);

WaitSendChar(data);

return;

}

switch(data)

{

case DLE:

case XON:

case XOF:

case CAN:

data^=0x40;

WaitSendChar(KDLE);

WaitSendChar(data);

break;

case NUL:

case 0x7f:

case 0xff:

if(ksrproto.escape_nul)

{

WaitSendChar(KDLE);

data^=0x40;

WaitSendChar(data);

break;

}

default :

if(ksrproto.escape_control && (((int)(data&0x7f))<32))

/* ESC CTRL */

{

SendChar(KDLE);

data^=0x40;

}

WaitSendChar(data);

}

}

/******************************************

Function : InsertEncodedByte

Encoding 글자를 삽입한다.

Returns : 삽입된 글자 수

******************************************/

/* Encoded byte insert */

short InsertEncodedByte(unsigned char data, char *buf)

{

if(encodeflag[data])

{

data^=0x40;

*buf=KDLE;

buf++;

*buf=data;

return 2;

}

switch(data)

{

case DLE:

case XON:

case XOF:

case CAN:

data^=0x40;

*buf=KDLE;

buf++;

*buf=data;

return 2;

case NUL:

case 0x7f:

case 0xff:

if(ksrproto.escape_nul)

{

*buf=KDLE;

buf++;

data^=0x40;

*buf=data;

return 2;

}

default :

if(ksrproto.escape_control &&(32>(int)(data&0x7f)))

/*ESC CTRL */

{

*buf=KDLE;

buf++;

data^=0x40;

*buf=data;

return 2;

}

*buf=data;

return 1;

}

}

/******************************************

Function : ReadEncodedByte

Encode 된 글자를 읽어낸다.

Returns : Encode가 풀린 글자 값

******************************************/

/* Read Encoded byte */

short ReadEncodedByte(mpro)

multi_proto *mpro;

{

short c;

Again:

c=WaitReadChar(MAX_WAIT_TIME);

if(c<0)

return c;

if(c>=32)

return c;

switch(c)

{

case DLE:

#ifdef CANDLE

return c;

#else

break;

#endif

case XON:

case XOF:

goto Again;

case CAN

#ifdef CANDLE

break;

#else

mpro->cancel_count++;

if(mpro->cancel_count>=5)

return GOTCAN;

goto Again;

#endif

default:

return c;

}

for(;;)

{

c=WaitReadChar(MAX_WATT_TIME);

if(c<0)

return c;

switch(c)

{

case 'l' : return GOTLONGPAD;

case 'k' : return GOTPAD;

case 'w' : return GOTW;

case 'g' : return GOTG;

case 'q' : return GOTQ;

case 'u' : return GOTUNUSE;

case XON :

case XOF : break;

case CAN :

#ifdef CANDLE

mpro->cancel_count++;

#endif

mpro->cancel_count++;

if(mpro->cancel_count>=5)

return GOTCAN;

return(-1); /* Encoding or Line Error */

default

c^=0x0040;

return c;

}

}

}

/******************************************

Function : ReadPacket

하나의 Packet을 읽어 들인다.

Returns : 성공하면 True(1), 실패하면 False(0)의 값

******************************************/

/* Read one packet */

short ReadPacket(multi_proto *mpro)

{

short State;

unsigned long crc;

short errors;

multi_proto spro;

short c;

short i;

int HeadType;

char crcbuf[4];

mpro->canncel_count=0;

if(mpro->skip_header)

{

mpro->packet_type=KBLOCKDATA;

mpro->head.packet_offset=pro->byte_count;

State = R_GET_DATA;

i=0;

crc=0xFFFFFFFFL;

HeadType=0;

}

else

State = R_GET_DLE;

while (TRUE)

{

switch (State)

{

case R_GET_DLE:

c = WaitReadChar(MAX_START_WAIT_TIME);

if (c<0)

State = R_TIMED_OUT;

else

if(c == KDLE) {

State = R_GET_K;

mpro->cancel_count++;

if(mpro->cancel_count >=5)

State = R_USER_CANCEL;

}

break;

case R_GET_K :

c = WaitReadChar(MAX_WAIT_TIME);

if(c<0)

State = R_TIMED_OUT;

else

{

switch(c)

{

case 'k':

State = R_GET_HEAD;

HeadType=0;

break;

case 'l':

State = R_GET_HEAD;

HeadType=1;

break;

case KDLE

State = R_GET_K;

mpro->cancel_count++;

if(mpro->cancel_count>=5)

State = R_USER_CANCEL;

break;

default:

State = R_GET_DLE;

}

}

break;

case R_GET_HEAD:

c = WaitReadChar(MAX_WAIT_TIME);

if(c<0)

State = R_TIMED_OUT;

else

if(c == KDLE)

{

State = R_GET_K;

mpro->cancel_count++;

if(mpro->cancel_count >=5)

State = R_USER_CANCEL;

}

else

{

crc = 0xFFFFFFFFL;

crc = CalculateCharacterCRC32(crc,

(unsigned char) c);

mpro->packet_type=c;

i= 0;

State = R_GET_OFFSET;

}

break;

case R_GET_OFFSET :

c = ReadEncodedByte(mpro);

if(c<0)

{

State = R_TIMED_OUT;

}

else

if(c==GOTPAD)

{

State = R_GET_HEAD;

HeadType=0;

}

else

if(c==GOTLONGPAD)

{

State = R_GET_HEAD;

HeadType=1;

}

else

if(c==GOTCAN)

{

State = R_USER_CANCEL;

}

else

{

if(HeadType==0)

{

crc=CalculateCharacterCRC32(crc,

(unsigned char) c);

/* 기종에 따라 구분을 해 준다. *

#ifdef _MTO_

mpro->head.headbyte[3-i] = c;

#else

mpro->head.headbyte[i] = c;

#endif

i++;

if(i==4)

{

i = 0;

State = R_GET_DATA;

}

}

else

{

if (c>='0' && c<='9')

c = c - '0';

else if(c>='a' && c <='f')

c = c - 'a' + 10;

else

{

State=R_CRC_ERROR;

break;

}

/* 기종에 따라 구분을 해 준다. */

#ifndef _MOTO_

switch(i)

{

case 0:

mpro->head.headbyte[0]=c<<4;

break;

case 1:

mpro->head.headbyte[0]:=c; break;

case 2:

mpro->head.headbyte[1]=c<<4;

break;

case 3:

mpro->head.headbyte[1]:=c;

break;

case 4:

mpro->head.headbyte[2]=c<<4;

break;

case 5:

mpro->head.headbyte[2]:=c;

break;

case 6:

mpro->head.headbyte[3]=c<<4;

break;

case 7:

mpro->head.headbyte[3]:=c;

break;

}

i++;

if(i==8)

}

i=0;

crc=CalculateCharacterCRC32(crc,

mpro->head.headbyte[0]);

crc=CalculateCharacterCRC32(crc,

mpro->head.headbyte[1]);

crc=CalculateCharacterCRC32(crc,

mpro->head.headbyte[2]);

crc=CalculateCharacterCRC32(crc,

mpro->head.headbyte[3]);

State = R_GET_DATA;

}

#else

switch(i)

{

case 0:

mpro->head.headbyte[3]=c<<4;

break;

case 1:

mpro->head.headbyte[3]:=c;

break;

case 2:

mpro->head.headbyte[2]=c<<4;

break;

case 3:

mpro->head.headbyte[2]:=c;

break;

case 4:

mpro->head.headbyte[1]=c<<4;

break;

case 5:

mpro->head.headbyte[1]:=c;

break;

case 6:

mpro->head.headbyte[0]=c<<4;

break;

case 7:

mpro->head.headbyte[0]:=c;

break;

}

i++;

if(i==8)

{

i=0;

crc=CalculateCharacterCRC32(crc,

mpro->head.headbyte[3]);

crc=CalculateCharacterCRC32(crc,

mpro->head.headbyte[2]);

crc=CalculateCharacterCRC32(crc,

mpro->head.headbyte[1]);

crc=CalculateCharacterCRC32(crc,

mpro->head.headbyte[0]);

State = R_GET_DATA;

}

#endif

}

}

break;

case R_GET_DATA :

c=ReadEncodedByte(mpro);

if(c<0)

{

State = R_TIMED_OUT;

}

else

{

switch(c)

{

case GOTW:

case GOTG:

case GOTQ:

mpro->terminator_type = c;

mpro->curblocksize=i;

crc=CalculateCharacterCRC32(crc, c&0xff);

State = R_GET_CRC;

i=0;

break;

case GOTUNUSE:

break;

case GOTPAD:

State = R_GET_HEAD;

HeadType=0;

break;

case GOTLONGPAD:

State = R_GET_HEAD;

HeadType=1;

break;

case GOTCAN:

State = R_USER_CANCEL;

break;

default:

if(i>=mpro->mybufsize)

{

State=R_CRC_ERROR;

}

else

{

crc=CalculateCharacterCRC32(crc,

(unsigned char)c);

mpro->buffer[i] = c;

i = i +1;

}

}

}

break;

case R_GET_CRC:

if(HeadType==0)

{

c=ReadEncodedByte(mpro);

if(c<0)

{

State=R_TIMED_OUT;

break;

}

else

if(c==GOTPAD)

{

State=R_GET_HEAD;

HeadType=0;

break;

}

else

if(c==GOTLONGPAD)

{

State=R_GET_HEAD;

HeadType=0;

break;

}

else

{

crc=CalculateCharacterCRC32(crc,

(unsigned char)c);

}

i++;

if(j==4)

{

if(crc!=0xdebb20e3L)

{

State=R_CRC_ERROR;

}

else /* Good sequence number */

State=R_SUCCESS;

}

}

else

{

c=ReadEncodedByte(mpro);

if(c>0)

{

State=R_TIMED_OUT;

break;

}

else

if(c==GOTPAD)

{

State=R_GET_HEAD;

HeadType=0;

break;

}

else

if(c==GOTLONGPAD)

{

State=R_GET_HEAD;

HeadType=1;

break;

}

else

{

if(c >= '0' && c <= '9')

c = c - '0';

else if(c >= 'a' && c <= 'f')

c = c - 'a' + 10;

else

{

State=R_CRC_ERROR;

break;

}

}

switch(i)

{

case 0:

crcbuf[0]=c<<4;

break;

case 1:

crcbuf[0]:=c;

break;

case 2:

crcbuf[1]=c<<4;

break;

case 3:

crcbuf[1]:=c;

break;

case 4:

crcbuf[2]=c<<4;

break;

case 5:

crcbuf[2]:=c;

break;

case 6:

crcbuf[3]=c<<4;

break;

case 7:

crcbuf[3]:=c;

break;

}

i++;

if(i==8)

{

i=0;

crc=CalculateCharacterCRC32(crc, crcbuf[0]);

crc=CalculateCharacterCRC32(crc, crcbuf[1]);

crc=CalculateCharacterCRC32(crc, crcbuf[2]);

crc=CalculateCharacterCRC32(crc, crcbuf[3]);

State = R_GET_DATA;

if(crc!=0xdebb20e3L)

{

State = R_CRC_ERROR;

}

else

State = R_SUCCESS;

}

}

break;

case R_CRC_ERROR :

errors=errors +1;

mpro->packet_type=CRC_ERROR;

return(FALSE);

case R_TIMED_OUT :

errors=errors +1;

mpro->packet_type=TIMEOUT;

return(FALSE);

case R_USER_CANCEL :

mpro->packet_type=USER_CANCEL;

return(FALSE);

case R_SUCCESS :

return(TRUE);

}

}

}

/******************************************

Function : SendPacket

한 Packet을 전송한다.

Returns : 없음

******************************************/

/* Send one Packet */

void SendPacket(multi_proto *mpro, short skip_head,

short terminator)

{

short i;

unsigned long crc;

crc=0xFFFFFFFFL;

if(skip_head==0)

{

WaitSendChar(KDLE);

WaitSendChar( 'k' );

WaitSendChar(mpro->packet_type);

crc=CalculateCharacterCRC32(crc,

(unsigned char)mpro->packet_type);

for(i=0, i<4, i++)

{

/* 기종에 따라 구분을 해 준다. */

#ifdef _MOTO_

SendEncodedByte(mpro->head.headbyte[3-i]);

crc=CalculateCharacterCRC32(crc,

(unsigned char)mpro->head.headbyte[3-i]);

#else

SendEncodedByte(mpro->head.headbyte[i]);

crc=CalculateCharacterCRC32(crc,

(unsigned char)mpro->head.headbyte[i]);

#endif

}

}

for(i=0; i<mpro->curblocksize; i++)

{

SendEncodedByte(mpro->buffer[i]);

crc=CalculateCharacterCRC32(crc,

(unsigned char)mpro->buffer[i]);

}

WaitSendChar(KDLE);

WaitSendChar(terminator);

crc=CalculateCharacterCRC32(crc, terminator&0xff);

crc = -crc;

for(i=0; i<4; i++)

{

SendEncodedByte((unsigned char)((crc&0xff)&0xff));

crc>>=8;

}

}

/******************************************

Function : SendDataPacket

Data packet을 보내는 함수

Returns : 없음

******************************************/

/* Data packet Sending rountine */

void SendDataPacket(multi_proto *mpro,

short skip_head,

short terminator,

FILE *handle)

{

short i;

short byte_count;

short read_count;

short left_count;

unsigned long crc;

fseek(hadle, mpro->byte_count, SEEK_SET);

crc=0xFFFFFFFFL;

windex=0;

if(skip_head==0)

{

writebuf[windex++]=KDLE;

writebuf[windex++]='k';

writebuf[windex++]=mpro->packet_type;

crc=CalculateCharacterCRC32(crc,

(unsigned char)mpro->packet_type);

for(i=0; i<4; i++)

{

/* 기종에 따라 구분을 해 준다. */

#ifdef _MOTO_

windex+=InsertEncodedByte(mpro->head.headbyte[3-i],

&writebuf[windex]);

crc=CalculateCharacterCRC32(crc,

(unsigned char)mpro->head.headbyte[3-i]);

#else

windex+=InsertEncodedByte(mpro->head.headbyte[i],

&writebuf[windex]);

crc=CalculateCharacterCRC32(crc,

(unsigned char)mpro->head.headbyte[i]);

#endif

}

}

left_count=mpro->curblocksize;

mpro->curblocksize=0;

while(left_count)

{

if(left_count>mpro->mybufsize)

{

read_count=mpro->mybufsize;

left_count-=read_count;

}

else

{

read_count=left_count;

left_count=0;

}

byte_count=fread(mpro->buffer, 1, read_count, handle);

for(i=0;i<byte_count;i++)

{

windex+=InsertEncodedByte(mpro->buffer[i], &writebuf[windex]);

crc=CalculateCharacterCRC32(crc,(unsigned char)mpro->buffer[i]);

mpro->curblocksize++;

if(windex>=2048)

{

WriteBuffer(&writebuf[0], windex);

windex=0;

}

}

if(byte_count!=read_count)

{

printf("Read Error\n");

break;

}

}

writebuf[windex++]=KDLE;

writebuf[windex++]=terminator;

crc=CalculateCharacterCRC32(crc, terminator&0xff);

crc = -crc;

for(i=0; i<4; i++)

{

windex+=InsertEncodedByte((unsigned char)(crc&0xff),

&wirtebuf[windex]);

crc>>=8;

}

WriteBuffer(writebuf, windex);

}

/* 기종에 따라 구분을 준다. */

#ifdef _MOTO_

#define F_NUM 2

#define S_NUM 3

#else

#define F_NUM 1

#define S_NUM 0

#endif

/******************************************

Function : KStreamSendFile

Kstream Protocol로 파일을 보낸다.

Returns : 성공하면 1, 실패하면 0

******************************************/

/* KStream main procedure */

short KStreamSendFile(char *name, short startx,

short starty, short resolution)

{

short i;

FILE *handle;

long bytes_to_read;

short byte_count;

long oldpos;

short ReadFlag;

char edata;

char *ptr;

unsigned char terminator.

short suspended;

short key;

short force_w_type;

short pos[2];

short ack_mode;

char filename[256];

short xo, yo, res;

short encodenum;

char data;

short hnumber;

short ackskip;

char *pStart;

char buf[5];

int sendblock;

int maxskip;

struct stat statbuf;

int len;

char octalstr[14];

maxskip=2;

ackskip=0;

for(i=0; i<256; i++)

encodeflag[i]=0;

/*

PAD나 ROUTLER 등의 장비와 충돌이 나는 글자는

아래와 같이 encodeflag을 1로 Setting 한다.

TELNET에서 쓸 때 ASCII 225를 Encoding 해야 한다.

/*

encodeflag[CR]=1;

encodeflag[LF]=1;

*/

/****

ack_mode를 1로 Setting 하면, 속도는 조금 떨어지나

안정된 전송이 가능함. 일반적으로는 0으로 Setting 해도

무방함. 이것을 사용자가 선택할 수 있도록 하는 것이

좋을 것 같음.

HALF DUPLEX 망에서는 반드시 ack_mode를 1로 해야 한다.

****/

ack_mode=forceackmode;

strcpy(filename, name);

/*

ptr=(char*)rindex(filename);

if(!ptr) strcpy(filename, name);

else strcpy(filename, ++ptr);

*/

/*** 보내는 Data 종류 검색 ***/

strupr(filename);

ptr=strchr(filename, '.');

if(ptr==NULL)

return(0);

for(i=0; i<3; i++)

if(ptr[i+1])

buf[i]=ptr[i+1];

else

buf[i]=' ';

buf[i]='*';

/* 시작 그래픽 좌표를 이곳에서 지정함 ..원래 Host에서 지정 */

xo=startx;

yo=starty;

res=resolution;

if((handle=fopen(name, "rb"))==NULL)

return(0);

file_length=filelength(handle);

if(file_lenght<=0L) /* FileSize Error */

{

fclose(handle);

return(0);

}

ksrproto.file_length=file_length;

ksrproto.byte_count =0L;

/* 시작하기 전에 NUL을 몇 개 보낸다. */

WaitSendChar(NUL);

WaitSendChar(NUL);

WaitSendChar(ESC);

WaitSendChar('%');

WaitSendChar('K');

for(i=0; i<4; i++)

WaitSendChar(buf[i]);

#ifdef WAIT_START

_WaitReadChar(2000);

#endif

ksrproto.escape_control=1;

ksrproto.escape_nul=1;

ksrproto.cancel_count=0;

ksrproto.buffer=databuf;

ksrproto.mybufsize=1024;

ksrproto.hisbufsize=1024;

ksrproto.buffer=datanbuf;

ReadPacket(&ksrproto);

if(ksrproto.packet_type!=KOPTINIT)

{

ksrproto.curblocksize=0;

ksrproto.buffer=databuf;

ksrproto.packet_type=KFAIL;

ksrproto.head.packet_offset=0L;

SendPacket(&ksrproto, 0, 'w');

goto End;

}

while(TRUE)

{

ksrproto.packet_type=KSINIT;

/*******************

Staring Position

*******************/

/* Graphic unit (Pixel) Left : xo, X position */

databuf[0]=xo % 256;

databuf[1]=xo / 256;

/* Top : yo, Y position*/

databuf[2]=yo % 256;

databuf[3]=yo / 256;

databuf[4]=res % 256;

databuf[5]=res / 256;

encodenum=0;

for(i=0; i<256; i++)

{

if(encodeflag[i])

{

sprintf((char *)(&databuf[encodenum*2+6]), "%X", i);

encodenum++;

}

}

ksrproto.curblocksize=6+encodenum*2;

ksrproto.buffer=databuf;

ksrproto.byte_count=0L;

ksrproto.head.packet_offset=0L;

ksrproto.head.headbyte[F_NUM]= ksrproto.mybufsize/512+'ⓐ';

if(forceescapemode)

ksrproto.head.headbyte[S_NUM]=0x40; /*!!!!!!!!!!!!*/

else

ksrproto.head.headbyte[S_NUM]=0;

if(forcenulmode)

ksrproto.head.headbyte(S_NUM]=0x20;

SendPacket(&ksrproto, 0, 'w');

ksrproto.buffer=databuf;

ReadPacket(&ksrproto);

if(ksrproto.packet_type==KRINIT)

{

if(ksrproto.head_headbyte[S_NUM] & 0x40)

/* 속도가 느려지면 escape_control을 0으로 바꾸세요 */

ksrproto.escape_control=1;

else

ksrproto.escape_control=forceescapemode;

if(ksrproto.head.headbyte[S_NUM]&0x20)

ksrproto.escape_nul=1;

else

ksrproto.escape_nul=forcenulmode;

/* Internet Always escape 7f ff*/

if(ksrproto.head.headbyte[F_NUM])

{

i=(ksrproto.head.headbyte[F_NUM]-'ⓐ');

ksrproto.hisbufsize=i*512;

if(ksrproto.hisbufsize>8192) /* Maximum 8192 */

ksrproto.hisbufsize=1024;

else

if(ksrproto.hisbufsize<=0)

ksrproto.hisbufsize=32;

}

else

ksrproto.hisbufsize=1024;

if(ksrproto.curblocksize>4)

encodenum=(ksrproto.curblocksize-4)/2;

else

encodenum=0;

for(i=0; i<encodenum; i++)

{

data=ksrproto.buffer[4+i*2];

if(data>='0' && data<='9')

hnumber=[data-'0')*16;

else

hnumber=[data-'A')*16;

data=ksrproto.buffer[4+i*2+1];

if(data>='0' && data<='9',)

hnumber+=[data-'0');

else

hnumber+=(data-'A');

encodeflag[hnumber]=1;

}

break;

}

if(ksrproto.packet_type==KSINIT)

/*** 상대방 프로토콜이 아직 동작하지 않을 ***/

{

/* 아래의 루틴은 필요 하지 않음.

간혹, Delay를 주어야 하는 시스템에서만 사용함

일정시간 만큼 Delay 해 줌. Delay 안해도 상관없음

*/

/*

#ifdef KSR_UNIX

sleep(1);

#else

delay(500);

#endif

*/

}

else

if(ksrproto.packet_type==USER_CANCEL)

/* User Cancel (5 CAN) */

{

goto End;

}

/* Otherwise Send KSINIT again */

/* TIMEOUT or CRC_ERROR or Unknown Packet */

}

NextFile:

while(TRUE)

{

/* filename + NUL character */

fstat(fileno(handle), &statbuf);

Long2Octal(statbuf.st_mtime, octalstr);

len=strlen(filename);

len+=strlen(octalstr);

len+=2;

sprintf((char *)databuf, "%s%c%s%c", filename, 0, octalstr, 0);

ksrpoto.curblocksize=len; /* strlen((char *)databuf)+1; */

ksrpoto.packet_type=KFILEINFO;

ksrpoto.byte_count=0L;

ksrpoto.head.packet_offset=ksrproto.file_length;

ksrpoto.buffer=databuf;

SendPacket(&ksrpoto, 0, 'w')

ksrpoto.buffer=databuf;

ReadPacket(&ksrproto);

if(ksrproto.packet_type==KBLOCKNEED)

{

ksrpoto.blockstartoffset=ksrproto.head.packet_offset;

ksrpoto.byte_count=ksrproto.head.packet_offset;

pStart=&ksrproto.buffer[0];

ksrproto.blockendoffset=0L;

while((*pStart!=' ') && *pStart)

{

ksrproto.blockendoffset*=10L;

ksrproto.blockendoffset+=((*pStart)-'0');

pStart++;

}

ksrproto.blockendoffset+=ksrproto.blockstartoffset;

pStart++;

switch(*pStart)

{

case '1':

case '2':

case '3':

case '4':

ack_mode=1;

maxskip=(*pStart)-'0';

break;

case '9':

ack_mode=0;

break;

}

/* Other Original Set */

if(ksrproto.blockendoffset==0L)

ksrproto.blockendoffset=ksrproto.file_length;

break;

}

if(ksrproto.packet_type==USER_CANCEL)

goto End;

}

terminator='w';

suspended=0;

force_w_type=0;

if(ksrproto.byte_count<ksrproto.blockendoffset)

/* 요구블록이 정상적이면 */

while(TRUE)

{

if(ksrproto.byte_count<ksrproto.blockendoffset)

{

bytes_to_read=ksrproto.blockendoffest-

ksrproto.byte_count;

if(bytes_to_read>((long)ksrproto.hisbufsize))

/** Error가 있으면 bufsize보다 작게 한다. **/

{

if(force_w_type)

terminator='w';

else

{

if(ack_mode)

terminator='q';

else

terminator='g';

}

byte__count=ksproto.hisbufsize;

}

else

{

terminator='w';

byte_count=(int)bytes_to_read;

}

ksrproto.buffer=databuf;

ksrproto.curblocksize=byte_count;

}

else

ksrproto.curblocksize=0;

force_w_type=0;

if(ksrproto.curblocksize)

{

ksrproto.packet_type=KBLOCKDATA;

ksrproto.head.packet_offset=ksrproto.byte_count;

ksrproto.buffer=databuf;

oldpos=ksrproto.byte_count;

/** 아래함수에서 읽어서 보낸다. **/

SendDataPacket(&ksrproto, suspended, terminator,

handle);

ksrproto.byte_count += ksrproto.curblocksize;

if(terminator!='w')

suspended=1;

else

suspended=0;

sendblock=;

}

else

{

sendblock=0;

terminator='w';

}

ReadFlag=0;

if(RxBufferReady())

{

if(terminator!='g' && sendblock)

ackskip++;

ReadFlag+1;

}

else

if(terminator=='q')

{

ackskip++;

if(ackskip>=maxskip) /*** We Must Read Ack ***/

{

ReadFlag=1;

}

}

else

if(terminator!='g')

{

if(sendblock)

ackskip++;

/*

ackskip=0;

*/

ReadFlag=1;

}

if(ReadFlag)

{

ReadAgain:

ReadPacket(&ksrproto);

switch(ksrproto.packet_type)

{

case KRPOS

ackskip=0;

ksrproto.byte_count= ksrproto.head.packet_offset;

suspended=0;

ClearTxBuffer();

if(ack_mode==0)

ack_mode=1;

else

if(ksrproto.hisbufsize>512)

{

ksrproto.hisbufsize/=2;

}

*/

break;

case KACK;

/****

if(ksrproto.byte_count != ksrproto.head.packet_offset)

printf("Error Packet\n");

*****/

if(ackskip>0)

ackskip--;

break;

case USER_CANCEL:

goto End;

case CRC_ERRPR:

ksrproto.byto_count=oldpos;

suspended=0;

break;

case TIMEOUT

goto ReadAgain;

break;

case KFLOW :

/* After now Wait for ACK */

force_w_type=1;

break;

case KBLOCKNEED :

ackskip=0;

ksrproto.blockstartoffset=

ksrproto.head.packet_offset;

ksrproto.byte_count= ksrproto.head.packet_offset;

pStart=& ksrproto.buffer[0];

ksrproto.blockendoffset=0L;

while((*pStart!=' ') && *pStart)

{

ksrproto.blockendoffset*=10L;

ksrproto.blockendoffset+=((pStart)-'0');

pStart++;

}

ksrproto.blockendoffset+=

ksrproto.blockstartoffset;

pStart++;

switch(*pStart)

{

case '1':

case '2':

case '3':

case '4':

ack_mode=1;

maxskip=(*pStart)-'0';

break;

case '9':

ack_mode=0;

break;

}

if(ksrproto.blockendoffset==0L)

ksrproto.blockendoffset=ksrproto.file_length;

suspended=0;

break;

case KBLOCKEND :

suspended=0;

break;

}

if(ksrproto.packet_type==KBLOCKEND)

break;

}

}

ksrproto.packet_type=KFILEEND;

ksrproto.head.packet_offset=ksrproto.file_length;

ksrproto.buffer=databuf;

ksrproto.curblocksize=0;

SendPacket(&ksrproto, 0, 'w');

End

fclose(handle);

return(1);

}

/******************************************

Function : main

Kstream을 부르는 Main부분

Sending 부분의 프로토콜

Returns : 없음

******************************************/

main(int argc, char *argv[])

{

int x, y, r;

int fnloc;

int i;

if(argc<2) {

printf("Usege: %s [option] <upload file name> [x loc] [y loc] \n",

argv[0]);

printf(" -e : Escape Control Characters\n");

printf(" -n : Escape NUL 7F FF\n");

printf(" -f : Force File Transfer Mode\n");

-a : Only Use Ack Packets\n");

exit(1);

}

else

{

x=0;

y=0;

forceescapemode=0;

forcenulmode=0; /*Internet에서는 이것을 1로 해야 된다. */

forcefilemode=0;

forceackmode=0; /*반이중 통신에서는 이것을 1로 해야 한다.

RxBufferReady 함수가 정상적으로 동작하지

않을 경우에도 1로 한다. */

fnloc=-1;

for(i=1; i<argc; i++)

{

x=0;

y=0;

if(argv[i][0]!='-')

{

fnloc=i;

break;

}

else

{

switch(argv[i][1])

{

case 'f':

forcefilemode=1;

break;

case 'e':

forceescapemode=1;

break;

case 'n':

forcenulmode=1;

break;

case 'a':

forceackmode=1;

break;

}

}

}

}

if(fnloc==-1)

{

printf("Need File Name\n");

exit(2);

}

if(argc>fnloc+1);

{

x=atoi(argv[fnloc+2]);

}

if(argc>fnloc+2)

{

y=atoi(argv[fnloc+2]);

}

if(argc>fnloc+3)

{

r=atoi(argv[fnloc+3]);

}

else

r=100;

/*

while(argc>fnloc)

{

*/

KStream_Send(argv[fnloc], x, y, r);

/*

fnloc++;

if(argc>fnloc)

sleep(2);

}

*/

/* printf("End KStream\n"); */

exit(0);

}

/******************************************/* */

/* 아래부터는 */

/* 기계와 관련된 부분이므로 기종에 따라 바꾸어야 함 */

/* Machine dependent part */

/* */

/*****************************************/

/* File handle 0 means stdin File handle 1 means */

struct termio saveterm;

KStream_Send(fname, x, y, r)

char *fname;

int x, y, r;

{

int i;

int ch;

TerminalOpen(O_RDWR);

/* Int RxBuffer 1 byte test Buffer */

cachedch=-1;

windex=0;

KStreamSendFile(fname, x, y, r);

TerminalClose();

}

/******************************************

Function : strupr

string을 Upper character로 바꾸어 준다.

Returns : Upper character된 String 값

******************************************/

/* 특정한 Machine에서는 없는 경우도 있으므로 지원함 */

char *strupr(char *tmpbuf)

{

int i;

for(i=0; tmpbuf[i]; i++)

{

if(tmpbuf[i]>='a' && tmpbuf[i]<='z')

{

tmpbuf[i]=tmpbuf[i]-'a'+'A';

}

}

return tmpbuf;

}

/*

strstr(orig, comp)

char *orig, *comp;

{

int olen, clen, i, end;

olen=strlen(orig);

clen=strlen(comp);

end=olen-clen;

for(i=0; i<=end; i++) {

if(!strncmp(orig+i, comp, clen)) return i;

}

return 0;

*/

/******************************************

Function : filelength

파일의 길이를 알아내는 함수

Returns : 파일의 크기

******************************************/

long filelength(FILE *fp)

{

#ifdef _STAT_USE_

struct stat file_status;

if(stat(path, &file_status) <0)

return -1L;

return file_status.st_size; /* long */

#else

long cur, loc;

cur=ftell(fp);

fseek(fp, 0L, SEEK_END);

loc=ftell(fp);

fseek(fp, cur, SEEK_SET);

return(loc);

#endif

}

/******************************************

Function : ClearTxBuffer

보낼 Buffer를 Clear : 현재는 사용하지 않고 있음

Returns : 없음

******************************************/

void ClearTxBuffer()

{

/* fflush(1); */

}

/******************************************

Function : GetAsync

글자를 한자 읽어 오는 함수

Returns : 읽은 글자 값

******************************************/

short GetAsync()

{

unsigned char ch;

/* -l is RX_BUFFER EMPTY */

if(cachedch>=0)

{

ch=cachedch;

cachedch=-1;

return ch;

}

if(read(0, &ch, 1)<1)

return ((short)-1); /* Buffer Empty */

return((short)ch);

}

/******************************************

Function : AsyncSend

글자를 한자 보내는 함수

Returns : 없음

******************************************/

void AsyncSend(unsigned char ch)

{

int writecount;

while(1)

{

writecount=write(1, &ch, 1);

if(writecount)

break;

}

}

/******************************************

Function : WriteBuffer

버퍼 위로 한 블록을 보내는 함수

Returns : 없음

******************************************/

/*

한 바이트씩 써도 되기는 하지만 시스템의 로드를

줄이기 위해 버퍼를 한꺼번에 write 한다.

버퍼는 위쪽에 2500byte 잡혀있다.

*/

void WriteBuffer(char *buf, short count)

{

write(1, buf, count);

}

/******************************************

Function : TxBufferFull

보내는 버퍼가 Full인지 검사하는 함수

Returns : Full이면 1, Full이 아니면 0

******************************************/

short TxBufferFull()

{

int Stream;

return 0;

/*

ioctl(1, TIOCMGET, &Stream);

if(Stream&TIOCM_DTR)

return 0;

else

{

printf("mode %x\r\n", Stream);

return 1;

}

*/

}

/******************************************

Function : RxBufferReady

받은 버퍼가 있는지 검사하는 함수

Returns : 값이 있으면 1, 없으면 0

******************************************/

/*

한 바이트를 읽어본 후 Data가 읽혀지면 1 돌려주고

그 Data를 cachedch에 저장하고 GetAsync에서 돌려준다.

*/

short RxBufferReady()

{

short Stream;

unsigned char ch;

long rbytes;

int state;

if(cachedch>=0)

return 1;

#ifdef IOCTL_SUPPORT

ioctl(0, FIONREAD, &rbytes);

if(rbytes>0L)

{

if(read(0, &ch, 1)<1)

{

cachedch=-1;

return (0);

}

else

{

cachedch=ch;

return (1);

}

}

else

{

cachedch=-1;

return(0);

}

#else

state=fcntl(0, F_GETFL);

fcntl(0, F_SETFL, state : O_NDELAY);

if(read(0, &ch, 1)<1)

{

cachedch=-1;

fcntl(0, F_SETFL, state);

return (0);

}

else

{

cachedch=ch;

fcntl(0, F_SETFL, state);

return (1);

}

#endif

}

/******************************************

Function : TerminalOpen

Terminal을 통신하기 위해 열어둔다.

Returns : 성공하면 0, 실패하면 -1

******************************************/

int Terminal Open(int fmode)

{

struct termio tstate;

if(ioctl(0, TCGETA, &tstate)<0) return(-1);

saveterm=tstate;

tstate.c_iflag &=∼(IGNBRK : BRKINT : IGNPAR : INPCK : ISTRIP

: INLCR : IGNCR : ICRNL : IUCLC : IXON

: IXANY : IXOFF : IMAXBEL);

tstate.c_ofag &=∼(OPOST);

#ifdef EXIST_RTSCTS

tstate.c_cflag &=∼(CSIZE : CSTOPB : PARENB : HUPCL : CRTSCTS);

#else

tstate.c_cflag &=∼(CSIZE : CSTOPB : PARENB : HUPCL);

#endif

tstate.c_lflag &=∼(ISIG : ICANON : ECHO : NOFLSH : TOSTOP:

FLUSHO : PENDIN);

/*

아래의 변수가 시간을 조정한다. 0,0 또는 0.1

0,0은 너무 로드가 많이 걸린다. read 함수에서의 TIMEOUT 값

*/

tstate.c_cc[VMIN]=0;

tstate.c_cc[VTIME]=1;

tstate.c_cflag := CS8 : CREAD : CLOCAL : 0;

ioctl(0, TCSETA, &tstate);

return 0;

}

/******************************************

Function : TerminalClose

열었던 터미널을 닫는다.

Returns : 없음

******************************************/

void TerminalClose()

{

ioctl(0, TCSETA, &saveterm);

}

/******************************************

Function : ConvDosShort

기종이 다른 이 기종간의 메모리 구조를 바꾸어 준다.

Returns : 변환된 값

******************************************/

/* #define _MOTO_ */

short ConvDosShort(short input)

{

#ifdef _MOTO_

union {

char byte[2];

unsigned short num;

} in, out;

in.num=input;

out.byte[0]=in.byte[1];

out.byte[1]=in.byte[0];

return out.num;

#else /* INTEL */

return input;

#endif

}

원시 Code 끝

참고문헌

부 록

실시간 멀티미디어 통신프로토콜 원시소스를 Unix에 포팅하기 위해서 사용합니다.

(출처: 한국전산원 NCA IV-GE-9326)

부 록 1. vi 에디터 사용법

vi 에디터를 사용하려면 UNIX 셀 프롬프트에서 다음과 같은 명령어를 사용한다.

% vi my-man.man

여기서 my-man.man은 새로 작성하고자 하는 파일이름이나 또는 이미 존재하는 파일 이름이다.

전자게시판이나 전자우편 시스템에서 메시지를 작성할 때 vi editor를 선택하면 바로 다음 그림과 같은 화면이 나타난다.




상태에서 키보드를 누르면 문자가

화면에 나타나지 않는다. 이 상태를

명령어모드라 하며 입력되는 모든 문

자를 명령어로 해석한다.


○ 명령어 mode에서 i나 a 등을 치면 입력 모드로 된다. 입력 모드에서 입력한 모든 문자는 문서에 기록되는 자료로서 인식되며 화면상에 나타난다.

○ 문안(text)의 편집

문서를 작성하는 도중에 이미 작성한 부분을 수정해야 할 경우에는 명령어모드에서 편집명령어를 이용하여 수정할 수 있다. 문서를 작성하는 입력 모드에서 명령어 모드로 빠져나가기 위해서는 Esc키를 누른다.

편집명령어는 다음과 같다.

i-삽입, 현재 커서의 앞에 문자를 삽입할 때 사용한다.

a-부가, 현재 커서의 다음 위치부터 문자를 삽입할 때 사용한다.

o-현재 커서가 위치한 줄의 다음 줄에 한 줄을 삽입할 때 사용한다.

0-현재 커서가 위치한 줄의 앞줄에 한 줄을 삽입할 때 사용한다.

x-삭제, 현재 커서가 위치한 문자를 삭제한다.

dw-단어삭제, 현재 커서가 위치한 오른쪽의 단어 하나를 삭제한다.

D-줄 삭제, 커서의 위치부터 그 줄의 끝까지 삭제한다.

dd-줄 삭제, 커서가 위치한 줄을 모두 삭제한다.

ndd-n줄 삭제, n개의 줄을 삭제한다.

(예) 10dd : 현재 커서 위치부터 다음 10줄을 삭제한다.

u-복구, 이전에 내린 명령을 취소한다.

(예) 10dd 한 다음 u를 누르면 삭제되었던 10줄이 삭제되기 이전상태로 복구 된다.

○문안의 치환

r-한문자 치환, 한 문자를 다른 문자로 바꾸고 싶을 때 사용한다.

(예) replace -> Esc -> rp -> replace

R-여러 문자 치환, 여러 문자를 다른 문자들로 바꾸고 싶을 때 사용한다. R 다음에 Esc를 누르기 전까지 입력한 모든 문자가 치환된다.

(예) reopqce -> Esc -> Rpla -> Esc ->replace

cw-단어 치환, 커서가 위치한 곳의 단어를 다른 단어로 바꾸고 싶을 때 사용한다.

(예) input -> esc -> cw -> output -> output

○ Cursor를 옮기는 방법

k

h →

커서

← l

j

vi에서 커서를 옮길 때는 화살표가 대신에 h, j, k, l을 사용한다.

h는 커서를 한 칸식 왼쪽으로 옮길 때, l은 오른쪽으로 옮길 때 사용하며 k는 커서를 한 줄 위로 옮길 때, j는 한 줄 아래로 옮길 때 사용한다.

○ 화면을 옮기는 방법

'f'-화면을 한 페이지 앞으로 옮김

'b'-화면을 한 페이지 뒤로 옮김

'd'-화면을 반 페이지 앞으로 옮김

'u'-화면을 반 페이지 뒤로 옮김

※ ^는 키보드의를 나타내며 ^f는 Ctrl과 f를 동시에 누른다는 뜻임.

○ vi를 빠져 나가는 방법

문서를 다 작성한 다음 vi를 종료할 때는 먼저 Esc키를 눌러 명령어 모드로 빠져 나간 다음 다음과 같은 명령을 사용한다.

:q!-파일의 내용을 저장하지 않고 빠져나간다.

:w-vi를 빠져나가지 않으면서 작성한 내용을 파일에 저장

:wq-파일에 저장하고 vi를 빠져나간다.

ZZ- :wq의 명령과 같다.

이것은 vi 사용설명서입니다.

이제 vi를 종료하겠습니다.

~

~

~

~

~

~

:wq

my_man.man" [new file] 43 lines, 244 characters

<3h:apdev/shkim>


부록 2. Remote Login

가. telnet

telnet은 TELNET 프로토콜을 사용하여 다른 호스트와 통신을 하기 위한 사용자 인터페이스이다. 만약 호스트가 지정되지 않으면 대기표시 "telnet>"과 함께 명령어 대기 모드로 들어간다. 그러나 호스트를 지정했을 경우는 telnet은 자동적으로 open 명령어를 수행한다. 일단 open 명령어가 수행되고 나면 telnet은 입력모드로 들어가게 되는데, 이 모드에서는 이제부터의 모든 입력이 상대 호스트에 전달된다. 또한, telnet은 원격 로그인이 실행되는 중간에 아래 정의된 telnet 명령어를 수행할 수 있는 기능을 제공한다. 아래에는 자신의 시스템에 로그인한 후 한국전산원의 musso 시스템으로 remote login한 예다.

# telnet

telnet> open 128.134.105.4

Typing..

Connected to 128.134.105.4

Escape character is]'.

UNIX SVR 3.2 (musso)

login : bbs

Password

Last login Thu Sep 17 10:13:33 on ttylal to henl

musso#

····etc

hen3# exit

Thu Sep 17 13:43:23 KST 1992

System usage: 0.0u 0.3s 0:04 8% 8+4k 23+4io 4pf+0w

Connection closed by foreign host.

#

←telnet 명령 입력

←open 및

Internet address 입력


←musso login message

←muses에 login 수행

←musso HOST 상태로

작업가능

←작업종료후 logout


←원래 시스템으로 복귀


도움말은 명령어 대기모드에서 help 또는 ?를 입력한다.


hen2% telnet

telnet>help

Commands may be abbreviated. Commands are

open host [port]

지정된 host의 port와의 연결을 설정한다. 여기서 port가

주어지지 않은 경우는 TELNET 서버의 정해진 포트에 연결을

시도하게 된다. host는 호스트의 이름이 될 수도 있고, 그

호스트의 Internet 어드레스가 될 수가 있다.

close 현재 설정된 연결을 해제한다.

quit 현재 설정된 연결을 헤제하고 telnet 프로그램을 끝낸다.

status 현재 telnet의 상태를 보여준다.

display [argument]

set. toggle의 값을 출력한다.

? [command] 명령어에 대한 도움말을 얻는다. 다만, command가 지정되지

않은 경우는 모든 명령어를 나열한다.

set argument value

telnet 변수의 값을 주어진 값으로 변경한다. 특별한 값

"off"는 변수와 관련된 기능을 해제한다. 각 변수의 값은

display 명령어로 확인할 수 있다. 설정 가능한 변수는

다음과 같다. escape telnet 명령어 상태(원격 시스템과

연결된 경우)로 변경하는 telnet 이탈문자(초기값은 "^]")

toggle arguments

telnet이 여러 가지 상황에 응답하는 방법을 제어하는 여러

플래그의 값을 변경한다. (TRUE와 FALSE 중 택일) 각 플래그의

상태는 display 명령어로 확인할 수 있다. 사용 가능한 인수

다음과 같다.

crmod crmod 선택여부를 변경한다. crmod가 선택된 경우, 상대

호스트로부터의 캐리지 리턴(CR)에 라인피드를 한다(CR+LF).

아무런 동작도 취하지 않는다. 이 모드는 원격 호스트가 CR만 전

송하는 경우 이외는 유용하지 않다. 초기값은 FALSE 이다.

debug socket 수준의 디버깅을 가능하게 한다(수퍼유저인 경우만 유

용하다). 초기값은 FALSE 이다.

options TELNET 선택 사양과 관계 있는 초기의 telnet 프로토콜

처리과정을 보여준다. 초기값은 FALSE 이다.

? 사용 가능한 toggle 명령어를 보여 준다.


나. rlogin

rlogin 명령어는 현재 로컬 호스트에 연결되어 있는 터미널을 원격지 호스트에 연결하여 준다. 각 호스트는 계정명을 공유할 수 있는 호스트의 목록을 포하하는 /etc/hosts.equiv 파일을 가진다. 동일한 시스템을 동일한 사용자로 rlogin 할 경우, password가 불필요하다. 각 사용자가 자신의 login directory에 개인적인 목록을 포함하는 .rhosts화일을 가질 수 있다. 파일의 각 행에 원격 호스트와 공백으로 구분된 사용자 이름으로 구성된다. rlogin을 요청하는 사용자와 원격 사용자가 일치하지 않으면, login과 같이 login과 password를 요구한다. 보안상의 문제로 .rhosts파일의 소유자는 원격 사용자이거나 root이어야 한다. 원격 터미널은 로칼 터미널과 같은 형이다.(환경변수 TERM 에 주어진 형태) 모든 echo는 원격 사이트에서 발생되며, rlogin은 지연을 제외하고는 인식하지 못한다. ^S와 ^Q에 의한 흐림제어와 인터럽트에 의한 입출력의 flushing은 적절히 처리된다.

# rlogin

usage: rlogin host [-ex] [-1 username] [-8]

[-L]

# rlogin musso

login: jikim

Password

Last login:Thu Sep 17 13:17:02 on tty2a7 to hen2

mosso#

····

mosso# exit

Thu Sip 17 13:42:53 KST 1992

System usage 0.3u 0.6s 0:11 8% 10+5k 58+6io 6pf+0

Connection closed.

#


←rlogin 의 사용예

←rlogin 및 HOST 명입력

←원격시스템에 동일

login name이 있으면

login 과정 불필요

←musso HOST 상태로

작업가능

←작업종료후 logout


← 원래 HOST로 복귀