Translate

2009/11/25

Upgrading to Indy 10 (從Indy9升級到Indy10)

Upgrading to Indy 10

Abstract: This session covers the changes made to Indy 10 and how to quickly port existing code. It also covers the advantages of Indy 10 and why you should port your code. While performance and other demonstrations are shown, this session is a practical overview and does not focus on demo code.

Chad Hower works for Atozed Software, and is the original author of Indy and IntraWeb. When not programming, he likes to bike, kayak, ski, drive, or just be outdoors. Chad is an ex-patriate who spends his summers in St. Petersburg, Russia, winters in Cyprus, and travels extensively year round.

Upgrading to Indy 10

Indy 10 contains many new features, especially relating to the portion referred to as super core. Indy 10 core has been further abstracted for easier expansion. Indy 10 core also contains many new capabilities, and performance enhancements.

Indy 10 Status

Indy 10 at this time still under development however it will likely be released near BorCon 2004. At the time of this writing Indy is still officially beta, however it has become rather stable and nearly all changes are bug fixes. Several companies including Atozed Software are now using Indy 10 in all production code and are no longer using Indy 9.

While the SuperCore package is still under heavy construction and is not stable, the system, core, and protocol packages have settled down and are fairly stable. Since SuperCore is purely optional, it will not affect the stability of your applications unless you use SuperCore. The consistency of the code may be likened to that of frozen Jell-O.

Installing Concurrent Versions

You may find it desirable to have more than one version of Indy installed on your system concurrently. At last count I had 8 separate versions of Indy installed (including minor versions), but I do not recommend this to the average user. As an average user you may want 2 versions, Indy 9 and Indy 10.

This is possible with some minimal side effects. To do Install both Indy 9 and Indy 10 concurrently:

3. For each application that uses Indy, explicitly add the path for Indy 9 or Indy 10 to the search path in the project's options (Project, Options, Directories / Conditionals Tab).

Only one version of Indy can be installed at a given time on the palette. Normally I keep 10 installed on my palette and use the above technique to allow me to continue to compile applications written in 9. However some properties have changed and this can cause problems with editing / debugging of Indy 9 based applications, although I can still compile and edit source. If I must edit a DFM containing an Indy 9 component I quickly change the installed package.

If you are working primarily with one version or another with only occasional use of the other version, the above technique will work quite well and you can recompile the Indy packages on demand if necessary.

If you are not working primarily with one version you can have the binaries of the packages and the .dcp files reside in the specific Indy version directories and adjust which actual package is loaded by using the Packages tab in Project Options. By controlling the packages in Project Options you can control which packages each individual project uses.

Compatibility

Indy 10 is not purely interface compatible with Indy 9 and most existing code will not compile without some adjustments. With the added support of the .NET platform, as well as new features implemented in SuperCore, changes have been necessary.

The core of Indy 10 has undergone massive structural changes. This will break some core code of users, but efforts have been made to preserve as much protocol and application level compatibility as possible. At times it may that seem the Indy Pit Crew takes no notice about the impact of changes on end users. However this is not true. Each interface change is evaluated, and the benefits are weighed against the disadvantages. Changes that are made, are designed in a manner so as to permit easy upgrading of existing source code with minimal effort. The Indy Pit Crew uses Indy in both private and commercial offerings, so each change is felt by the team as well.

It is the belief of the Indy Team that progress is not without some sacrifice. Through small changes to interfaces, large gains can be achieved and this a better Indy. Without such changes, the future directions are hindered, and vestigial baggage is retained. Just as Winshoes to Indy 8, and then Indy 8 to Indy 9 increased abstraction, so will Indy 10.

One of Indy's primary tenets is that it is easy to program and that it is based on a blocking model. This allows for easy development and user code does not suffer from serialization. Indy's design goal is to be easy to use, and fast enough for 95% of the end user's needs.

However in very large installations this causes excessive context switching and accumulated high thread overhead. These limitations appear only in large installations, at around 2,000 concurrent threads in a well designed application. In most applications limitations in user code appear long before the limitations of Indy.

Traditionally to serve the remaining 5% of the users, easily written code must be abandoned for complex and hard to maintain code such as direct overlapped I/O, I/O completion ports, or highly divided work units using threads against non blocking sockets. Indy 10 preserves Indy's easy to use blocking model, while making performance increases internally. Indy 10 does this by using advanced network interfaces and efficiently translating such back to a user friendly blocking model. Indy 10 thus is intended to serve 99.9% of the community's needs, with only a very small minority still requiring custom code for very unusual situations.

Indy 10 achieves this in many ways, but fibers are the keystone to this achievement. A fiber is very similar to a thread, but it is more flexible, and has lower overhead than a thread if used properly.

Most of the changes have affected Core, which means that users who solely use the protocol components will see very few changes. The developers who will feel the largest impact are the users directly using core components such as TIdTCPClient, and especially TIdTCPServer.

Migration

If you upgrade to Indy 10 now, it will be necessary for you to perform various manual conversions. The amount of work varies, but impacts the users of TIdTCPServer the most.

The Indy Documentation team is working on a comprehensive migration guide that will be released at a later time. In addition, the Indy team is also performing an updated impact analysis and evaluating the possibility of custom property readers as well as conversion utilities.

A custom property reader would upgrade DFM's automatically as each DFM is loaded into the IDE and convert the appropriate properties to Indy 10.

A conversion utility would scan DFM's and / or source files and make as many conversions as possible.

Major Changes and Additions

This section will attempt to cover and detail the major changes and additions in Indy 10. It is not a complete list of every change and addition.

.NET Support

Indy 10 suports the .NET platform by using the Delphi for .NET compiler. Using Delphi for .NET, Indy not only supports Delphi 8 and 9, but now can be used by C# and Visual Basic.NET users as well. Delphi users can use source code directly, while a precompiled assembly is available for other .NET users.

Only System, Core and Protocols are supported for .NET. SuperCore is not sceduled in the near future to be ported. In addition, not all of the protocols have been ported to .NET, but most of the important ones have and more and more are being ported every month. At the time of writing, IMAP4 server, raw clients, and a few of the lesser used protocols still have to be ported.

Separation of Packages

Indy 10 has been separated into four packages:

7System

7Core

7Protocols

7SuperCore

The Core package (IndyCore.dpk) contains all of the core pieces, base client components, and base server components. Core does not implement any higher level protocols.

The SuperCore package (IndySuperCore.dpk) contains the new features for scalable servers and massively parallel clients.

Indy 10 Packages

This allows the Indy Pit Crew to better focus on specific parts. It also benefits users who are implementing custom protocols and may not need the protocols package.

Indy 10 Package Structure

The packages have been built such that Core can operate independently from the other packages. Both Protocols and SuperCore require core, but neither Protocols or SuperCore requires each other. They can however be used in conjunction allowing SuperCore features to be used with Protocols. All packages rely on System.

IOHandler Restructuring

To provide performance enhancements the IOHandlers in Indy have been restructured and given a more important role. Previously the role of an IOHandler was to do only very basic I/O consisting of only the following functions:

7Open (Connect)

7Close (Disconnect)

7Read raw data

7Write raw data

7Check connection status

This role allowed for alternate IOHandlers to be created that performed the I/O from sources other than a socket. Even the default socket I/O was implemented using a default IOHandler.

Because the functionality was minimal, implementing IOHandlers was very straightforward. However this also often caused the IOHandlers to perform their I/O with less efficient methods. For example, an I/O handler may be receiving data from a local file to write, but it would have no way of knowing such because it only has one write method for all data. Even if the I/O handler had the capability of a high performance file read, it could not use it.

Indy's IOHandlers now implement not only the minimal low level methods, but high level methods as well. Such higher level methods were previously implemented by TIdTCPConnection.

IOHandlers can be created as before by implementing only the low level methods. The base IOHandler contains default implementations of the higher level methods which use the lower level methods. However each IOHandler can override additional higher level methods to provide optimized implementations specific to the IOHandler.

Method Signatures

Raw read and write methods were moved from the TIdTCPConnection to the IOHandler. However most of them underwent signature or even method name changes as well. The most notable of these are the methods for writing data to the connection.

In Indy 9 methods for writing existed as Write, WriteInteger, WriteStream, etc. In Indy 10, most of the Write methods have simply been renamed to Write and overloaded. So whether you have a string, integer, or stream, one method named Write handles them all.

TCP Server

TIdTCPServer has received the largest impact of the changes in Indy 10. While efforts are planned in the future to provide for automatic conversion, there will remain some changes that are necessary to be performed manually. This section will cover the major changes independent of any conversions that may be performed automatically in the future.

Server Split

In Indy 9, TIdTCPServer contained all the necessary code and properties for command handlers. In Indy 10, the command handler functionality has been separated into a separate component named TIdCmdTCPServer which descends from TIdTCPServer.

This allows those implementing binary servers and other servers not requiring command handlers to be implemented on the base TCP server. This also allows for future expansion of TIdCmdTCPServer without adding extra baggage to TIdTCPServer.

TCP Server Split

In essence, the old TIdTCPServer is now TIdCmdTCPServer, and the new TIdTCPServer is a subset. This introduces a DFM problem however when old projects are loaded. The IDE will try to load the components by name. In Indy 10 this causes a the new TIdTCPServer to be loaded with less properties.

If you are not using command handlers you can click "Ignore All" when the IDE prompts you to upgrade the DFM.

If you are using command handlers, you need to manually edit the DFM and change the class type to TIdCmdTCPServer.

DFM Breakage

In addition to the server split, some properties have changed that cause DFM incompatibility. The primary property change is the sub properties of TIdReply which is used in the TCP Server's .Greeting and other properties.

Formerly the reply class contained both a NumericCode and TextCode property to accomodate the non standard POP3 protocol. In Indy 10, both of these properties have been consolidated into a single Code property and are handled by polymorphism for each separate protocol type.

However when the IDE loads a TIdTCPServer, for each instance of a reply it will be unable to find NumericCode or TextCode. You can manually edit the DFM and change each to Code, or use "Ignore All". If you used replies however, "Ignore All" will lose any values stored previously. If you did not use the reply properties, "Ignore All" is an option as it will be just ignoring default values which will be restored.

In the future it is planned to create custom readers to convert the older values.

Raw Reads and Writes

All raw reads and writes including Read, ReadLn, Write, WriteLn, etc are now part of the IOHandler instead of the TCPConnection.

This change was performed for two reasons.

The first reason is to isolate the raw I/O from the protocol descendants so that protocol component users only have access to the protocol methods. That is, WriteLn is normally not used by users of the HTTP client. It is only useful to such users if they are adding custom code to existing functionality. In such cases, it is still available but is now not available directly from the HTTP client. It must be referenced from the HTTP client's IOHandler property.

The second reason is one of polymorphism. When the raw I/O methods were part of the connection they could not be easily reimplemented by different IOHandlers except at a low level. Moving higher level methods into the IOHandlers allows better efficiency. For more information, see IOHandler Restructuring.

In most cases updating source code is easy. Just insert IOHandler. in front of the I/O call.

Adjustment for Server Code

Clients are adjusted the same, but do not have the Connection object, the clients are the connection object. In many TCP servers the I/O code is wrapped in a with statement. Thus the change only needs to be made in one place.

New Event Sigature

Since most server events were threaded, an argument (AThread) was passed in to specify that thread. This included OnConnect, OnExecute and others.

In Indy 9, an example OnExecute looked like this:

procedure TformMain.IdTCPServer1Execute(AThread: TIdPeerThread);

var

i: Integer;

begin

with AThread.Connection do begin

for i := 1to15dobegin

WriteLn(IntToStr(i));

end;

Disconnect;

end;

end;

In Indy 10, the code must be adjusted as follows. The changes are highlighted.

procedure TformMain.IdTCPServer1Execute(AContext: TIdContext);

var

i: Integer;

begin

with AContext.Connection.IOHandlerdobegin

for i := 1to15dobegin

WriteLn(IntToStr(i));

end;

Disconnect;

end;

end;

In summary, AThread is changed to AContext in most cases, and IOHandler is inserted before raw I/O methods.

The events are still executing in a thread. In Indy 9, each connection was assigned to one thread. In Indy 10 in some cases it is possible that a single connection will be handled at different times by different threads. For example, the OnConnect may be called from on thread, the OnExecute from another, and the OnExecute yet another.

Contexts

In Indy 9, connection information could be stored in AThread.Data, or in fields added to a custom descendant of TIdPeerThread. Because Indy 10 does not assign a unique thread to each connection, this storage association is no longer available and therefore had to be separated from the thread class.

Instead Indy 10 provides a context in the form of TIdContext. TIdContext has a .Data property just as TIdPeerThread did. You can also create custom descendants of TIdContext and tell the server to use it instead by setting the ContextClass property.

The context that is passed into each event is uniquely identified and associated with a specific connection. The context does not represent the connection itself, but only session information related to the connection. Indy manages the association automatically.

To migrate such code to Indy 10, simply change your reference or ancestor class to TIdContext.

Schedulers

TCP Servers in Indy 9 had a ThreadMgr property to allow specification of an alternate thread manager such as the TIdThreadMgrPool component which offered thread pooling.

This property has been eliminated and replaced by the Scheduler property. The scheduler property still allows use of the thread managers, but also allows for advanced schedulers in SuperCore for scheduling fibers.

SSL

SSL Core

The SSL capabilities of Indy 10 are now completely pluggable. Prior to Indy 10, the SSL support was pluggable at the TCP level, however protocols such as HTTP which used SSL for HTTPS were fixed to use Indy's default SSL implementation of OpenSSL.

Indy 10 continues to include support for OpenSSL, however the SSL capabilities of Indy are completely pluggable at the core and protocol level for other implementations.

SSL and other encryption methods are in the works from both the third parties SecureBlackbox and StreamSec.

SSL Protocols

Indy 10 now supports both implicit TLS and explicit TLS in the following clients and servers:

7POP3

7SMTP

7FTP

7NNTP

7IMAP4

SASL

The SASL support code has been redesigned so it is usable with POP3 and IMAP4. Indy now supports anonymous SASL, plain SASL, OTP (one-time-only password system) SASL, external SASL, and Auth Login.

FTP

FTP Client

The FTP Client has been expanded in the following ways:

7MLST and MLSD are now supported. This provides a standard FTP directory list format that is easily parsed.

7A special combine command has been added for multipart upload support. Note that this does require a server that supports the COMB command such as GlobalScape Secure FTP Server or the server component in Indy 10.

7A XCRC command has been added for obtaining the CRC of files. Note that this does require a server that supports the COMB command such as GlobalScape Secure FTP Server or the server component in Indy 10.

7The client now supports the MDTM command for obtaining the last modified date

7OTP (One-Time-Only password) calculator built in and OTP now automatically detected

7FTPX or Site to site file fransfer (where a file is transfered between two servers) is now supported. Note that FTPX transfers will only work if the server supports it (many administrators and developers now disable this capability for security reasons).

7A virtual system component can now be used to make FTP Server much easier.

7FTP specific IP6 extensions have been added.

7Implicit and explicit SSL support.

Also FTPX site to site transfers capability can now be disabled. This is done for security reasons as the Port and PASV commands are subject to abuses as described by:

7http://www.cert.org/tech_tips/ftp_port_attacks.html

7http://www.kb.cert.org/vuls/id/2558

7http://www.cert.org/advisories/CA-1997-27.html

7http://www.geocities.com/SiliconValley/1947/Ftpbounc.htm

7http://cr.yp.to/ftp/security.html

FTP List Parsing

Indy 10 contains a plug in FTP list parsing system with provided parsers for nearly every FTP server type in existence, and even a few which are probably no longer functioning.

If the chance arises that an unsupported system is encoutnered, a custom handler can be used.

FTP servers supported:

7Bull GCOS 7 or Bull DPS 7000

7Bull GCOS 8 or Bull DPS 9000/TA200

7Cisco IOS

7Distinct FTP Server

7EPLF (Easily Parsed List Format)

7HellSoft FTP Server for Novell Netware 3 and 4

7HP 3000 or MPE/iX including HP 3000 with Posix

7IBM AS/400, OS/400

7IBM MVS, OS/390, z/OS

7IBM OS/2

7IBM VM, z/VM

7IBM VSE

7KA9Q or NOS

7Microware OS-9

7Music (Multi-User System for Interactive Computing)

7NCSA FTP Server for MS-DOS (CUTCP)

7Novell Netware

7Novell Netware Print Services for UNIX

7TOPS20

7UniTree

7VMS or VMS (including Multinet, MadGoat, UCX)

7Wind River VxWorks

7WinQVT/Net 3.98.15

7Xecom MicroRTOS

Super Core

SuperCore is a new high performance model for implementing high load servers, or massively parallel clients.

The SuperCore package contains advanced functionality and will be not fully complete in the first Indy 10 release. SuperCore is designed to handle very heavily loaded servers with thousands of connections, or massively parallel clients in which thousands of clients must be created and used concurrently.

SuperCore also implements Fibers, Fiber Schedulers, and IOCP (I/O Completion Ports). These components while required for the overall function of SuperCore, can also be used independently by developers.

SuperCore also creates additional overhead and should only be used when such scalability is required. With smaller and medium size projects (Most projects fall into one of these two categories), using SuperCore can actually slow down your application. This is because SuperCore is a bit like a Boeing 747, while without SuperCore Indy is like a smaller commuter plane. If you are flying a short distance and only have 30 passengers, the economics of a 747 do not make sense. A commuter plane is perfect. However if you have 300 passengers to go from Pittsburgh to London, a 747 makes perfect sense. SuperCore versus Core is a bit like this.

Currently SuperCore also requires:

7Windows 2000, 2003, or XP

7Delphi 7 (Delphi 9 soon)

Windows 95, 98, and ME are not supported. Not only do they not contain the necessary support functions in all areas, but they are not designed for hosting servers and would negate any benefits provided by SuperCore. Any port to these operating systems would require extra effort and would be of use only academically.

Only Delphi 7 is supported because of changes in the VCL between versions. It is possible to support Delphi 6 and even Delphi 5, however this significantly increases testing as critical parts of the VCL vary. At this time there is no plans to support Delphi 5 or 6 because of such maintenance issues.

Network Interfaces

Indy previously implemented only one network interface. On Windows this interface was Winsock, and on Linux the BSD socket interface. Indy still implements these interfaces but also implements more efficient optional interfaces under Windows. At this time no other interfaces have been implemented under Linux, but may be in the future. The need is less urgent under Linux because its network implementation.

Some of the additional interfaces are not available under all versions of Windows and should be used only in server implementations, or systems with a very large number of client connections. There is no need for such implementations in normal client applications. The additional network interface is IOCP (I/O Completion Ports).

Normally to use IOCP, complicated and highly unintuitive code must be written. However with Indy, just as before, Indy takes care of all these details and presents the developer with a friendly easy to use interface.

Fibers

In addition to expanding thread support, Indy now contains support for fibers. What is a fiber?

In short, its a "Thread" but one that is controlled by code - not the operating system. In fact, a thread may be thought of as an advanced fiber. Fibers are similar to user threads in Unix.

A thread is a basic unit that the operating system allocates time to. A thread contains its own stack, certain processor registers, and a thread context. Threads are automatically scheduled by the operating system.

In general, fibers do not provide advantages over a well-designed multi threaded application. However fibers combined with an intelligent scheduler that has pertinent information available to it significantly increase performance through large efficiency gains.

Multiple fibers can be run using a single thread. A single fiber can be run by multiple threads, although only one at thread at a time can run a single fiber. You can run multiple fibers inside. The code to perform this can be quite complex, but Indy handles all this for you. All Indy components, both clients and servers support fibers, and in a transparent manner.

By using fibers, Indy is also able to translate the complex and unfriendly lower layer network interfaces into a developer friendly interface.

Fibers have been implemented as part of SuperCore and in Windows only at this time.

Schedulers

Indy's fiber weaver is a scheduler which schedules fibers onto one or more threads. Fibers deposit work items into a work queue and then wait. When a fiber's work item has been completed, the scheduler puts the fiber in a list of fibers that can be scheduled.

Operating system schedulers schedule threads in an intelligent manner, however they have limited information about threads as each thread is common and generic among all tasks in a system. Operating system schedulers can only schedule based on the wait state, and priority of a thread.

Indy's fiber weaver (fiber scheduler) uses advanced information that is specific to the task to determine scheduling needs, priorities and wait states. By doing so Indy is able to drastically reduce the number of context switches that occur relative to the work completed. This results in significant performance gains.

A context switch is when one thread is suspended and another is scheduled. To perform this the operating system must preemptively interrupt the execution path of the processor and save the context of the thread by storing several processor registers in memory. It must then restore another thread by loading processor registers from another memory location, and resume the thread's execution path.

Thread schedulers must balance the need to reduce context switching, with the need to make sure each thread receives enough processor time. Switching to often increases overhead and cause overhead to increase beyond any benefits. Switching not often enough causes unnecessary inter thread wait dependencies, and sluggish responses because threads are not receiving enough processor time.

To manage this, operating systems define a quantum, or a maximum time slice that a thread may receive processor time per switch. In most cases a thread will yield prior to this time being reached by entering a wait state. Wait states occur explicitly, or more commonly implicitly by making an operating system call, or I/O call which cannot be completed immediately. When such a call occurs, the operating system accepts the yield and switches to another thread. If the thread is waiting on I/O or some other blocking operation it will be placed in a wait state and not considered for scheduling until the requested operation has completed, or timed out.

Indy's fiber weavers work in a similar fashion, but determine wait states at a much higher level using a wider range of information. Fibers can be determined before hand to be in wait states without the need to context switch to them and wait for them to enter a wait state. Indy also divides the work between fibers and chain engines which now perform much of the low level work.

The division of labor allows for more efficient network interfaces to be used such as I/O completion ports. I/O completion ports are more efficient because they run at a level closer to the hardware interface. Winsock and other calls which are farther from the hardware interface layer must communicate with the kernel to perform the actual call to the hardware interface. Calls which communicate with the kernel must undergo context switching to do so. Thus each Winsock call often involves many unnecessary context switches just to perform its function.

Schedulers can use a single thread, multiple threads, threads on demand, or even a thread pool.

Fiber Weaver Using Single Thread

Fiber Weaver Using Multiple Threads

Work Queues

Work queues are first in first out (FIFO) queues which hold the work items requested by fibers. Most of this functionality is completely transparent to the average developer and Indy uses this functionality internally.

Chains

The system of work queues, schedulers, and chain engines in Indy are referred to as chains. While chains are used by Indy, they are not limited to Indy's internal use and have end user applications as well.

When chains are used, a chain based IOHandler deposits work items into the associated work queue. The fiber is then suspended until a unit of work for it has been completed. This is because the fiber can do nothing until the result of some work is ready to be processed. Each IOHandler method is reduced to one more or more work tasks. For best performance, each method should resolve to as few specialized work tasks as possible.

A scheduler then manages the fibers while they wait for their work items to be processed. Finally, a chain engine processes the requests in the work queue and communicates with the schedulers.

Chain Structure

Chain Engines

A chain engine is the lowest level of the chain system. The chain engine performs all of the actual input and output. The chain engine may consist of a single thread, or several.

The job of a chain engine is to extract tasks from a work queue and complete the tasks. Upon completion of each task, the chain engine notifies the scheduler and the scheduler evaluates which fiber should be considered for scheduling. The chain engine then moves on to the next task in the work queue.

If there are no items in the work queue, the chain engine remains idle.

Multiple chain engine types can be implemented to implement I/O completion ports, Winsock, Overlapped I/O, or other. Indy currently only implements IOCP. While Winsock is possible and was used early on for testing, it is slow and is only of academic use.

SuperCore Revealed

Other

Other notable changes and improvements to Indy 10 include but are not limited to:

7Threads can now be named.

7Server intercepts have been added permitting you to log the FTP server and they work similarly to the client intercepts.

7Systat UDP and TCP client and servers have been added.

7A DNS server component has been added.

7HTTP connect through proxy support has been added.

7TIdIPAddrMon has been added for monitoring all IP addresses and all network adaptors

7IP6 support

7A One-Time-Only password system has been implmeneted as both an OTP calculator for the clients and as a user manager component. This supports MD4, MD5, and SHA1 hashes.

Sample Conversions

So far the details of changes have been covered. But what is actually involved in porting an existing application? To see, I will take two Indy 9 demos, one client and one server, and port them to Indy 10.

SendMail

The client demo I chose to test is the SendMail demo available at the Atozed Indy Demo Playground. Two design time errors were encountered upon loading the datamodule.

MaxLineAction Property

The MaxLineAction property which formerly existed in TIdTCPConnection has now been moved to the IOHandler. Because of this it no longer exists in any TCP client as TIdTCPConnection is its ancestor. If you are like most users, you were not using this property and you can simply click Ignore. If you were using this property, you will need to set it in the IOHandler.

AuthenticationType Property

The AuthenticationType property no longer exists. The AuthenticationType property was specific to SMTP client. All authentication functionality has been moved into separate pluggable components. To use an authentication type other than basic, you will need to use the authentication components on the component palette. Two new properties in SMTP client also exist: AuthType and SASLMechanisms. AuthType was renamed as its values changed and have different meanings now.

Unless you were using authentication, and the authentication was a type other than basic, you can simply click ignore.

Ignore All

Most properties that were removed or altered in TCP clients were of advanced nature. In most cases you can simply click Ignore. You should not click Ignore All however. Instead you should click Ignore for each one and determine if it is a property that is vital to your application and then determine the new method of implementing that functionality.

All TCP clients share the MaxLineAction property change. Some clients only have this change, while others have component specific changes such as SMTP client did with its AuthenticationType.

Code Changes

No source code changes were required to compile the SendMail demo once the design time changes were completed. Many protocol clients can be compiled with no changes, while some require some changes if more advanced functionality is used. Users of the base TCP client will need to adjust all the read and write methods to both use the new signatures, but also to call them from the IOHandler.

Since most users use the base functionality and use protocol components, the porting of client applications from Indy 9 to Indy 10 is generally quite easy.

TCP Server

Base TCP servers require the most changes to be ported to Indy 10 and why I have chosen an base TCP server demo for this example. As with protocol clients, most protocol servers require less changes than base TCP servers. The demo that I ported to Indy 10 is the OnExecute version of the Post Code lookup demo. This demo is available at the Atozed Indy Demo Playground.

Indy 10 also includes new features and enhancements to command handlers. During this port I did not change any of the functionality, but simply performed the changes to compile in Indy 10 using the same functionality.

Command Handlers

These properties still exist, but have been moved to the new TIdCmdTCPServer class. Since this application does not use command handlers you can simply click Ignore. In applications which use command handlers the DFM should be opened in text mode and the class type changed to TIdCmdTCPServer.

Event Signatures

When the application is compiled or saved the following errors will appear.

As discussed previously, the events of TCP servers have changed. Instead of passing a thread, they now pass a context. The easiest way to fix these changes is to cut the old events and place them in notepad. Create a new event using the form editor, and then paste the contents into each of the new events. This will allow Delphi to fix up the form headers, as well as the DFM for you.

The new events accept a TIdContext as an argument and thus the IdContext unit will need to be added to the interface uses clause.

interface

uses

Graphics, Controls, Forms,

Classes,

IdBaseComponent, IdComponent, IdTCPServer,

IdContext,

SysUtils;

Source Code Changes

The final step is to compile the application and determine what is broken. The Post Code Server has two server events, OnConnect and OnExecute. I have included both of them below. The first example is the Indy 9 version, and the second is the changed Indy 10 version with the changes highlighted in red.

Indy 9 Source

procedure TformMain.IdTCPServer1Connect(AThread: TIdPeerThread);

begin

AThread.Connection.WriteLn('204 Post Code Server Ready.');

AThread.Data := TUserData.Create;

end;

Indy 10 Source

procedure TformMain.IdTCPServer1Connect(AContext: TIdContext);

begin

AContext.Connection.IOHandler.WriteLn('204 Post Code Server Ready.');

AContext.Data := TUserData.Create;

end;

Indy 9 Source

procedure TformMain.IdTCPServer1Execute(AThread: TIdPeerThread);

var

LCmd: string;

LCity: string;

LLine: string;

LPostCode: string;

begin

with AThread.Connection dotry

LLine := ReadLn;

LCmd := Fetch(LLine);

if AnsiSameText(LCmd, 'QUIT') thenbegin

WriteLn('201-Paka!');

WriteLn('201 ' + IntToStr(TUserData(AThread.Data).FRequestCount)

+ ' requests processed.');

Disconnect;

endelseif AnsiSameText(LCmd, 'Lookup') thenbegin

WriteLn('200 Ok');

LPostCode := Fetch(LLine);

while LPostCode <> ''dobegin

LCity := FPostCodeList.Values[LPostCode];

if LCity <> ''thenbegin

Inc(TUserData(AThread.Data).FRequestCount);

WriteLn(LPostCode + ': ' + LCity);

end;

LPostCode := Fetch(LLine);

end;

WriteLn('.');

endelseif AnsiSameText(LCmd, 'Help') thenbegin

WriteLn('100 Legal Commands');

WriteLn('Help');

WriteLn(' Provides a list of supported command.');

WriteLn('Lookup ...');

WriteLn(' Looks up and provides associated cities for specified postal'

+ ' codes.');

WriteLn('Quit');

WriteLn(' Signals server that a disconnect is reqeusted.');

WriteLn('.');

endelsebegin

WriteLn('401 Unrecognized command.');

end;

except

on E: Exception dobegin

WriteLn('500 ' + StringReplace(E.Message, EOL, ' ', [rfReplaceAll]));

raise;

end;

end;

end;

Indy 10 Source

procedure TformMain.IdTCPServer1Execute(AContext: TIdContext);

var

LCmd: string;

LCity: string;

LLine: string;

LPostCode: string;

begin

withAContext.Connection.IOHandlerdotry

LLine := ReadLn;

LCmd := Fetch(LLine);

if AnsiSameText(LCmd, 'QUIT') thenbegin

WriteLn('201-Paka!');

WriteLn('201 ' + IntToStr(TUserData(AContext.Data).FRequestCount)

+ ' requests processed.');

AContext.Connection.Disconnect;

endelseif AnsiSameText(LCmd, 'Lookup') thenbegin

WriteLn('200 Ok');

LPostCode := Fetch(LLine);

while LPostCode <> ''dobegin

LCity := FPostCodeList.Values[LPostCode];

if LCity <> ''thenbegin

Inc(TUserData(AContext.Data).FRequestCount);

WriteLn(LPostCode + ': ' + LCity);

end;

LPostCode := Fetch(LLine);

end;

WriteLn('.');

endelseif AnsiSameText(LCmd, 'Help') thenbegin

WriteLn('100 Legal Commands');

WriteLn('Help');

WriteLn(' Provides a list of supported command.');

WriteLn('Lookup ...');

WriteLn(' Looks up and provides associated cities for specified postal'

+ ' codes.');

WriteLn('Quit');

WriteLn(' Signals server that a disconnect is reqeusted.');

WriteLn('.');

endelsebegin

WriteLn('401 Unrecognized command.');

end;

except

on E: Exception dobegin

WriteLn('500 ' + StringReplace(E.Message, EOL, ' ', [rfReplaceAll]));

raise;

end;

end;

end;

About the Author

Chad Z. Hower, a.k.a. Kudzu

"Programming is an art form that fights back"

Chad works for Atozed Software, and is the original author of both Internet Direct (Indy) and IntraWeb. Both Indy and IntraWeb have been licensed by Borland for inclusion in Delphi, Kylix and C++ Builder. Chad speaks at 6- 8 conferences each year in Europe and North America, writes frequently for developer magazines, and also posts free articles, programs, utilities and other oddities at Kudzu World.

Chad's background includes work in the employment, security, chemical, energy, trading, telecommunications, wireless, and insurance industries. Chad's area of specialty is TCP/IP networking and programming, inter-process communication, distributed computing, Internet protocols, and object-oriented programming. When not programming, he likes to bike, kayak, ski, drive, and do just about anything outdoors.

Chad is an ex-patriate who spends his summers in St. Petersburg, Russia, winters in Limassol, Cyprus, and travels extensively year round.