View this PageEdit this PageAttachments to this PageHistory of this PageHomeRecent ChangesSearch the SwikiHelp Guide
Hotspots: Admin Pages | Turn-in Site |
Current Links: Cases Final Project Summer 2007

Boss Objects over Network Sockets

(by Michael Tanner)
(April 19, 2008)


Contents:




Overview

If you are having trouble getting Boss objects to pass across a network socket stream, then this documentation should help. The solution involves converting a BOSS object into a hex string. Also provided below is a big picture overview of how Network Socket connections work.


What is BOSS?

BOSS stands for Binary Object Storage System. Boss is a great way to convert a SmallTalk object into a binary stream. This is helpful because it enables you to take SmallTalk objects and save them to a hard drive, thereby letting you reuse those objects at a later time. You can also use Boss to send objects to another computer across a network.

See also:


What's the deal with Network Sockets and Streams?

SmallTalk supports common network standards such as TCP/IP; which therefore let's you create client/server applications capable of communicating across a network or the Internet. The big picture of how this works is as follows: A server application creates a port listener; which is thereby capable of connecting to client apps when a connection is requested. In order for this to happen the client app needs to know the IP address of the server and the port number that the Server app is listening to. Think of the IP address as the address of the Server's computer and the Port Number as the address of the specific application on the Server's computer. A single Server machine can be running many different types of Server applications; such as an http server, ftp server, mail server, database server, or even a server app written in SmallTalk. Here, each application is listening to a distinct port number on the same machine with the same IP address.

Once a connection is established both the Server App and the Client App can send data to each other across a stream. A network stream works like any other stream, such as a file stream that you would use to read or write data on a hard drive. The Server App uses multiple Threads to maintain concurrent connections. The Client App would also use Threads so it would be functional while at the same time waiting for data to come from the Server. This is just a high level overview of how Network Sockets work.

See also:


So what's the problem?

BOSS and Network Sockets together make a powerful combination; however, a bit tricky to implement. The problem arises when you create an instance of BOSS (BinaryObjectStorage) using a network stream object. Once you've passed one BOSSed object across the network you can't easily pass another without closing the BOSS object. However, when you close the BOSS object you are also closing the Network Stream. Now, with your network connection closed you can't pass any more data without reopening the network socket (which gets messy).


What's the solution?

One solution is to convert a BOSSed Object to a Hex string, pass the Hex string across the network, and then reassemble the BOSSed Hex string back into the original object. Included below is sample code of how this works.


Sample Code:


4 Methods:


objectToBossHex: aObject
    " Input: An object to be BOSSed                          "
    " Output: a hex string representing the BOSSed object    "
    " 
      This method uses BinaryObjectStorage to convert an object to
      a temp binary stream.  The temp binary stream is connected to
      a ByteArray.  The ByteArray is then converted to a hex string
      and returned.
      The BtyeArray class has a method called asHexString that
      performs the hex conversion.
    "
    | hexString boss tmpStream tmpArray |

    hexString := ''.
    tmpArray := ByteArray new.
    tmpStream := tmpArray readWriteStream.

    boss := BinaryObjectStorage onNew: tmpStream.
    boss nextPut: aObject.

    tmpArray := tmpArray copyFrom: 1 to: (tmpStream size).
    hexString := tmpArray asHexString.
    boss close.
    ^hexString.



hexToBossObject: hexString
    " Input: A hex string representing a BOSSed object                "
    " Output: The object contained in the BOSSed Hex String           "
    " 
      This method reverses what objectToBossHex performed.  I could not
      find a built in method to convert the hex back to binary; therefore,
      I created a helper method called asciiToHexInt.  I've included this
      code below.
    "
     | aObject isEven hex1 hex2 tmpString tmpArray tmpStream boss intByte |
     aObject := ''.
     tmpString := hexString convertToByteArray.
 
     tmpArray := ByteArray new.
     tmpStream := tmpArray readWriteStream.
     tmpStream position: 0.
 
     isEven := false.
     1 to: (tmpString size) do: [ :index |
           (isEven) ifFalse: [
                isEven := true.
                hex1 := self asciiToHexInt: (tmpString at: index).
                hex2 := self asciiToHexInt: (tmpString at: (index+1)).
                intByte := (hex1 * 16) + hex2.
                tmpStream nextPut: ((intByte) asByteArray) first.
           ]
           ifTrue: [
                isEven := false.    
           ].
     ].
 
     tmpStream position: 0.
     boss := BinaryObjectStorage onOldNoScan: tmpStream.
     aObject := (boss contents) at: 1.
     boss close.
 
     ^ aObject.



asciiToHexInt: tmp
    " Helper method used in hexToBossObject. "
     | tmp2 |
     tmp2 := tmp - 48.
     (tmp2 > 9) ifTrue: [
           tmp2 := tmp2 - 7.
     ].
     ^ tmp2.



testing
       " This is the code I used to test these methods. "

       | tmpHexString tmpObj list2 list |
       list := OrderedCollection new.
       list2 := OrderedCollection new.
       list add: 'Apple'.
       list add: 'Orange'.
       list add: 'Peach'.
       list2 add: 'Pizza'.
       list2 add: 'Salad'.
       list add: list2.

       tmpHexString := self objectToBossHex: list.
       Transcript show: tmpHexString; cr.
       tmpObj := self hexToBossObject: tmpHexString.
 
       Transcript show: '......'.
       Transcript show: (tmpObj at:1), ' .. '.
       Transcript show: ((tmpObj at:4) at: 1), ' .. '.


Notes and Remarks

I hope you found this documentation helpful. A few things I'll mention is that though this technique works great, it's probably not the most efficient way to accomplish this. For very large objects, say over a mega-byte, the hex conversion can be slow. Also the size of the object that can be "BOSS HEXed" is limited to the size that the ByteArray can hold. I found this limit to be approximately a few megabytes. This issue could be solved if you partitioned an object into multiple ByteArray's (as in an array of ByteArray's).

An extension to this code that I found useful is in passing files across a network. I created two methods to handle this. The first method is fileToHex which takes in a file's path location and filename and then returns a hex string. The second method is hexToFile which takes in a file location/name and a hex string; this method then saves the file to the specified file location. This made file transfers very easy to implement. Here again this process is slow and limited to smaller file sizes. But it works great if you need to transfer or store something like an image or a Word document.



Links to this Page