This document explains how to use the Socket Set product from Totally Objects for IBM's VisualAge for Smalltalk. The product provides tools that enable you to add TCP/IP capabilities to your applications without getting tied down with the complexities of sockets programming. Some high level tools are included to do everyday tasks such as sending and receiving email (SMTP + POP3 with attachments) or uploading and downloading files (FTP).
The most valuable aspect of the product is the ease in which servers can be built. You specify a port to which the server should listen and a block to be executed for each client that connects: the threads are managed for you so the connections appear asynchronous.
This Totally Objects product has been packaged as three configuration maps. To import them into your library select 'Browse Configuration Maps' from the 'Tools' menu of the 'System Transcript'. In the 'Configuration Maps Browser' select 'Import...' from the 'Names' menu and select the file tobss5-5_1-0-*.dat. You should then select the three configuration maps contained within this file:
To load the configuration maps into your image select each of them in the 'Configuration Maps Browser' select 'Load With Required Maps' from the 'Editions' menu. You must first, however, load the application 'SocketCommunicationsInterface' (and its prerequisites) - these are part of VAST.
If you are using the Parts interface to Socket Set you will need to copy the directory 'tobsock' (which contains icons) into your root VAST directory.
You then need to specify the address (comprising a host and port number) you wish to connect to. You use the TobSocketsAddress class to do this. For example:
The client side to the TCP/IP protocols in the Socket Set are subclasses of TobSocketsProtocol. These subclasses can be instantiated using #new or #statusStream: (which requires a WriteStream as input to which status messages would be put).
|
Method |
Description |
|
sendersEmailAddress: |
The email address of the sender (a String) |
|
sendersName: |
The name of the sender (a String) |
|
subject: |
The subject of the message (a String) |
|
messageText: |
The contents of the message itself (a String - 7bit ASCII) |
To specify the recipients (or recipients of copies) use these methods:
|
Method |
Description |
|
addRecipient: |
Adds a recipient (a TobSocketsMailRecipient) |
|
addRecipient:emailAddress: |
Adds a recipient using name and email address (both Strings) |
|
addCopyRecipient: |
Adds a recipient of a copy (a TobSocketsMailRecipient) |
|
addCopyRecipient:emailAddress: |
Adds a recipient of a copy using name and email address (both Strings) |
|
addBlindCopyRecipient: |
Adds a recipient of a blind copy (a TobSocketsMailRecipient). |
|
addBlindCopyRecipient:emailAddress: |
Adds a recipient of a blind copy using name and email address (both Strings) |
To send the message use TobSocketsSMTP>>#mail:address: giving the message and the address as arguments.
For example:
To send any items in the header of the message (in addition to those automatically created) use TobSocketsSMTPMailMessage>>#extraHeaderItems: and give it a Dictionary where the keys are the header item names (e.g. 'DigitalCertificate:') and the values are the header item values.
To find out how successful the sending was (assuming no error occurs) you can send the resulting TobSocketsSMTP object the message #statusOfRecipients. This will answer a Dictionary with the recipients (or recipients of copies) as keys and TobSocketsSMTPRecipientStatus objects as values.
To add attachments use these methods (in TobSocketsSMTPMailMessage):
|
Method |
Description |
|
attachTextStream:name: |
Expects a ReadStream and String as arguments. The contents of the stream will be attached as a text (7-bit ASCII) and assigned the given name. |
|
attachApplicationDataStream:name:applicationName: |
Expects a ReadStream and Strings as arguments. The contents of the stream will be attached as binary (encoded using base64) and assigned the given name. The application name corresponds to a registered MIME application that should be used by the client to open the attachment. |
|
addAttachment: |
This should be used when the above methods do not provide the flexibility you require. The argument is an instance of TobSocketsSMTPAttachment (described below). |
TobSocketsSMTPAttachments have four basic attributes: The contents, the encoding to be used, the type and the subtype. The Internet Request For Comments 1341 should be consulted for a definitive explanation of the acceptable values. The following methods should be used to set these attributes:
|
Method |
Description |
|
type: |
Requires a String. Valid values are 'application', 'audio', 'image', 'message', 'multipart', 'text', 'video' or any String prefixed with 'x-'. |
|
subType: |
Requires a String. Valid values depend on the 'type' specified. |
|
applicationType: |
Sets the type to 'application' and the subType to the argument provided. |
|
audioType: |
Sets the type to 'audio' and the subType to the argument provided. |
|
imageType: |
Sets the type to 'image' and the subType to the argument provided. |
|
messageType |
Sets the type to 'message' and the subType to 'rfc822' (the only valid subType for thetype). |
|
multipartType |
Sets the type to 'multipart' and the subType to 'mixed'. |
|
textType: |
Sets the type to 'text' and the subType to the argument provided ('plain' or 'richtext'). |
|
videoType: |
Sets the type to 'video' and the subType to the argument provided. |
|
content:encoding: |
Sets the content of the attachment (a String). The second argument specifies which encoding has been applied to the content (not the encoding to be applied). Valid encodings are 'BASE64', 'QUOTED-PRINTABLE', '8BIT', '7BIT', 'BINARY' or a String prepended with 'X-'. For ASCII text '7BIT' should be used. |
|
base64EncodeContent: |
Sets the content of the attachment (a String) and specifies that base64 encoding should be applied. This is ideal for sending binaries. |
|
quotedPrintableEncodeContent: |
Sets the content of the attachment (a String) and specifies that Quoted-Printable encoding should be applied. This is ideal for formatted text. |
|
rawContent: |
Sets the content of the attachment (a String) and specifies that it should not be encoded. This is ideal for 7-bit ASCII. |
The following code snippet shows how you might send a binary executable and some binary application data as attachments.
Notice that the above code uses an alternative way to send email (by creating the message and using the method #mail:).
To use the POP3 protocol to access a mailbox an instance of TobSocketsPOP3 should be created and then configured using the methods:
So to download all messages from the server and delete them (so they are not downloaded again next time) the code would look something like this:
The answer to the above message will be an OrderedCollection of objects of the class TobSocketsPOP3MailMessage. These objects understand the following methods:
|
Method |
Description |
|
headedItems |
Gets an EsOrderedDictionary of the items in the messages header. |
|
messageText |
Gets a 7-bit ASCII String - the body of the message. |
|
decodedMessage |
Gets the body of the message as a String after the appropriate decoding algorithm has been applied. |
|
attachments |
Gets an OrderedCollection of TobSocketsPOP3Attachments (described below). |
|
addXDecoder:name: |
Specifies a decoding object (i.e. one that understands #decode: - taking a String and answering the decoded String) to be used for attachments that have an transfer encoding type corresponding to the second argument. These transfer encoding types must be prefixed with 'X-'. |
An incoming mail with attachments will answer an OrderedCollection of TobSocketsPOP3Attachments when sent the message #attachments. These objects understand the following methods:
|
Method |
Description |
|
content |
Gets the raw undecoded attachment content (a String). |
|
contentTransferEncoding |
Gets the name of the transfer encoding used (a String). |
|
type |
Gets the type of attachment (a String). |
|
contentTransferEncoding |
Gets the subType of attachment (a String). |
|
decodedContent |
Gets the content of the attachment after applying the appropriate decoding algorithm. |
|
headerItems |
Gets an EsOrderedDictionary of the items in the attachment's header. |
The method TobSocketsFTP>>#downloadFile:address:username:password:account:type:stream: has been provided for downloading files. It can be used as follows:
Similarly the method TobSocketsFTP>>#uploadFile:address:username:password:account:type:stream: has been provided for uploading files to servers. Two additional uploading methods can also be used. TobSocketsFTP>>#uploadAppendFile:address:username:password:account:type:stream: appends the contents of the stream onto the file on the server (or creates a new file if one does not already exist). TobSocketsFTP>>#uploadUniqueFile:address:username:password:account:type:stream: will use a different file name (chosen by the server) if the file already exists on the server.
Deleting or renaming files can be done with TobSocketsFTP>>#deleteFile:address:username:password:account: and TobSocketsFTP>>#renameFile:to:address:username:password:account:. For example:
To handle stalling during data transfer you can set the #stallTimeOut value of the TobSocketsFTP object. Whenever there is inactivity for a period either ExTobSocketsReceiveTimedOut or ExTobSocketsSendTimedOut will be signalled.
The default value is got from the class method defaultStallTimeOut which is initially 0 (i.e. ignore time-outs).
You can also download files using FTP by creating a URL. To create the URL object you can do either:
Or,
The resulting object (an instance of TobSocketsFTPURL) can be sent any of the following messages to download a file:
So to download the file given in the earlier example:
If you wish to use the Socket Set to implement the client end of your TCP/IP protocols then you have two options:
An example of option (2) is given below.
Say you have a TCP server (listening on port 9000) that will reverse the order of strings that you send it and responds with appropriate messages upon success or failure. The code might look something like this:
Methods in the TobSocketsSocket class that are of general use:
|
Method |
Description |
|
connect: |
Takes a TobSocketsAddress as an argument. If the connecting process takes longer than the value #connectTimeOut then the exception ExTobSocketsConnectTimedOut will be signalled. |
|
connectTimeOut: |
Sets the number of milliseconds after which a time-out will occur when using #connect:. Using the value 0 means 'No time-out'. |
|
connectLoopBlock: |
Sets a two argument Block (or nil) that will get executed repeatedly (using #value:value) while trying to connect (using #connect:). If the connection happens immediately or an error occurs the block may not be executed at all. The arguments sent to the block will be: The block should answer a Boolean. If 'false' then the connection will terminate with the exception ExTobSocketsConnectAborted. This facility is useful if you need to execute any code while trying to connect to a server (e.g. allowing a user to press a 'Cancel' button, animating a graphic, or allowing a user to abort a connection when there is no time-out). |
|
lineDelimiter: |
Set the String to use as a line delimiter for the #cr and #nextLine methods (default value is <CR><LF>) |
|
receiveStrings |
Collections received from the socket will be expressed as Strings. |
|
receiveByteArrays |
Collections received from the socket will be expressed as ByteArrays. |
|
peek: |
Peeks the specified number of characters from the socket. The answered collection may be shorter than the specified length if insufficient characters are ready to be peeked. If the peeking takes longer than the value #receiveTimeOut then the exception ExTobSocketsReceiveTimedOut will be signalled. |
|
peekExactly: |
The same as #peek: except the method will wait until there are sufficient characters to peek (but may still time-out). |
|
peek |
Peeks one Character or byte (depending whether the socket has been set to receive Strings or ByteArray) from the socket. |
|
peekNoTimeOut: |
The same as #peek: but ignores the time-out. |
|
peekNoTimeOut |
self peekNoTimeOut: 1 |
|
next: |
Receives the specified number of characters from the socket. The String answered may be shorter than the specified length if insufficient characters are ready to be received. |
|
nextExactly: |
The same as #next: except the method will wait until there are sufficient characters to receive. |
|
next |
Receives one Character or Byte (depending whether the socket has been set to receive Strings or ByteArray) from the socket. |
|
nextNoTimeOut: |
The same as #next: but ignores the time-out. |
|
nextNoTimeOut |
self nextNoTimeOut: 1 |
|
nextDatagram: |
Answers an instance of TobSocketsDatagram (must be a UDP socket). The argument indicates the size of the buffer to use for the data within the datagram. Use the #truncated method to see if the data within the datagram has been truncated. |
|
nextDatagramNoTimeOut: |
The same as #nextDatagram: but ignores the time-out. |
|
receiveTimeOut: |
Sets the number of milliseconds after which a time-out will occur when using some of the above methods. Using the value 0 means 'No time-out'. |
|
upTo: |
Receives up to the next occurance of the specified Character or byte and answers a collection of the received items (not including the value in the argument). If the receiving takes longer than the value #upToTimeOut then the exception ExTobSocketsUpToTimedOut will be signalled. |
|
upToNoTimeOut: |
The same as #upTo: but ignores the time-out. |
|
upToAll: |
The same as #upTo: except the receiving is up to the specified String or ByteArray. |
|
upToAllNoTimeOut: |
The same as #upToAll: but ignores the time-out. |
|
nextLine: |
Receives up to the lineDelimiter. |
|
nextLineNoTimeOut: |
Receives up to the lineDelimiter but ignores the time-out. |
|
upToTimeOut: |
Sets the number of milliseconds after which a time-out will occur when using some of the above methods. Using the value 0 means 'No time-out'. |
|
nextPutAll: |
Sends the specified String or ByteArray. If the sending takes longer than the value #sendTimeOut then the exception ExTobSocketsSendTimedOut will be signalled. |
|
nextPutAllNoTimeOut: |
The same as #nextPutAll: but ignores the time-out. |
|
nextPut: |
Sends the specified byte or Character. If the sending takes longer than the value #sendTimeOut then the exception ExTobSocketsSendTimedOut will be signalled. |
|
nextPutNoTimeOut: |
The same as #nextPut: but ignores the time-out. |
|
cr |
Sends the collection answered by the message #lineDelimiter. |
|
tab |
Sends a tab character (ASCII 9) |
|
space |
Sends a space character (ASCII 32) |
|
sendDatagram:to: |
Sends the data in argument one (a TobSocketsDatagram) to the address specified in the second argument. It answers the number of bytes of the data sent. Use the #truncated method to see if the data within the datagram had to be truncated before sending. |
|
sendDatagramNoTimeOut:to: |
The same as #sendDatagram:to: but ignores the time-out. |
|
sendTimeOut: |
Sets the number of milliseconds after which a time-out will occur when using some of the above methods. Using the value 0 means 'No time-out'. |
Time-outs: Notice that there are four types of time-out:
Creating TCP/IP servers can be a more complicated than clients, so the Socket Set provides you with the utility class TobSocketsServer (and its subclasses TobSocketsDatagramServer and TobSocketsStreamServer) to do the work for you.
To create a server you simply use the class method #address:clientBlock:.
The first argument is the address that you want to listen on and the second is a single argument block where the argument is the socket to a client that connects.
This example creates server corresponding to the client example above.
To try out the above code execute it, open a terminal application (Select 'Run...' from the 'Start' menu and type 'telnet' to get the application that comes with Windows 95 or NT). Connect to your machine on port 9000. You should see the prompt 'OK'. Next type 'REVERSE some text' (you may not be able to see what you are typing) and press 'Enter'. Then you should then see 'BAD REQUEST' (if you made a typo) or 'OK txet emos'. The connection will then be closed.
To stop the server execute:
Alternatively, the server will automatically be stopped if the process in which it runs is terminated using the debugger. Note that the name of the process includes the address and port number for easy identification.
Most of the time you will find that this simple approach to servers does everything you need, but there are a few more methods that you may find useful:
|
Method |
Description |
|
childSockets |
Answers a collection of child sockets (TobSocketsChildSocket) relating to the current connections. |
|
forkChildren:, forkChildren |
Sets and gets whether the process should fork (with the same priority as the server) whenever a client connects. If this value is false then a second child can not connect until the first execution of the clientBlock has completed. The default value is true. |
|
isRunning |
Answers true if the server is waiting for clients to connect. |
|
killChildren |
Closes all connected sockets. |
|
loopBlock: |
A two-argument block that is executed every time the listening socket loops waiting for a client to connect. The first argument is the server itself and the second is the server's listening socket. The loopBlock can also be specified using the class method #address:clientBlock:loopBlock: instead of #address:clientBlock:. |
|
numberOfChildren |
Answers the number of children currently connected. |
|
numberOfConnections |
Same as #numberOfChildren (for compatibility). |
|
start |
Starts the server without forking the process. |
Your servers will usually need to manage many simultaneous connections from clients. If this is the case the server should be configured to fork child connections (the default setting); that is, create a new process for each connection. The server object will automatically manage the resumption of these child processes to simulate asynchronous behaviour. If, however, you do not wish the server object to automatically resume the child process (say, to delay or wait on a semaphore) then the message #resumedByServer: can be sent to the process with argument false. Alternatively the method #notResumedByServerDuring: can be used. For example:
If you wish to use UDP instead of TCP (to send datagrams to a server) then the process is simpler. You first create the socket using #newDatagram, create a new TobSocketsDatagram and then send it using the #sendDatagram:to: message. For example:
Creating a UDP server is very similar to creating a TCP server. The main difference being that the argument to the client block is the incoming datagram. For example:
To specify the maximum size of the data of incoming datagrams send the server the message #bufferSize:. Any data larger than this value will be truncated and the 'truncated' flag set to true. The message #timeOut can be sent to the server. This specifies the period of the time the server should be prepared to wait when no datagrams are being sent to it. If the period is exceeded then an ExTobSocketsReceiveTimedOut exception is signalled. Setting the value to 0 (the default value) means that the server will not time out.
The incoming datagram understands three special messages:
|
Method |
Description |
|
data |
Answers the data put into the datagram by the sender (a String or ByteArray). |
|
sender |
The address of the sender. |
|
truncated |
A Boolean value indicating whether the dataString has been truncated. |
To stop the example server execute:
Alternatively, the server will automatically be stopped if the process in which it runs is terminated using the debugger. Note that the name of the process includes the address and port number for easy identification.
A description of each is given below. Indentation denotes the parent-child relationships between exceptions.
A new parts category ('Totally Objects' Socket Set') will be added to the Composition Editor. This category contains ten parts. These are:
To see examples of these parts in action load the configuration map 'Totally Objects, Socket Set - Parts Examples'. A brief overview of each is given below.
Then press 'Download...' and enter the name of the file you would like to create locally.
After a short while the file should be downloaded.
Similarly TobSocketsPartsExamplesSMTPMailWithAttachment can be used to send internet mail with a simple text attachment.
Copyright (c) 2002 DirectDual Limited (trading as TotallyObjects).
Totally Objects - DirectDual Limited,
34 Compton Avenue,
Gidea Park, Essex,
RM2 6ES, England.
Tel: +44 1708 733295
Fax: +44 1708 783438
sales@totallyobjects.com
DirectDual Limited is an IBM Object Connection member.