네트워크어플리케이션의제작 (Creating Network Application) 37 장에서는소켓을이용한어플리케이션제작방법에대해서알아보았다. 이번장에서는 델파이 4 에서제공되는 NetMaster 의컴포넌트들을소개하고, 이를이용하여작성할수 있는네트워크어플리케이션을소개한다. NetMaster 컴포넌트 델파이의인터넷컴포넌트는 2.01 버전의 NetManage 컴포넌트에서시작되었다. 이컴포넌트들은 TCP/IP, SMTP, POP3, Telnet, FTP 등의인터넷서비스를모두지원했지만그다지높은호응을얻지는못했는데, 그이유는델파이의 native VCL 컴포넌트가아니라액티브 X 컴포넌트였기때문이다. 즉, 액티브 X 컨트롤을같이배포하고이를등록하지않으면사용할수없는단점이있었다. 그렇기때문에많은개발자들이프리웨어로배포된 Francois Piette 등의인터넷컴포넌트를사용하였다. 델파이 4 에서는이러한문제점을시정하여완전한 native VCL 컴포넌트로변신한 NetMaster 컴포넌트들을만나볼수있다. NetMaster 컴포넌트에는단순한인터넷표준프로토콜을지원하는이외에나름대로커스터마이징이가능한원시컴포넌트를다수지원하고있기때문에수월하게네트워크프로그래밍을할수있다. 그렇지만, 아쉬운점이있다면가장흔히사용되는 HTML 컴포넌트는여전히액티브 X 컨트롤을사용한다는점이다. 그래도델파이 3 에서의 HTML 컴포넌트가버전 1.0 만지원하여무척부족했지만, 델파이 4 에서는버전 2.x 를지원하기때문에대부분의 HTML 페이지를표시할수있다. 그러면 NetMaster 컴포넌트들의종류와이들에대해개략적으로알아보도록하자. TNMFTP 컴포넌트 TNMFTP 컴포넌트는 FTP 프로토콜을이용하여 FTP 서버로파일을전송하거나, 파일을다운로드받기위해서사용된다. 이렇게 TNMFTP 컴포넌트를이용하여파일을전송하기위해서는원격지호스트와의접속이선행되어야한다. 이를위해서는먼저유효한 FTP 서버의 IP 주소를 Host 프로퍼티에설정하고, Port 프로퍼티를정확하게설정해야한다. 그리고나서, 서버에서허용하는계정의사용자 ID 와패스워드를 UserID, Password 프로퍼티에설정한다. 보통대부분의 FTP 서버는사용자 ID 로 Anonymous 와패스워드로 E-mail 주소를사용할수있도록되어있다. 이런기본적인프로퍼티설정이끝나면 Connect 메소드를호출하여서버에접속한다.
이제서버의디렉토리리스트를얻어올차례이다. 이를위해서는 List 메소드를호출하고, OnListItem 이벤트핸들러에서넘어오는디렉토리리스트의아이템을처리해주어야한다. 디렉토리를변경하기위해서는 ChangeDir 메소드를사용한다. 원격지호스트에파일을업로드하기위해서는업로드를하기위한디렉토리에먼저위치한뒤에 Upload 메소드를호출하면된다. 이메소드에로컬컴퓨터의파일의이름과원격지허스트에위치할파일이름을파라미터로지정할수있다. 파일을다운로드할때에는먼저 List 메소드를호출하여다운로드할파일을찾을수있어야한다. 그리고나서 Download 메소드를호출하는데, 여기에서파라미터로다운로드할파일이름과로컬드라이브에저장할파일의경로아이름을지정한다. 디렉토리를원격호스트에만들기위해서는 MakeDirectory, 디렉토리를지우기위해서는 RemoveDir 메소드를이용한다. TNMHTTP 컴포넌트 TNMHTTP 컴포넌트는인터넷을통해 HTTP 전송을수행한다. URL 의특성상 URL 에호스트와포트의내용을포함하기때문에 Host 와 Port 프로퍼티는설정할필요가없다. 문서를 Get 하고자할때에는단순히 Get 메소드를호출하면된다. 이때불러올문서의 URL 을지정하면되는데, 이렇게해서불러온데이터는 InputFileMode 프로퍼티의내용에따라다르게저장된다. 이값이 True 이면문서의 body 부분은 Body 프로퍼티에지정된파일에저장되고, header 부분은 Header 프로퍼티에지정된파일에저장된다. 반면에이값이 False 이면 Body 와 Header 프로퍼티에직접저장된다. 데이터를 Post 할때에도 Post 메소드에 URL 을첫번째파라미터로지정하고, 두번째파라미터인 PostData 는 OutputFileMode 프로퍼티의값에따라결정된다. OutputFileMode 프로퍼티의값이 True 이면 PostData 파라미터에는특정위치에전달될데이터가저장된파일이지정된다. 반면이값이 False 이면전달된데이터가직접 PostData 파라미터에지정된다. 이메소드에의해전달되어오는문서를저장하는방법은 InputFileMode 에의해결정되는데, 그내용은 Get 메소드와동일하다. TNMNNTP 컴포넌트 TNMNNTP 컴포넌트는인터넷뉴스를 NNTP 프로토콜에의해인터넷뉴스서버에서읽고, 메시지를전달하기위해사용된다. TNMNNTP 컴포넌트의핵심함수를이용할때에는먼저뉴스호스트에접속해야한다. 이를위해서 Host 프로퍼티에뉴스서버의주소를설정하고, Connect 메소드를이용하여서버에접속한다. 뉴스그룹의리스트를얻기위해서는 GetGroupList 메소드를이용한다. 그리고, 기사를읽어올뉴스그룹을선택해야하는데, SetGroup 메소드에뉴스그룹의이름을파라미터로넘
겨서호출하면된다. 인터넷뉴스기사를읽을때에는일단그룹을선택한뒤에 GetArticle 메소드를호출하면된다. 이렇게하면 Body 프로퍼티와 Header 프로퍼티의값을이용해서선택된기사를읽을수있게된다. 새로운뉴스기사를전송하기위해서는 PostHeader, PostBody 프로퍼티에메시지의헤더와내용을채우고, PostArticle 메소드를호출하면된다. TNMPOP3 컴포넌트 TNMPOP3 컴포넌트는인터넷 E-mail 을 POP3 서버로부터가져오는역할을한다. 이를위해서먼저 Host 프로퍼티를 E-mail 서버의주소로설정하고, UserID 프로퍼티와 Password 프로퍼티에사용자이름과패스워드를지정하고 Connect 메소드를호출하여서버에접속해야한다. 인터넷메일을가져오기위해서는 GetMailMessage 메소드를호출하면된다. 메일의 body 의파트는 MailMessage 프로퍼티에저장된다. TNMSMTP 컴포넌트 TNMSMTP 컴포넌트는 SMTP 프로토콜을이용하여인터넷메일서버에메일을전송하는역할을한다. 다른컴포넌트와마찬가지로 Host 와 Post 프로퍼티를설정하고. Connect 메소드를호출하여메일서버와접속하고, Disconnect 메소드를호출하여접속을중단한다. 메일을보내기위해서는 PostMessage 프로퍼티에전송할데이터를지정하고, SendMail 메소드를호출하면된다. 경우에따라서는사용자가연결된호스트에있는지찾아볼필요가있는데이럴때에는 Verify 메소드를이용한다. 그리고, 메일링리스트의멤버는 ExpandList 메소드를이용하여결정하는데, OnMailListReturn 이벤트핸들러를작성한다. TNMFinger TNMFinger 컴포넌트는인터넷 Finger 서버로부터사용자에대한정보를얻을때사용된다. 먼저 Finger 서버에접속해야하므로, Host 프로퍼티에서버의주소를지정하고 Port 프로퍼티는거의대부분 79 번을사용하므로변경할필요가없다. 사용자에대한정보는 FingerStr 프로퍼티에서얻을수있다. TNMUDP TNMUDP 컴포넌트는 UDP 프로토콜을이용하여데이터그램패킷을전송하는데사용한다. 이컴포넌트역시마찬가지로패킷을전송하기전에원격호스트와포트를알아서
RemoteHost 와 RemotePort 프로퍼티에값을대입해야한다. 실제로데이터를전송하기위해서는 SendBuffer 또는 SendStream 메소드를이용하는데 SendBuffer 메소드는원격호스트에문자의배열형태로데이터를전송하며, SendStream 메소드는데이터의스트림을전달한다. UDP 데이터를받을때에는 LocalPort 프로퍼티를반드시설정해야한다. 이프로퍼티는반드시디자인타임에서설정해야하며, 런타임에서변경할수없다. 읽어올 UDP 데이터가있으면 OnDataAvailable 이벤트가호출되며이이벤트에서 ReadBuffer 메소드를이용하여데이터를버퍼로읽어들이거나 ReadStream 메소드를이용하여스트림으로데이터를읽는다. TNMDayTime TNMDayTime 컴포넌트는인터넷 daytime 서버로부터 RFC 867 에정의된날짜와시간에대한정보를불러온다. 사용방법은 Host 프로퍼티에서버의주소를지정하고, 호스트에접속하기만하면 DayTimeStr 프로퍼티에지정된호스트의날짜와시간이전송되어저장되므로이를이용하기만하면된다. TNMTime TNMTime 컴포넌트는인터넷 time 서버에서 RFC 868 에정의된날짜와시간에대한정보를불러온다. 사용방법은 Host 프로퍼티에서버의주소를지정하고, 호스트에접속하기만하면 TimeStr 프로퍼티에지정된호스트의날짜와시간이전송되어저장되므로이를이용하기만하면된다. TNMEcho TNMEcho 컴포넌트는텍스트를인터넷에코서버에전송하고, 이내용을다시받을때사용되는데, RFC 862 에정의된프로토콜을사용한다. 이를이용해서네트워크의무결성과속도를측정한다. 서버와의접속을위해 Host 프로퍼티와 Port 프로퍼티를설정하는데, 보통에코서버는 7 번포트를이용한다. 텍스트를전송할때에는 Echo 메소드를이용한다. TNMUUProcessor TNMUUProcessor 컴포넌트는인터넷을통해전송되는파일을인코딩하고디코딩할때사 용된다. 사용방법은 InputFile 프로퍼티를처리할파일로설정하고, Method 프로퍼티에서
인코딩, 디코딩할방법을지정하고, OutputFile 프로퍼티에결과로생성될파일을지정한다. 그리고실제로실행을위해서 Encode, Decode 메소드를호출하면된다. 사용하는인코딩메소드로는 MIME/Base 64 를지원하는 uumime 과 UUEncoding/ Decoding 을지원하는 uucode 를사용할수있다. 이컴포넌트를사용하는예제를다음장에서볼수있을것이다. TNMURL TNMURL 컴포넌트는 HTTP 전송에대한문자열과 URL 포맷사이를인코드, 디코드할때사용된다. 사용방법은 InputString 프로퍼티에문자열이나 URL 포맷의값을설정하면 Encode 프로퍼티에 URL 포맷으로인코드된문자열을포함하게되고, Decode 프로퍼티에는디코딩된문자열이포함된다. 이때에러가발생하면 OnError 이벤트가호출된다. TNMMsg TNMMsg 컴포넌트는간단메시지를전송하기위해서사용하는데, TNMMsgServ 컴포넌트와함께사용하여야한다. 메시지를전송하기전에 Host 프로퍼티와 Port 프로퍼티에해당되는메시지서버의 IP 주소와포트를설정하고, FromName 프로퍼티에서메시지를전송한사람에대한정보를설정하고, PostIt 메소드를호출하면메시지가전송된다. TNMMsgServ TNMMsgServ 컴포넌트는 TNMMsg 컴포넌트에서전송된메시지를받는역할을한다. 어플리케이션을디자인할때 Port 프로퍼티에는메시지서버가사용할포트를지정하는데, 디폴트값을사용해도무방하다. 메시지서버에전송된메시지를받아서처리하기위해서는 OnMsg 이벤트핸들러를작성하면된다, TNMStrm TNMStrm 컴포넌트는스트림을스트림서버에전송하기위해서 TNMStrmServ 컴포넌트와함께사용된다. 먼저 Host 와 Port 프로퍼티를서버에맞도록설정하고, FromName 프로퍼티에스트림을전송하는사람의정보를지정하고, PostIt 메소드를이용하여스트림을전송하면된다. TNMStrmServ
TNMStrmServ 컴포넌트는 TNMStrm 컴포넌트에서전송하는스트림을처리하는역할을한다. TNMMsgServ 컴포넌트와마찬가지로 Port 프로퍼티를설정하고, OnMsg 이벤트에서전송된스트림을처리하면된다. TPowersock TPowersock 컴포넌트는 TCP 통신을이용하여다양한인터넷프로토콜을이용할수있도록제공하는기초클래스이다. 그러므로이컴포넌트는상속을받아서새로이구성해야하는것으로, Connect 메소드를호출하면 Host 와 Port 프로퍼티에지정한원격호스트의서비스에접속하게된다. 원격호스트에서받아온데이터는 Read, ReadLn, CaptureFile, CaptureStream, CaptureString 메소드를이용하여읽을수있다. 그리고, 데이터를원격호스트에전송할때에는 Write, WriteLn, SendFile, SendStream 메소드를이용한다. 그리고, Transaction 메소드는원격호스트에데이터를전송하고서버로부터응답을받는다. TNMGeneralServ TNMGeneralServer 컴포넌트는다중쓰레드를지원하는인터넷서버를개발할때 RFC 표준을지원하는서버나사용자정의서버를개발할때사용되는기초클래스이다. TNMGeneralServer 클래스를상속하는서버클래스를제작하기위해서는 Server 메소드를오버라이드해야하는데, 여기에서서버가클라이언트연결에어떻게반응할것인지를결정한다. 클라이언트와의상호작용은 TPowersock 의 read, write 메소드를이용한다. 주의할점은 Port 프로퍼티를디자인타임에서설정해야한다는것이다. 메일클라이언트어플리케이션의제작 그러면, 표준인터넷프로토콜을지원하는컴포넌트중에서 POP3 와 SMTP 프로토콜을이용한메일클라이언트를제작해보도록하자. 먼저폼을디자인해야하는데, 나름대로성의를가지고여러개의패널객체와 Align 프로퍼티를적절히설정해서깔끔한인터페이스를구성해보도록하자. POP3 와 SMTP 클라이언트를하나의폼으로구성하되, 메일을받는인터페이스와보내는인터페이스는 TPageControl 컴포넌트를이용하여다른페이지로구성한다. 메일을보낼때에는파일을 Attach 하여보낼수있도록 TOpenDialog 컴포넌트를추가한다. 그리고, 실제프로토콜을관리하는 TNMPOP3, TNMSMTP 컴포넌트를폼에추가하도록하자. 필자가구성한폼의디자인은다음과같다. 이와비슷하게필요한형태로폼을디자인하면될것이다.
접속 버튼을클릭하면 POP3 와 SMTP 라벨옆에있는에디트박스의내용을바탕으로서버에접속한다. POP3 의경우에는사용자 ID 와패스워드를이용하여메일서버에접속하는과정이필요하다. 이때패스워드는입력할때입력하는문자가보이지않거나, 특수문자로처리하는데대개의경우 * 문자를이용한다. 이럴때에는 TMaskEdit 컴포넌트를이용하는것이좋다. MaskEdit1 객체의 PasswordChar 프로퍼티를 * 로설정한다. 이버튼의 OnClick 이벤트핸들러를다음과같이작성하여, 메일서버에접속하도록한다. procedure TForm1.Button1Click(Sender: TObject); NMPOP31.AttachFilePath := '.'; NMPOP31.DeleteOnRead := FALSE; NMPOP31.ReportLevel := Status_Basic; NMPOP31.TimeOut := 20000; NMPOP31.Host := Edit1.Text; NMPOP31.UserID := Edit3.Text; NMPOP31.Password := MaskEdit1.Text; NMPOP31.Connect; NMSMTP1.TimeOut := 20000; NMSMTP1.ReportLevel := Status_Basic; NMSMTP1.Host := Edit2.Text;
NMSMTP1.UserID := Edit5.Text; NMSMTP1.Connect; 접속을하기위해서는 TNMPOP3, TNMSMTP1 컴포넌트모두 Connect 메소드를호출하면된다. 이때 Host 프로퍼티에메일서버에 IP 주소를대입하고, TimeOut 프로퍼티는접속을시도할시간을 msec 단위로설정한다. ReportLevel 프로퍼티는 OnStatus 이벤트에서상태메시지를어떻게표시할것인지를결정하는프로퍼티인데보통은 Status_Basic 으로설정한다. POP3 컴포넌트에서의 DeleteOnRead 프로퍼티는메시지를읽을경우에메일서버에서읽은메시지를제거할것인지여부를결정한다. 이값을 True 로할경우에는메시지를클라이언트로다운로드하면서버에서메시지가삭제되며, False 로설정할경우에는메일클라이언트에서읽더라도서버에메시지가그대로남게된다. 이경우에는메시지를직접삭제할수있도록해야한다. 마찬가지로 접속해제 버튼을클릭하면 Disconnect 메소드를호출하면된다. procedure TForm1.Button2Click(Sender: TObject); NMPOP31.Disconnect; NMSMTP1.Disconnect; POP3 페이지에서 List 버튼을클릭하면 POP3 메일서버에도착한메시지를나열하게되는데, 이렇게나열되는메시지는 OnList 이벤트핸들러에서처리할수있다. Clear 버튼을클릭하면메모컴포넌트의내용을지우도록한다. procedure TForm1.Button3Click(Sender: TObject); NMPOP31.List; procedure TForm1.Button4Click(Sender: TObject); Memo3.Lines.Clear; OnList 이벤트핸들러는다음과같이작성하여, 전달된메시지의번호와크기를메모컴포
넌트에나열하도록한다. procedure TForm1.NMPOP31List(Msg, Size: Integer); Memo3.Lines.Add(IntToStr(Msg) + ' / ' + IntToStr(Size)); 이렇게메모컴포넌트에나열된메일서버의메시지중에서읽어올메시지번호를에디트박스에지정하고, Get Message 버튼을클릭하면해당되는메시지를읽어와서 header 는우측의위쪽메모컴포넌트에, body 는아래쪽메모컴포넌트에표시한다. GetMessage 버튼의 OnClick 이벤트핸들러는다음과같이작성한다. procedure TForm1.Button5Click(Sender: TObject); NMPOP31.GetMailMessage(StrToInt(Edit4.Text)); Memo1.Lines.Assign(NMPOP31.MailMessage.Head); Memo2.Lines.Assign(NMPOP31.MailMessage.Body); if NMPOP31.MailMessage.Attachments.Text <> '' then ShowMessage('Attachments: ' + #10#13 + NMPOP31.MailMessage.Attachments.Text); 즉, 메시지를읽어올때에는 GetMailMessage 메소드에서파라미터에넘겨진번호의메시지를읽어오게된다. 이렇게읽어온메시지는 MailMessage 프로퍼티에저장되는데, 각각 header 와 body 부분으로나눌수있다. 이들은모두 TStrings 데이터형이기때문에메모컴포넌트에직접대입할수있다. MailMessage 의 Attachment 프로퍼티에는 attach 된파일을나타내는데, 파일의이름을 Text 프로퍼티를이용하여얻을수있다. 이것으로버튼의이벤트핸들러는모두작성하였다. 이제는 TNMPOP3 컴포넌트의이벤트핸들러를작성하도록하자. 대개의경우 TStatusBar 컴포넌트에메일이전달되는상황에대한정보를표시하는정도로작성한다. procedure TForm1.NMPOP31Connect(Sender: TObject); StatusBar1.SimpleText := 'POP3 Server Connected!'; procedure TForm1.NMPOP31Disconnect(Sender: TObject);
if StatusBar1 <> nil then StatusBar1.SimpleText := 'POP3 Server Disconnected!'; procedure TForm1.NMPOP31Status(Sender: TComponent; Status: String); if StatusBar1 <> nil then StatusBar1.SimpleText := 'POP3: ' + Status; procedure TForm1.NMPOP31ConnectionFailed(Sender: TObject); ShowMessage('POP3 Server Connection Failed'); procedure TForm1.NMPOP31Failure(Sender: TObject); ShowMessage(' 작업이실패했습니다.'); procedure TForm1.NMPOP31HostResolved(Sender: TComponent); StatusBar1.SimpleText := 'POP3 Host Resolved'; procedure TForm1.NMPOP31Success(Sender: TObject); StatusBar1.SimpleText := 'POP3 Operation Successful!'; procedure TForm1.NMPOP31PacketRecvd(Sender: TObject); StatusBar1.SimpleText := IntToStr(NMPOP31.BytesRecvd) + ' Bytes of ' + IntToStr(NMPOP31.BytesTotal) + ' Received';
procedure TForm1.NMPOP31RetrieveStart(Sender: TObject); Form1.Cursor := crhourglass; StatusBar1.SimpleText := 'Retrieval Start'; procedure TForm1.NMPOP31RetrieveEnd(Sender: TObject); Form1.Cursor := crdefault; StatusBar1.SimpleText := 'Retrieval End'; 이것으로 POP3 에대한부분은모두완성되었다. 이번에는 SMTP 클라이언트기능을추가할차례이다. 인터페이스디자인은다음과같다. SMTP 클라이언트에서는 Add, Remove 버튼을클릭하여 attach 될파일을파일열기대 화상자를이용하여리스트박스에추가하도록 Add, Remove 버튼의 OnClick 이벤트핸 들러를다음과같이작성한다. procedure TForm1.Button6Click(Sender: TObject);
If OpenDialog1.Execute then ListBox1.Items.Add(OpenDialog1.FileName); procedure TForm1.Button7Click(Sender: TObject); ListBox1.Items.Delete(ListBox1.ItemIndex); 이어서 Send 버튼을클릭하면이들과함께메모컴포넌트의내용을 To, CC, BCC 로지정 된주소에전송한다. 이때보내는사람에대한정보를인터넷메일주소와실제이름과함 께전송한다. Send 버튼의 OnClick 이벤트핸들러는다음과같이작성한다. procedure TForm1.Button8Click(Sender: TObject); NMSMTP1.PostMessage.FromAddress := Edit7.Text; NMSMTP1.PostMessage.FromName := Edit6.Text; NMSMTP1.PostMessage.Subject := Edit11.Text; NMSMTP1.PostMessage.ToAddress.Add(Edit8.Text); NMSMTP1.PostMessage.ToBlindCarbonCopy.Add(Edit10.Text); NMSMTP1.PostMessage.ToCarbonCopy.Add(Edit9.Text); NMSMTP1.PostMessage.Attachments.AddStrings(Listbox1.Items); NMSMTP1.PostMessage.Body.Assign(Memo4.Lines); NMSMTP1.SendMail; POP3 와마찬가지로 SMTP 컴포넌트에서도여러가지이벤트에대한상황을표시할수있 도록다음과같이이벤트핸들러들을작성한다. procedure TForm1.NMSMTP1Connect(Sender: TObject); StatusBar1.SimpleText := 'SMTP Server Connected!'; procedure TForm1.NMSMTP1Disconnect(Sender: TObject);
If StatusBar1 <> nil then StatusBar1.SimpleText := 'SMTP Server Disconnected'; procedure TForm1.NMSMTP1Status(Sender: TComponent; Status: String); if StatusBar1 <> nil then StatusBar1.SimpleText := 'SMTP: ' + Status; procedure TForm1.NMSMTP1EncodeStart(Filename: String); StatusBar1.SimpleText := 'Encoding ' + Filename; procedure TForm1.NMSMTP1EncodeEnd(Filename: String); StatusBar1.SimpleText := 'Finished Encoding ' + Filename; procedure TForm1.NMSMTP1ConnectionFailed(Sender: TObject); ShowMessage('SMTP Server Connection Failed'); procedure TForm1.NMSMTP1Failure(Sender: TObject); StatusBar1.SimpleText := 'SMTP Operation Failed'; procedure TForm1.NMSMTP1HostResolved(Sender: TComponent); StatusBar1.SimpleText := 'SMTP Host Resolved';
procedure TForm1.NMSMTP1PacketSent(Sender: TObject); StatusBar1.SimpleText := IntToStr(NMSMTP1.BytesSent) + ' Bytes of ' + IntToStr(NMSMTP1.BytesTotal) + ' Sent'; procedure TForm1.NMSMTP1RecipientNotFound(Recipient: String); ShowMessage('Recipient "' + Recipient + '" not found!'); procedure TForm1.NMSMTP1SendStart(Sender: TObject); StatusBar1.SimpleText := 'Sending Message...'; procedure TForm1.NMSMTP1Success(Sender: TObject); StatusBar1.SimpleText := 'Operation Successful!'; 이것으로그럴듯한인터넷메일클라이언트가작성되었다. 다음그림들은필자가메일클 라이언트를사용하여메일을받고, 메시지를작성하여하이텔의필자 ID 로전송한뒤에이 를하이텔에접속하여메일이도착했는지확인하는그림이다.
메시지클라이언트 / 서버어플리케이션의제작 이번에는 NetMaster 의메시지클라이언트와서버컴포넌트를이용하여간단한메시지를주고받을수있는메시지클라이언트 / 서버어플리케이션을만들어보자. 컴포넌트의사용방법을익히고자하는것이목적이므로 1:1 채팅어플리케이션과마찬가지로하나의어플리케이션이서버이면서동시에클라이언트역할을하도록만들도록한다. 먼저폼에패널을하나얹고패널위에메시지서버의 IP 주소를지정할에디트박스와보내는사람의이름을설정할에디트박스, 그리고실제메시지를입력할에디트박스를추가한다. 그리고, 메모컴포넌트를폼에추가하여전달되는메시지를표시하도록한다. 그리고다음과같이 TStatusBar 컴포넌트를추가하여인터넷메일클라이언트에서와마찬가지로메시지컴포넌트의상태를표시할수있도록한다. 소켓프로그래밍을이용한채팅프로그램보다사용방법은더간단하다. 다음과같이서버의 IP 주소와보내는사람의이름을각각 Host, FromName 프로퍼티에지정하고, 보내는메시지내용을 PostIt 메소드를이용하여전송하는것으로끝이다.
procedure TForm1.Edit3KeyPress(Sender: TObject; var Key: Char); if Key = #13 then NMMsg1.Host := Edit1.Text; NMMsg1.FromName := Edit2.Text; NMMsg1.PostIt(Edit3.Text); 이제는메시지서버컴포넌트와메시지컴포넌트의각이벤트를처리하기만하면된다. 먼 저메시지서버컴포넌트의 OnClientContact 이벤트는클라이언트가접속했을때발생하게 된다. procedure TForm1.NMMSGServ1ClientContact(Sender: TObject); NMMsgServ1.ReportLevel := Status_Basic; NMMsgServ1.TimeOut := 90000; StatusBar1.SimpleText := 'Client connected'; 그리고, 전송된메시지는메시지서버컴포넌트의 OnMSG 이벤트에서처리할수있다. procedure TForm1.NMMSGServ1MSG(Sender: TComponent; const sfrom, smsg: String); Memo1.Lines.Add('(' + sfrom + ') ' + smsg); 메시지서버컴포넌트의상태에대한정보는 OnStatus 이벤트핸들러에서처리한다. procedure TForm1.NMMSGServ1Status(Sender: TComponent; Status: String); if StatusBar1 <> nil then StatusBar1.SimpleText := Status;
그다음에는메시지클라이언트컴포넌트의이벤트를처리해야하는데, 이들의처리방법은 앞서설명한인터넷메일컴포넌트들과큰차이가없으므로자세한설명은생략하도록하 겠다. procedure TForm1.NMMsg1Connect(Sender: TObject); StatusBar1.SimpleText := 'Connected'; procedure TForm1.NMMsg1ConnectionFailed(Sender: TObject); ShowMessage('Connection Failed'); procedure TForm1.NMMsg1Disconnect(Sender: TObject); if StatusBar1 <> nil then StatusBar1.SimpleText := 'Disconnected'; procedure TForm1.NMMsg1HostResolved(Sender: TComponent); StatusBar1.SimpleText := 'Host Resolved'; procedure TForm1.NMMsg1MessageSent(Sender: TObject); ShowMessage('Message sent!'); procedure TForm1.NMMsg1Status(Sender: TComponent; Status: String); if StatusBar1 <> nil then StatusBar1.SimpleText := Status;
이제이프로그램을컴파일하고실행한뒤에 1:1 채팅을하듯이사용해보자. 같이무리없이잘동작할것이다. 다음그림과 스트림클라이언트 / 서버어플리케이션의제작 이번에는스트림서버와클라이언트컴포넌트를이용하여파일을전송하고, 이파일의내용을볼수있는어플리케이션을만들어보자. 먼저서버의 IP 주소를지정할수있는에디트박스와버튼 2 개와 TStatusBar 컴포넌트, 그리고 TOleContainer 컴포넌트와 TOpenDialog, 스트림클라이언트와서버컴포넌트를각각하나씩다음과같이폼에추가하도록한다. Send 버튼을클릭하면파일열기대화상자에서지정한파일을전송하며, Load 버튼을클
릭하면이파일을 OLE 컨테이너에서볼수있도록해준다. 먼저 Send 버튼의 OnClick 이벤트핸들러를다음과같이작성한다. procedure TForm1.Button1Click(Sender: TObject); var MyFStream: TFileStream; If OpenDialog1.Execute then NMStrm1.Host := Edit1.Text; NMStrm1.FromName := ExtractFileName(OpenDialog1.FileName); MyFStream := TFileStream.Create(OpenDialog1.FileName, fmopenread); try NMStrm1.PostIt(MyFStream); finally MyFStream.Free; 사용방법은 Host 프로퍼티에접속할서버의 IP 주소를대입하고, 지정된파일스트림을 PostIt 메소드를이용하여서버로전송한다. 참고로 FromName 프로퍼티에파일이름을지정하면파일의확장자를알수있으므로이를활용한다. 즉, 스트림에대한정보를 FromName 프로퍼티를이용하여문자열로전송하는것이다. 이렇게전송된파일스트림은스트림서버컴포넌트의 OnMSG 이벤트에서받아볼수있는데, 이이벤트핸들러에서임시파일로저장할수있도록한다. 스트림서버컴포넌트의 OnMSG 이벤트핸들러를다음과같이작성한다. procedure TForm1.NMStrmServ1MSG(Sender: TComponent; const sfrom: String; strm: TStream); var MyFStream: TFileStream; Extension := ExtractFileExt(sFrom); if FileExists(ExtractFilePath(ParamStr(0)) + '\tmp' + Extension) then DeleteFile(ExtractFilePath(ParamStr(0)) + '\tmp' + Extension);
MyFStream := TFileStream.Create( ExtractFilePath(ParamStr(0)) + '\tmp' + Extension, fmcreate); try MyFStream.CopyFrom(strm, strm.size); finally MyFStream.Free; 이이벤트핸들러에서파일이름이 tmp 이고파일확장자는앞서스트림클라이언트컴포넌트에서 FromName 프로퍼티에지정한문자열이 sfrom 파라미터로넘어오므로, 이값을이용하여확장자를지정할수있다. 여기서는디렉토리를현재어플리케이션이실행되고있는디렉토리에저장하도록하였다. 이렇게저장된파일을 OLE 컨테이너에서보여주는역할을하는, Load 버튼은비교적구현하기쉽다. 파일이름이 tmp 이고, 지정된확장자를가진파일을 CreateObjectFromFile 메소드를이용해서 OLE 컨테이너에보여준다. procedure TForm1.Button2Click(Sender: TObject); if FileExists(ExtractFilePath(ParamStr(0)) + '\tmp' + Extension) then OleContainer1.CreateObjectFromFile(ExtractFilePath(ParamStr(0)) + '\tmp' + Extension, False); 그리고, 폼의 OnClose 이벤트핸들러에서이렇게생성된임시파일을삭제하도록한다. procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction); if FileExists(ExtractFilePath(ParamStr(0)) + '\tmp' + Extension) then DeleteFile(ExtractFilePath(ParamStr(0)) + '\tmp' + Extension); 스트림컴포넌트의다른이벤트핸들러는메시지클라이언트 / 서버와마찬가지로상태정보를보여주는역할을한다. 각각의이벤트핸들러는다음과같이구현하며, 그다지어려운내용이아니므로자세한설명은생략하도록하겠다.
procedure TForm1.NMStrm1Connect(Sender: TObject); StatusBar1.SimpleText := 'Connected'; procedure TForm1.NMStrm1Disconnect(Sender: TObject); if StatusBar1 <> nil then StatusBar1.SimpleText := 'Disconnected'; procedure TForm1.NMStrm1HostResolved(Sender: TComponent); StatusBar1.SimpleText := 'Host Resolved'; procedure TForm1.NMStrm1Status(Sender: TComponent; Status: String); if StatusBar1 <> nil then StatusBar1.SimpleText := Status; procedure TForm1.NMStrmServ1ClientContact(Sender: TObject); NMStrmServ1.ReportLevel := Status_Basic; NMStrmServ1.TimeOut := 90000; StatusBar1.SimpleText := 'Client connected'; procedure TForm1.NMStrmServ1Status(Sender: TComponent; Status: String); if StatusBar1 <> nil then StatusBar1.SimpleText := Status;
procedure TForm1.NMStrm1ConnectionFailed(Sender: TObject); ShowMessage('Connection Failed'); procedure TForm1.NMStrm1MessageSent(Sender: TObject); ShowMessage('Stream Sent'); procedure TForm1.NMStrm1PacketSent(Sender: TObject); StatusBar1.SimpleText := IntToStr(NMStrm1.BytesSent) + ' of ' + IntToStr(NMStrm1.BytesTotal) + ' Sent'; 이것으로스트림서버 / 클라이언트어플리케이션이완성되었다. 프로그램을컴파일하고실행한뒤에 Send 버튼을클릭하여파일을지정하면, 서버로파일스트림이전송될것이다. 다음그림은이책의 1 장에해당되는워드파일을보여주는것이다. 정리 (Summary) 델파이는 2.01 버전부터지원하던액티브 X 컨트롤형태의인터넷컴포넌트를버리고, 델파
이 4 부터는 native VCL 형태의뛰어난컴포넌트를제공하여지금까지의많은불평들을해소시켜주었다. 또한 TPowersock, TGeneralServer 와같은컴포넌트를이용하면 RFC 에서제정하는새로운프로토콜을지원하는컴포넌트를직접만들거나, 프로토콜부터새로만들어서사용할수도있다. 그렇지만, 중요한것은이러한과정을제대로이해하기위해서는윈속 API 를이용한실제구현방법에대해서알아야할것이다. NetMaster 컴포넌트에대해서아쉬운것은델파이 4 에번들되어있지만, 소스코드는공개되지않았다는점이다. 이런측면에서 Francois Piette 의 ICS 컴포넌트 suite 를추천하고싶다. 이컴포넌트는델파이수퍼페이지나델파이델리와같은사이트에서쉽게구할수있다.