文章详情

  • 游戏榜单
  • 软件榜单
关闭导航
热搜榜
热门下载
热门标签
php爱好者> php文档>delphi(15)

delphi(15)

时间:2006-06-10  来源:许我一个信仰

第16章  Intranet与Internet编程

最早出现的网络协议分为两个集合,二者一同出现,分别是TCP和IP。TCP(传输控制协议)和IP(网际协议)通过协作,在局域网、内部网、外部网和Internet连接之上提供了一个逻辑层。局域网是计算机通过导线和网卡(NIC卡)在物理上连接起来,或像我家里和办公室那样,通过无线连接。楼下供孩子用的计算机使用AirEzy 2405无线收发器,使得地下室中的计算机可以连接到我办公室的局域网,进而又连接到Internet。这样孩子们可以上网冲浪、玩Half Life游戏;而其他工作也可以同时进行。

TCP协议并未涉及过多细节,其中包括了对报文头的描述,可以使数据在网络上漫游。TCP协议会维护状态信息,使得连接的两端可以相互了解。IP协议向报文添加了特定的头部信息,用于对点分IP地址进行分类和解析。例如,198.109.162.177与域名www.softconcepts.com相关联。我的ISP服务提供商向DNS(域名服务器)添加了一个名字项,将www.softconcepts.com与198.109.162.17关联起来。当向我的网站收发信息时,IP数据报中包括了寻址信息。在数据包由工作站移动到集线器再移动到网络服务器,通过铜缆调制解调器(经AT&T许可),再经过路由器和其他服务器的过程中,TCP和IP协议定义了跟踪数据的手段。

其他协议如Gopher等在TCP/IP出现之前很流行,在TCP/IP层之上还有其他基于TCP/IP的协议,这些协议都是具有特定用途的。例如,FTP(File Transfer Protocol,文件传输协议)用于方便网络上的文件传输。UDP(User Datagram Packet,用户数据报)协议是无连接的,它由TCP/IP发展而来,并不维护客户与服务器之间的连接信息,它在某些方面非常有用,如流类型的媒体,其中有一些报文丢失是可以接受的。

如果想要深入了解各种协议的细节,包括TCP/IP、FTP、UDP、Gopher等等,可能需要找一本书来看看,如Que出版的《Using TCP/IP》,作者是John Ray。另一种方法是,可以查找特定的RFC(Request For Comment)和白皮书,来阅读有关特定协议的所有细节。本章并不讲述有关协议的底层细节,但您可以学到如何使用Delphi提供的各种组件和类,对大多数常见的协议进行编程。Delphi 6中新增了来自Nevrona的Internet Direct组件。除了前一版本的Delphi提供的Internet和Fastnet组件之外,在Internet Direct或Indy中包括了许多客户和服务器端的组件来支持TCP/IP、UDP、Echo、Finger、Gopher、HTTP、POP3、SMTP,NNTP和Telnet等协议。对特定协议内容的简要评论可能较为有用,除此之外,本章还将示范许多新的客户和服务器端组件。到本章结束,通过使用这些强大的组件,您可以学到大量Internet和内部网编程技术,还可以在实例程序中很好的实践一下。

16.1  传输控制协议(TCP)组件

组件面板Indy Clients和Indy Servers属性页上的IdTCPClient和IdTCPServer组件直接支持TCP协议,而Internet属性页上来自Borland公司的TcpClient和TcpServer组件也同样支持该协议。由于支持TCP的Indy组件较新,本节我们将把注意力集中于它们。请记住,两对组件都支持同一协议,而每对组件都可以用TCP编程;而Nevrona和Borland的组件可能在特定的名字和特性上不同。

注意:所有的Internet Direct(Indy)组件都以Id为前缀。

 

16.1.1  Indy客户端TCP组件

通过给出服务器的主机名或IP地址,以及服务器程序所监听的端口号,一个TCP客户程序即可连接到TCP服务器。对于Internet或HTTP来说,服务器程序的通用端口号是80。其他已分配的通用端口号包括:FTP协议20和21,Telnet协议23,SMTP协议25,Gopher协议70,POP3协议110。在命令行运行netstat.exe,即可确定系统使用的所有端口号。下面的列表包括了在我的工作站上使用的一部分端口。

注意:在巴隆计算机术语词典中,将端口定义为CPU与另一设备(非内存)之间的连接,信息可以通过该连接出入计算机。虽然该定义没有达到非常令人不满的程度,但端口确实是一个被滥用的术语。将一根5型电缆插入到网络接口卡中,插口也可称为端口。对我们的目的而言,端口是微处理器与物理设备之间的一个微小的物理连接。端口号告诉CPU向何处发送数据。扬声器的端口号是$61。在向端口$61发送比特值0和1之后,扬声器将发出稳定的蜂鸣声。而发送另一个值清除01比特,就关掉扬声器。下面的步骤示范了直接向扬声器端口发送数据的过程。
1.打开命令行窗口。
2.在命令行进入Debug,运行Debug程序。

3.Debug程序使用短划线(-)作为命令提示符。在Debug命令提示符后键入o61,3,并敲回车键。

4.然后可以听到扬声器发出蜂鸣声。键入o61,0将关掉扬声器。这些指令将直接从微处理器发送到61端口。

Active Connections

Proto Local Address Foreign Address              State

TCP   ptk800:1025   SCI.TCIMET.NET:nbsession        ESTABLISHED

TCP    ptk800:1482   64.124.41.224.napster.com:8888 ESTABLISHED

TCP    ptk800:1621   SCI.TCIMET.NET:1046             ESTABLISHED

TCP    ptk800:1625   SCI.TCIMET.NET:1072             ESTABLISHED

TCP    ptk800:1630   SCI.TCIMET.NET:1046             ESTABLISHED

TCP    ptk800:1634   SCI.TCIMET.NET:1072             ESTABLISHED

TCP    ptk800:1636   SCI.TCIMET.NET:nbsession        ESTABLISHED

TCP    ptk800:1026   LocalHost:1029                  ESTABLISHED

TCP    ptk800:1029   LocalHost:1026                  ESTABLISHED

即使没有Web服务器来测试应用程序也不要担心;可以使用LocalHost——127.0.0.1——地址来测试客户和服务器程序。另外,也可以建立测试专用的Web服务器,在Windows NT下可以从Network Applet(控制面板中的网络设置)中安装Peer Web Server,而Windows 98中可以安装Personal Web Server。要在使用Windows NT的计算机上安装Peer Web Server,打开控制面板上的Network Applet。然后转到Services属性页,单击Add按钮。安装过程可能需要Windows NT光盘以完成安装。

netstat输出的第二栏中是本地计算机名,后接端口号;第三个栏目是远程计算机,或接收主机和端口号,后接状态。当Foreign Address栏中显示LocalHost时,指的是IP地址127.0.0.1,即所谓的loopback地址,PC机可以使用该地址引用其自身。LocalHost的URL是IP地址127.0.0.1。

注意:由于某些原因,使得运行Peer Web Server是个好主意。首先,规模较小的Web服务器可以成为很好的单元测试平台;其次,它可以迅速而简易地建立起内部网的网站,用于在公司内与您的项目有关的人共享信息。它对于团队开发是既有价值又较为廉价的工具。

IdTCPClient——Internet Direct TCP客户组件,位于组件面板的Indy Clients属性页上;要测试该组件,需要完成下列步骤并添加列出的代码。

1.Windows NT下,在控制面板的Network Applet中添加Peer Web Server(Windows 9x下使用Personal Web Server)。默认情况下,该服务器将使用LocalHost地址的80端口以及default.htm页面。

2.启动Delphi,向默认的空白窗体添加TIdTCPClient组件。

3.向窗体添加TMemo组件,可在其中放置TCP连接的输出。

4.向FormCreate事件处理程序添加下列代码。

procedure TForm1.FormCreate( Sender : TObject );

begin

IdTCPClient1.Host := '127.0.0.1';

IdTCPClient1.Port := 80;

IdTCPClient1.Connect;

try

IdTCPClient1.SendCmd('GET /default.htm' );

Memo1.Lines.Add( IdTCPClient1.CurrentReadBuffer );

finally

IdTCPClient1.Disconnect;

end;

end;

警告:如果为TIdTCPClient.OnStatus编写事件处理程序,需要手工向uses组件添加IdStatus,在该单元中包括了TIdTCPClient.OnStatus事件方法,否则将出现编译错误(当您阅读到这里时,该问题可能已经解决)。

代码的第一行将主机的IP地址定义为字符串。可以使用TIPAddress组件,它可以方便地从用户输入得到IP地址。第二行包含了端口号。对于HTTP协议,通常是80端口,但如果要通过代理服务器,可能是别的端口号,像8080。第三行使用指定的端口号连接到服务器。SendCmd方法向进行响应的服务器发送合适的字符串。GET和POST对于HTTP服务器来说是合适的。命令字符串是否合适是依赖于协议的,例如对于21端口上的FTP协议,其他的命令如LIST可能较为合适。如果要建立支持某个特定协议的程序,可能需要子类化客户和服务器组件,以通过非基于文本的命令集。

如果在运行这个简单的示例时出现问题,可以打开Windows NT任务管理器并确认inetinfo.exe已经出现在进程列表中。如果inetinfo.exe(即Peer Web Services)已经运行,可以打开Internet Service Manager(从开始菜单中选择Start | Program Files | Micorsoft Peer Web Services | Internet Service Manager即可)。然后确认TCP端口和Home目录(见图16.1)。在安装Peer Web Services后Web、FTP和Gopher程序被注册为服务程序。在系统启动时,这些程序自动启动。可以使用Internet Service Manager或Services Applet(如图16.2所示)来停止这些服务。也可以使用Services Applet使这些服务在启动时自动运行;如果您在使用内部网,您可能希望这样设置。

图16.1  Web Services Properties对话框可以配置

         Peer Web Server,或确认端口和目录信息

图16.2  Peer Web Services在Windows NT中作为服务运行,

默认情况下,在Windows启动时,该服务将自动启动

在本书的CD-ROM中包括了TCPClient程序的一个稍加完善的版本,TCPClient.dpr。TCPClient程序并不限于使用HTTP服务器。许多通用的协议是基于TCP的,这意味着您可以使用同一TCPClient程序连接到特定类型的服务器。对前面的代码稍加修改,即可连接到FTP服务。

IdTCPClient1.Host := '127.0.0.1';

IdTCPClient1.Port := 21;

IdTCPClient1.Connect;

try

IdTCPClient1.SendCmd('USER anonymous');

IdTCPClient1.SendCmd('PASS [email protected]');

IdTCPClient1.SendCmd('HELP' );

Memo1.Lines.Add( IdTCPClient1.CurrentReadBuffer );

finally

IdTCPClient1.Disconnect;

end;

提示:关于TCP协议的更多知识,可以阅读RFC 0793,位于http://sunsite. iisc.ernet.in/collection/rfc/rfc0793.html。

列出的代码假定您已经安装了FTP服务和Peer Web Services,并在默认情况下允许匿名登录(您也可以在Microsoft Internet Service Manager中确认FTP服务的设置)。通常,您可能觉得特定协议的客户和服务器组件比一般的TCP组件更容易使用。

16.1.2  Indy服务器TCP组件

IdTCPServer组件可用于创建TCP服务器程序。TCP服务器组件绑定到一个服务器端口,例如8080,通过响应OnExecute事件在该端口上接收TCP客户请求。例如,如果TCP客户程序使用WriteLn向服务器发送文本,然后即可用OnExecute方法的AThread参数读出客户发送的文本(TIdUDPServer组件的使用可以参见下一节,特性和方法都非常相似)。

表16.1列出了TIdTCPServer组件值得注意的特性。

表16.1  TIdTCPServer组件的特性。设置Bindings特性(IP:Port)、DefaultPort和

Active状态,即可通过传递给OnExecute方法的TIdPeerThread对象接收客户请求

特性

描述

AcceptWait

对客户连接的超时等待时间

Active

布尔值,表示服务器是否处于激活状态

Bindings

IP:Port格式的地址字符串,表示服务器所监听的IP地址和端口号

DefautlPort

监听客户连接的默认端口号

Intercept

对TIdServerInterceptOpenSSL组件的引用,该组件接收发送到TIdTCPServer组件的数据

ThreadMgr

对线程管理器集合的引用,其中包括了引用客户连接的线程对象的列表

如果要在端口9090创建一个响应客户的服务器程序,需要将DefaultPort特性设置为9090,并把Active特性设置为True。设置好特性后,使用OnExecute事件处理程序和TIdPeerThread参数即可与客户程序进行通信。例如,读出客户程序发送的文本字符串,可使用下列代码:

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);

begin

ShowMessage( AThread.Connection.ReadLnWait );

end;

TCP服务器组件的事件处理程序IdTCPServer1.OnExecute是通过TIdPeerThread对象的一个实例来调用的。AThread参数中包括了对进行调用TIdTCPServerConnection实例的引用,TIdTCPServerConnection由TIdTCPConnection子类化而来。TIdTCPConnection中包含了发送和接收文本格式数据、字符串命令或TStream对象的方法,还可以检测TCP组件的连接状态。

16.2  用户数据报(UDP)组件

UDP协议由TCP协议衍生而来。TCP用于确保点对点的连接,而UDP并不保证发送的数据一定到达,也不表明数据报的传输成功或失败。UDP协议所失去的在其速度中得到了补偿。可以发送流化数据的程序中找到UDP的应用,如视频、声音和视频游戏等。对于这种类型的数据,丢失一两帧不会有什么影响,速度才是最重要的。“当程序的目的在于要传输尽可能多的信息时,当数据的丢失相对不重要时,可以使用UDP。(Ray,1999)”

注意:当编写本章时,UDP组件只支持发送接收字符串数据,这使得建立能够流化视频和图像的UDP客户和服务器较为困难。也许还会有些变化,等到您使用Delphi 6时,可能已经可以传输TStream对象和二进制数据了。

UDP在RFC 0768中描述,可以在http://www.cis.ohio-state.edu/htbin/rfc/rfc0768.html得到。Delphi提供了UDP协议的客户和服务器组件:TIdUDPClient和TIdUDPServer。TIdUDPClientServer.bpg文件中包含了一个客户服务器程序的例子。客户通过发送程序员定义的字符串命令来停止和开始由TMediaPlayer播放的.AVI视频。

注意:演示用的客户和服务器程序并不代表着UDP协议的最佳用途,只是对Internet Direct实现的该组件的功能性的示范。

为创建使用TIdUDPClient和TIdUDPServer的示例程序,首先创建Delphi工程。然后向工程组再添加一个工程。把一个工程作为客户,另一个作为服务器。为连接到UDP服务器,需要一个TIdUDPClient组件。用来表示服务器的IP地址和端口号。对于示例程序,可以选择一个未被其他基于TCP的协议占用的端口号,如8090。由于UDP是无连接的,因此在UDP的接口方法中不包括Connect方法。要使用Internet Direct实现的组件,只需使用Send向给定的IP地址和端口号广播一个命令字符串。示例中的客户程序向服务器发送“PLAY”和“STOP”字符串。假定一个通常的窗体,使用按钮在播放和停止状态之间进行切换,可以如下实现。

procedure TForm1.Button2Click(Sender: TObject);

const

Command : string = 'PLAY';

begin

IdUDPClient1.Send(Command);

Memo1.Lines.Add( IdUDPClient1.ReceiveString );

if( Command = 'PLAY' ) then

Command := 'STOP'

else

Command := 'PLAY';

end;

较为重要的代码是IdUDPClient1.Send(Command),这个语句很简单。可以注意到其中并未表示连接或断开。

服务器是作为应用程序实现的,它对客户程序发出的一个简单的命令集进行响应。为实现服务器,程序将监听DefaultPort端口(8090,与客户相匹配)上的广播,并将TIdUDPServer.Active设置为True。当UDP服务器从客户接收数据时,将调用TIdUDPServer.OnUDPRead事件处理程序。下面列出的代码示范了如何从OnUDPRead事件的参数中读出数据。

procedure TForm2.IdUDPServer1UDPRead(Sender: TObject; AData:

TStream;

const APeerIP: String; const APeerPort: Integer);

var

S : TStringStream;

begin

S := TStringStream.Create('');

try

S.CopyFrom( AData, AData.Size );

if( CompareText( S.DataString, 'PLAY' ) = 0 ) then

MediaPlayer1.Play

else if (CompareText( S.DataString, 'STOP' ) = 0 ) then

MediaPlayer1.Stop;

IdUDPServer1.Send( APeerIP, APeerPort, S.DataString );

finally

S.Free;

end;

end;

OnUDPRead有四个参数,类型分别是:TObject、TStream、string和integer。TObject参数表示发送者对象。TStream参数包含了从客户端发送的数据。字符串参数APeerIP,即第三个参数,包含了发送数据的客户计算机的IP地址。APeerPort参数用于标识广播的端口号。

提示:本章进行写作时,Delphi 6 beta版并未集成Indy帮助文档和示例程序。当Delphi 6正式版出售时,应该已经包括文档和示例;现在可以访问http://www.nevrona.com/Indy/Download.html来得到示例程序、源代码和帮助文件。

上面列出的代码使用TStringStream来把数据从TStream对象复制到StringStream,使得代码可以像处理字符串一样处理该数据(另外,也可以使用As操作符来确定TStream对象实际数据类型)。在本例中,把字符串数据与简单的命令集进行比较,然后在使用调用客户的IP地址和端口号将该命令回送到客户端。

16.3  建立FTP客户程序

文件传输协议(FTP)是一个基于TCP/IP的协议,用于在网络上传输文件。当从Internet下载文件时,可能使用HTTP命令GET,也可能使用FTP。FTP协议具有自身的命令集合,Delphi中包含了一些组件,可用于建立FTP客户程序(例如Ipswitch公司的WS_FTPPro程序和Windows自带的命令行FTP程序)和FTP服务器程序(例如与Microsoft Internet Information Server一同发布的FTP服务器)。

注意:在浏览器中,像AOL的Netscape Navigator或Microsoft的Internet Explorer,可能最常用的协议是超文本传输协议(HTTP),但也可以使用其他几种协议,包括FTP、file、gopher和Telnet。

本书的光盘包含了几个FTP示例程序,包括SimpleFTP,它是一个简单的示例程序,使用NetMasters公司的TNMFTP组件实现,该组件可以在组件面板的FastNet属性页上找到,该程序的另一个版本是使用Nevrona公司的TIdFTP组件实现的。除此之外,光盘上还有一个精致的FTP程序FTPPro(如图16.3所示)。FTPPro程序完整的代码并未在此列出,该程序共有大约2000行代码。FTPPro.exe程序还演示了应用程序在注册表中的设置(参考本章稍后POP3示例的章节,其中有一个例子,使用TIniFile类来把程序设置保存在INI文件中)。FTPPro.exe示例程序还包括了闪烁屏幕、新的shell控件、可选窗体、密码对话框和很多的FTP功能。可以从光盘上复制该程序,并仔细查看一下。本节涵盖了FTP功能中最重要的部分,同时也突出强调了与FTP协议明确相关的代码。

图16.3  FTPPro.exe演示了一个基于Windows的FTP客户程序的

       完整实现,其中使用了NetMasters公司的TNMFTP组件

16.3.1  连接到FTP服务器

FTP客户程序的所有完整实现都必须满足FTP协议的要求。无论特定的特性和方法是否命名相同,这一点都是必须的。在Delphi中有两个组件可用于创建FTP客户程序。分别是NetMasters LLC公司的TNMFTP组件和Nevrona公司的TIdFTP组件。本节演示了为把FTP客户程序连接到FTP服务器,所有必要的特性和代码实例。

使用TNMFTP组件连接到FTP服务器

FTP协议要求主机IP和端口号。主机IP可以是如何运行FTP服务器的主机地址,而端口号通常是21。依赖于FTP服务器的配置,您可能需要提供用户名和密码。如果服务器允许匿名登录,那么可以使用anonymous作为用户名,并使用任何包含@字符的字符串作为密码(在匿名登录时,通常要求使用电子邮件地址作为密码)。下面的代码演示了匿名登录到FTP站点的过程,其中使用名为NMFTP1的TNMFTP组件。

NMFTP1.Host := '198.109.162.177';

NMFTP1.Port := 21;

NMFTP1.UserID := 'anonymous';

NMFTP1.Password := '[email protected]';

NMFTP1.Connect;

假定主机和端口号是正确的(本例中是正确的),而且服务器允许匿名登录,则上述代码将把FTP客户程序连接到所指定的主机。

两种组件都提供了基本的FTP服务,其不同点在于完全性和示范易于实现。在与文件上传和下载相关的章节中提供了一个例子,演示了如何用TNMFTP组件执行基本的FTP服务。FTPPro示例程序是用TNMFTP实现的;您可以从本书的CD-ROM中装载并运行FTPPro工程。

认证  如果使用TNMFTP组件登录到需要有效用户和密码的站点(而不是匿名登录时),则TNMFTP组件需要进行认证。通过实现TNMFTP.OnAuthenticationNeeded事件处理程序,可以提供动态认证功能。OnAuthenticationNeeded事件处理程序传递一个布尔变量参数Handled。将Handled赋值为True,TNMFTP将继续尝试登录。下面列出的代码示范了认证事件的实现。

procedure TFormMain.NMFTP1AuthenticationNeeded(var Handled: Boolean);

var

UserID, Password : string;

begin

UserID := NMFTP1.UserID;

Password := NMFTP1.Password;

Handled := GetPassword( UserID, Password );

if( Handled ) then

begin

NMFTP1.Password := Password;

NMFTP1.UserID := UserID;

end;

end;

GetPassword是一个全局函数(定义在本书光盘的UFormPassword单元中),该函数创建一个模式对话框窗体,该窗体非常简单,只包括两个输入域,分别代表用户名和密码。

代理服务器  有些FTP服务器可能会使用代理服务器作为防火墙,以避免黑客攻击;也可能像Software Conceptions公司一样,使用代理服务器向几台工作站通过Internet访问功能。通过设置TNMFTP.Proxy特性,可以指定代理服务器IP地址;通过设置TNMFTP.ProxyPort特性可以指定代理服务器端口。

使用TIdFTP连接到FTP服务器

Nevrona实现的Indy FTP组件TIdFTP支持的公开特性较少,但与TNMFTP基本相同。分别设置TIdFTP.Host、TIdFTP.Port、TIdFTP.User和TIdFTP.Password特性,并调用connect方法,即可将FTP客户程序连接到FTP服务器。SimpleFTP.exe的第二种实现位于本书的光盘上。

16.3.2  上传和下载文件

FTP协议的首要目的在于提供客户与服务器之间的文件传输功能。您可能对TNMFTP了解较多,这里对两种FTP实现TNMFTP和TIdFTP进行了比较,使得您可以了解不同的实现风格。

TNMFTP组件的文件传输方法

TNMFTP.Upload和TNMFTP.Download方法都需要两个参数。上传是将文件从客户复制到服务器的过程,而下载刚好相反。两个方法的声明如下。

procedure Download(RemoteFile, LocalFile: string);

procedure Upload(LocalFile, RemoteFile: string)

假定已经连接到FTP服务器,可以将要下载的服务器上文件的名字作为第一个参数,而本地计算机上保存的文件名作为第二个参数。

如果目标文件在远程或本地计算机已经存在,相应的上传和下载过程将覆盖目标文件。文件传输是异步的。这样,对上传和下载方法的调用将在文件传输完成之前返回。文件传输的成功或失败,需要通过实现TNMFTP.OnSuccess和TNMFTP.OnFailure事件方法来进行通知。两个事件方法的Trans_Type参数类型都是TCmdType。例如,如果调用OnFailure方法而TCmdType参数是cmdDownload,则可以知道下载操作已经失败。

TIdFTP组件的文件传输方法

Indy的FTP实现中,下载方法命名为Get,上传方法命名为Put。Get实现为过载方法。

procedure Get(const ASourceFile: string; ADest: TStream);

overload;

procedure Get(const ASourceFile: string; const ADestFile:

string; const ACanOverwrite: boolean); overload;

Get方法的第一个版本取得远程计算机上的源文件名,并把文件下载到TStream对象中。第二个版本与TNMFTP的实现较为相似。第一和第二个参数分别表示远程和本地文件名,第三个参数指明在目标文件已经存在时是否覆盖。下面列出的代码示范了如何将文件下载到TStream对象中。

var

FileName : string;

S : TStringStream;

begin

FileName := Copy( ListView1.Selected.Caption, LastDelimiter(' ',

ListView1.Selected.Caption), 255 );

S := TStringStream.Create('');

try

if( Pos( '<DIR>', ListView1.Selected.Caption ) = 0 ) then

IDFTP1.Get( FileName, S );

S.Position := 0;

Memo1.Lines.LoadFromStream(S);

finally

S.Free;

end;

end;

代码中有一些假定。第一个假定是TListView组件的Caption特性中包括文件名(在演示TIdFTP组件的SimpleFTP示例工程中确实如此,该工程在本书的光盘上),而且窗体上有一个名为Memo1的TMemo组件。代码将文件名从TListView组件中提取出来。然后创建TStringStream对象,TStringStream是TStream的后代,因此我们可以对TIdFTP.Get方法的TStream参数使用TStringStream对象。将创建的流对象传递给Get方法。在调用Get方法后需要将流的位置重置为0,向流对象复制数据将改变流的当前位置。在装载了StringStream对象之后,可以使用TStrings.LoadFromStream方法将TStringStream对象中的数据复制到TMemo对象中(请记住,TMemo的Lines特性是一个TStrings类型的对象特性)。

实现中过载了Put方法,以便直接在流对象和文件上进行工作(该实现是对称的,使得组件的使用非常直接。按照直觉,我们可以认为,既然Get方法可以在流对象和文件上工作,那么,与之相对应的Put方法也应该能够在流对象和文件上工作)。Put方法的声明如下:

procedure Put(const ASource: TStream; const ADestFile: string;

const AAppend: boolean); overload;

procedure Put(const ASourceFile: string; const ADestFile: string;

const AAppend: boolean); overload;

在Put的第一个实现中,该方法将数据从客户端的流对象复制到远程服务器上的文件。如果AAppend参数设置为True,那么数据将附加在已存在的文件之后。在Put方法的第二个实现中,源数据和目标数据都是文件,而AAppend参数的作用与第一种实现相同。

16.3.3  向FTP服务器发送命令

FTP协议包含一个命令集,由大约20条命令组成。对于特定的组件而言,不见得把协议命令集中的每个命令都作为公有方法实现。通过使用一个一般性的方法向服务器发送命令字符串,可以支持在TIdFTP和TNMFTP组件的接口中并不直接支持的FTP命令。TIdFTP使用SendCmd方法实现了这种能力。

function SendCmd(const AOut: string; const AResponse: SmallInt):

SmallInt; virtual; overload;

function SendCmd(const AOut: string; const AResponse: Array of

SmallInt): SmallInt; virtual; overload;

在上述函数中,需要传递一个命令字符串和一个响应代码(或一组响应代码)。如果服务器的响应与响应代码并不匹配,将产生异常。在下面的代码片断中,使用响应代码150和226向FTP服务器发送了一个通常的list命令。如果服务器的响应不是150(打开连接)和226(传输完成),将产生异常。第二行代码将把缓冲区中的内容读入到TString对象Dest中。

IdFTP1.SendCmd('ls', [150, 226]);

Dest.Text := IdFTP1.CurrentReadBuffer;

如果一个方法是可用的,则无需使用一般性的SendCmd方法。TIdFTP.List方法实现如下。

procedure List(ADest: TStrings; const ASpecifier: string;

const ADetails: boolean);

第一个参数类型为TStrings,第二个参数不是文件掩码,而第三个参数表示返回文件信息。使用下述代码中的参数调用List,可以只返回文件名。

var

Dest : TStrings;

begin

Dest : TStringList.Create;

try

IDFtp1.List( Dest, '*.*', False );

for I := 0 to Dest.Count - 1 do

with ListView1.Items.Add do

Caption := Dest[I];

finally

Dest.Free;

end;

end;

上面的代码向List传递了TStrings对象Dest和文件掩码‘*.*’,而且不要求返回详细信息。for循环将字符串数组中的数据复制到名为ListView1的TListView组件中。

提示:FTP协议定义在RFC 959中。

 

幸运的是,Indy和NetMaster FTP组件实现了许多通常的FTP命令,如上传和下载文件、建立和改变目录、删除和列出文件等。DoCommand是SendCmd方法的TNMFTP版本,该方法只有一个字符串参数,即所用的命令。对于FTP客户端的实现细节,请参见本书光盘上的FTPPro例子程序。

16.4  创建Telnet客户程序

终端仿真客户可以利用Telnet协议连接到Telnet服务器,通常使用端口23。尽管可以连接到其他的TCP/IP服务器,通常Telnet客户只用于连接到返回终端数据的服务器(参见John Ray的《Special Edition Using TCP/IP》一书,其中有一些有趣的例子使用Telnet客户端连接到POP3和SMTP服务器)。本书光盘上的例子程序TelnetPro包含有演示Telnet客户端的源代码,该客户端模仿了Windows自带的Telnet.exe程序。

TelnetPro程序的终端仿真器非常基本。为创建完整的Telnet程序,需要实现(或找到)一个终端仿真器。如果要实现终端仿真器,可以在http://www.inwap.com/pdp10/ANSIcode.txt

找到仿真器所必须支持的ANSI代码的完整描述。ANSI代码的形式类似于$1B[,即ASCII码27(十六进制$1B)所代表的Esc字符后接左方括号,其后是特定的字符串序列。

图16.4  如图所示,使用TelnetPro.exe示例程序连接到密歇根州

        立大学图书馆的MAGIC服务器,网址为magic.msu.edu

为完成终端仿真器,需要定义一个语法分析循环,用于从明文中提取ANSI转义代码,并根据ANSI代码确定何时删除显示内容、滚屏或修改显示字体。除了工作站上自带的很小的Telnet客户端(可以通过Start | Run菜单运行)之外,NetManage.com提供了Rumba。这两个产品演示了Telnet程序应具有的功能。

使用TIdTelnet组件,需要指定主机和端口号。例如,将主机设置为magic.msu.edu,端口号设置为23,即可连接到MSU大学的MAGIC服务器。实现TIdTelnet.OnDataAvailable方法从字符串参数Buffer中读入服务器的响应。Buffer参数中包含了ANSI代码和要显示的文本。可使用SendCh或SendStr向服务器发送数据。SendCh每次通过Telnet端口发送一个字符,而SendStr每次向服务器传递一个字符串参数。下面的示例代码捕获RichEdit控件中的击键事件,并按每次一个字符将其发送出去。

procedure TFormMain.RichEdit1KeyPress(Sender: TObject; var Key:

Char);

const

Input : string = '';

begin

if( Not IdTelnet1.Connected ) then exit;

IdTelnet1.SendCh(Key);

Key := #0;

end;

如果TIdTelnet组件并未连接到服务器,上面的代码将忽略key参数。否则,每次将向服务器发送一个字符,并把key参数设置为#0;这使得一直可以向RichEdit控件直接插入字符。下面列出的代码示范了一个简单的Telnet应用程序。

unit UFormMain;

interface

uses

Windows, Messages, SysUtils, Variants, Classes, Graphics,

Controls, Forms,

Dialogs, IdBaseComponent, IdComponent, IdTCPConnection,

IdTCPClient,

IdTelnet, StdCtrls, Menus;

 

type

TFormMain = class(TForm)

Memo1: TMemo;

IdTelnet1: TIdTelnet;

MainMenu1: TMainMenu;

Connect1: TMenuItem;

Connect2: TMenuItem;

Disconnect1: TMenuItem;

N1: TMenuItem;

Exit1: TMenuItem;

procedure IdTelnet1DataAvailable(Buffer: String);

procedure FormDestroy(Sender: TObject);

procedure Memo1KeyPress(Sender: TObject; var Key: Char);

procedure Exit1Click(Sender: TObject);

procedure Connect2Click(Sender: TObject);

procedure Disconnect1Click(Sender: TObject);

procedure FormClose(Sender: TObject; var Action:

TCloseAction);

procedure FormCreate(Sender: TObject);

private

{ Private declarations }

public

{ Public declarations }

end;

 

var

FormMain: TFormMain;

implementation

{$R *.DFM}

procedure TFormMain.IdTelnet1DataAvailable(Buffer: String);

begin

Memo1.Lines.Add(Buffer);

end;

 

procedure TFormMain.FormDestroy(Sender: TObject);

begin

IdTelnet1.Disconnect;

end;

 

procedure TFormMain.Memo1KeyPress(Sender: TObject; var Key:

Char);

begin

if( IdTelnet1.Connected ) then

begin

if( Key = #13 ) then Memo1.Clear;

IdTelnet1.SendCh(Key);

Key :=#0;

end;

end;

 

procedure TFormMain.Exit1Click(Sender: TObject);

begin

Close;

end;

 

procedure TFormMain.Connect2Click(Sender: TObject);

var

Host : string;

begin

Memo1.Clear;

Host := 'magic.msu.edu';

Disconnect1Click(Self);

if( InputQuery( 'Host', 'Enter host address:', Host )) then

begin

IdTelnet1.Host := Host;

IdTelnet1.Connect;

end;

end;

 

procedure TFormMain.Disconnect1Click(Sender: TObject);

begin

if( IdTelnet1.Connected ) then

IdTelnet1.Disconnect;

end;

 

procedure TFormMain.FormClose(Sender: TObject; var Action:

TCloseAction);

begin

Disconnect1Click(Self);

end;

 

procedure TFormMain.FormCreate(Sender: TObject);

begin

Connect2Click(Self);

end;

end.

当创建窗体或单击Connect菜单项时,该应用程序需要用户输入主机名。还实现了Disconnect和Exit菜单。当客户连接到服务器时,如果已经定义了OnDataAvailable事件处理程序,将调用该事件。在本例中,文本直接发送到TMemo控件。为正确显示数据流,需要终端仿真器来读入并解释服务器发送的ANSI命令。如果客户已连接到服务器,Memo1KeyPress处理程序将把每个字符都发送到Telnet服务器。您可以使用例子程序连接到远程服务器,但其显示可能不很美观(从实现代码即可看出)。

16.5  使用POP3和SMTP建立Internet Email客户端程序

POP3和SMTP也可称为Internet email协议,它们支持收发email。POP3(Post Office Protocol)和SMTP(Simple Mail Transfer Protocol)是用于收发Internet email的两个协议。TIdPOP3和TIdSMTP两个组件提供了email支持,可用于建立Internet email的客户端程序。这两个组件使用了一个第三方组件TIdMessage,该组件代表了邮件服务器发送到客户程序的内容。要成功地传输数据,这些协议需要比前面的协议更多的信息,因此我们将从基于任务的角度来仔细查看每个组件;您可以看一下本书光盘上的SimplePOP3例子程序(我们不会在这里列出完整的程序代码)。

16.5.1  使用TIdPOP3组件

TIdPOP3组件需要一些必要的信息,以便使用POP3协议进行工作。组件的Host、Password、Port和UserID特性代表了这些信息。这些值存储在控制面板的Mail小应用程序中,可使用这些值接到POP3服务器(如图16.5所示)。在Mail小应用程序中,选定Internet E-mail配置文件并单击Properties按钮即可看到这些特性的值。

图16.5  Mail Properties对话框包含了特定邮件服务

     器的信息,如POP3或Exchange服务器

检查邮件

POP3组件可用于向邮件服务器查询邮件。调用TIdPOP3.CheckMessages将返回邮件的数目。下面的代码中将邮件数目存储到Count,然后进行Count次循环,重复调用TIdPOP3.Retrieve从邮件服务器获取每一个邮件。Retrieve的参数为邮件序号和TIdMessage对象。邮件序号从1开始。如果使用从0开始循环,需要向循环控制变量加1,才能得到有效的邮件序号。下面的代码对此进行了示范:

procedure TFormMain.ActionSendReceiveExecute(Sender: TObject);

var

I, Count : Integer;

begin

Count := IdPOP31.CheckMessages;

for I := 0 to Count - 1 do

if( IdPOP31.Retrieve( I + 1, IdMessage1 )) then

ShowMessage( IdMessage.From.Name + ':' +

IdMessage1.Subject );

end;

列出的代码假定已经调用了TIdPOP3.Connect方法并成功连接到邮件服务器。如果TIdPOP3.CheckMessages方法返回的邮件数目大于零,则使用TIdMessage对象示例IdMessage1来获取所有的邮件(TIdMessage是一个组件,位于组件面板的Indy Misc属性页上)。TIdMessage组件包含了所有必要的特性,可以看到附件并对邮件进行响应。关于SMTP组件可以阅读后面的章节,其中包含了一些响应和发送邮件的例子。

删除邮件

在TIdPOP3组件的当前实现中,Delete方法有一个参数表示邮件序号。您需要根据邮件获取顺序把序号存储起来。在TIdMessage对象中并不存储该信息。TIdMessage组件确实存储了MsgId字符串,但Delete方法无法使用MsgId进行工作。

IdPOP31.Delete( 1 );

从这个语句可以看出,在服务器上删除信息显然是很简单的。但并不显然的是,在邮件对象中没有存储服务器上的邮件序号。您只能手工编写代码来存储邮件序号。

注意:在更好的实现方法中可能会基于邮件的内容进行删除,以后可能会进行这样的修订。

当使用相对邮件序号调用Delete方法时,邮件被标记为删除,但直到与服务器断开连接时才真正删除。在图形用户界面中,可以选择删除或只是标记为删除。

16.5.2  使用TIdMessage组件

当使用Internet Direct POP3和SMTP组件时,无论是使用POP3组件获取邮件,还是使用SMTP组件发送邮件,都需要使用TIdMessage组件存储邮件。TIdMessage组件将邮件体存储为TStrings对象,可通过Body属性进行访问。From特性是一个TIdEmailAddressItem对象,使用TCollectionItem实现。Recipients特性定义为TIdEmailAddressList对象,是由TOwnedCollection子类化而来。Subject是一个字符串特性。像CCList、Date和ReplyTo等特性在集成帮助文档中都可以查到。

要发送一个基本的邮件,需要对上述特性赋值。下面的代码片段摘自SimplePOP3程序,示范了如何从可视化组件向TIdMessage类型的对象IdMessage1复制数据。

IdMessage1.Body.Assign( MemoBody.Lines );

IdMessage1.From.Text := IniFile.EmailAddress;

IdMessage1.Recipients.EmailAddresses :=

LabeledEditRecipient.Text;

IdMessage1.Subject := LabeledEditSubject.Text;

当准备好发送邮件时,需要使用TIdSMTP组件,将TIdMessage对象作为参数传递给该组件的send方法。下一节演示了如何发送邮件。

16.5.3  使用TIdSMTP组件

假定已经正确的配置了TIdMessage对象,下面就需要使用TIdSMTP组件来发送邮件。POP3服务器通常监听110端口,主机就是POP3主机名。SMTP服务器通常监听25端口。SMTP主机可能与POP3主机并不相同。为配置SMTP客户,需要给出SMTP主机、端口、用户名和密码。例如,您的SMTP主机名可能是与邮件有关的字符串,而端口号可能是25。

提示:如果不知道POP3和SMTP端口号、主机、用户id和密码,可以使用相应的email管理程序或控制面板中的Mail Properties Applet进行查看。

注意:.INI文件的作用与注册表相仿。两种方法都是可以接收的,但注册表对于存储应用程序的持久数据是首选方法(INI文件进行开发时更容易使用,而且应用程序出现问题时也不容易破坏注册表)。

TIdSMTP.Port、TIdSMTP.Host、TIdSMTP.UserID和TIdSMTP.Password特性可以在设计时或运行时配置。例如,本书光盘上的SimplePop3示例程序使用INI文件来存储连接信息。下面的代码演示了如何将静态值赋予SMTP组件特性并发送邮件(要发送的参数是一个TIdMessage对象实例。关于初始化邮件对象的例子,其参见上一节)。

IdSMTP1.Host := 'mail';

IdSMTP1.Port := 25;

IdSMTP1.UserID := 'userid';

IDSMTP1.Password := 'password';

IdSMTP1.Connect;

try

IdSMTP1.Send(IdMessage1);

finally

IdSMTP1.Disconnect;

end;

上面的代码假定已经正确的配置了TIdMessage对象。发送邮件前对TIdMessage对象的配置,请参见上一节。另外,您可以使用类方法QuickSend来发送email而无需使用TIdMessage组件。

IdSMTP1.QuickSend('mets.tcimet.net', 'Reminder',

'[email protected]', '[email protected]',

'Buy Building Delphi 6 Applications');

还有其他几种协议是在TCP/IP上实现的,当然也可以另行实现自己的协议,或子类化并扩展Internet Direct组件来得到自定义接口。关于使用Delphi进行TCP/IP编程的进一步信息,请阅读Andrew Wozniewicz的《Web Programming with Delphi》一书,关于TCP/IP协议的一般性信息,可以阅读John Ray的《Special Edition Using TCP/IP》。

16.6  小  结

本章涵盖了若干种不同的协议,它们都是基于TCP/IP的。TCP/IP并非联网的惟一手段,但它是一种跨越Internet、内部网和外部网连接计算机的可靠手段。本章中,您已经学到了如何使用一些新的来自Nevrona公司的Internet Direct组件;并测试了FTP、UDP、Telnet、TCP、POP3和SMTP组件。还有其他许多新的组件可用于创建客户和服务器程序。

下一章我们将继续讨论Web编程,使用一些协议来建立Web服务器和基于Web的应用程序。

 

相关阅读 更多 +
排行榜 更多 +
谷歌卫星地图免费版下载

谷歌卫星地图免费版下载

生活实用 下载
谷歌卫星地图免费版下载

谷歌卫星地图免费版下载

生活实用 下载
kingsofpool官方正版下载

kingsofpool官方正版下载

赛车竞速 下载