[comp.lang.smalltalk] Using ParcPlace ST80 2.5 BOSS and UDP UnixSocketAccessors

chase@boulder.Colorado.EDU (Chase Turner) (09/07/90)

I am interested in using ParcPlace's BOSS storage to send objects from one
smalltalk image to another by means of UDP broadcasts.  The following code
is a simple attempt to encode an object using Boss, store the encoded object
in a ByteString, and send the ByteString as a packet to a listener located
on another machine.  To my suprise, I've found that I cannot encode simple
objects using BinaryStorage -- or even with AsciiBinaryStorage.  Would
anyone care to help me with a workaround (or re-orient my thoughts about
how I can send any type of object from one image to another smalltalk image
using UDP broadcasts (Not TCP or some such.....I am attempting to recreate
the Amoeba Operating System's reliable broadcast protocol in Smalltalk....)

Thanks,

chase@boulder.colorado.edu

-----detailed description follows--------
Subject: 
AsciiBinaryStorage unable to encode an instance of Time.

Description:  
Using AsciiBinaryStorage, I am able to write the BOSS header to the ReadWriteStream on: ByteString new: 400, but attempts to place a Time now instance in the buffer results in a failure when an integer value of 0 is passed the message size.  This is a consequence of AsciiBinaryStorage attempting to encodeOpByte: 192 on the stream. 

To recreate:  
FileIn the following code. 
Browse on StreamBufferedUDPSocketAccessor class, protocol for "examples", method "broadcast".   Be certain to replace the machine name 'alpo' with your machine name (DO NOT USE '-local host-'!).
Select the code in double quotes and DoIt.
Code should halt in the broadcast method when the AsciiBinaryStorage encodeOpByte: 192.

Notes:
To verify the ability to encode the BOSS header, remove the following lines by sorrounding them with quotes as shown by: 
				bos _ AsciiBinaryStorage write: broadcastSocket. 
				bos nextPut: (Time now).
				"AsciiBinaryStorage write: broadcastSocket."
Replace with:
				"bos _ AsciiBinaryStorage write: broadcastSocket. 
				bos nextPut: (Time now)."
				AsciiBinaryStorage write: broadcastSocket.
Select the code in double quotes and DoIt (the code at the beginning of the method).
Broadcast will succeed and return an inspector window on an AsciiBinaryStorage object.
Be certain to DoIt on "StreamBufferedUDPSocketAccessor closeAllInstances" to kill any open sockets .....

Also:
I want to replace AsciiBinaryStorage with BinaryStorage for more efficient encoding.  However, doing so results in another error -- the inability of the ByteString buffer to accept integer bytes.  This is a consequence of the BinaryStorage attempting to convert all characters to integers and storing on the ReadWriteStream' ByteString collection....what is the alternative?  Should I create a subclass of ByteString which will accept integers?  Or, if I utilize a ByteArray, I cannot use accessor at:put: -- sh






ould I therefore subclass ByteArray and override it's error message?
Or, if I am to use UninterpertedBytes, how am I to make methods next, nextPut: nextPutAll?

chase@boulder.colorado.edu

----------------cut here------------------------

IPSocketAddress variableByteSubclass: #IPBroadcastAddress
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Cactis-Network'!

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

IPBroadcastAddress class
	instanceVariableNames: ''!


!IPBroadcastAddress class methodsFor: 'instance creation'!

forLANwithHostNamed: aName port: aPort 
	| IPhostAddress hostAddress  |
	IPhostAddress _ IPSocketAddress hostName: aName.
	hostAddress _ IPhostAddress hostAddress.
	hostAddress at: 4 put: 255.
	^self hostAddress: hostAddress port: aPort! !

ReadWriteStream subclass: #SocketReadWriteStream
	instanceVariableNames: ''
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Cactis-Network'!


!SocketReadWriteStream methodsFor: 'positioning'!

readLimit: aValue 
	readLimit _ aValue! !

UnixSocketAccessor subclass: #StreamBufferedUDPSocketAccessor
	instanceVariableNames: 'buffer stream bytesRecieved bytesSent '
	classVariableNames: ''
	poolDictionaries: ''
	category: 'Cactis-Network'!


!StreamBufferedUDPSocketAccessor methodsFor: 'initialize-release'!

close
	"Inform Transcript that the socket is closed"

	[self isActive ifTrue: [Transcript cr; show: self getName printString , ' closed at ' , Time now printString , '.']]
		valueNowOrOnUnwindDo: 
			[self primClose.
			super release]!

initialize
	"initialize buffers."

	"super initialize."
	self isActive
		ifTrue: [Transcript cr; show: self getName printString , ' is initialized at ' , Time now printString , '.']
		ifFalse: ["Transcript cr; show: 'Socket is inactive during initalization....'"].
	buffer _ ByteString new: self bufferSize.
	stream _ SocketReadWriteStream on: buffer.
	^self! !

!StreamBufferedUDPSocketAccessor methodsFor: 'specialized IO'!

receiveFrom: aSocket 
	"I will listen to aSocket and buffer accordingly."

	bytesRecieved _ self receiveFrom: aSocket buffer: buffer.
	stream on: buffer.
	stream readLimit: bytesRecieved.
	^self!

sendTo: aSocket 
	"I will sendTo to aSocket. If the operation is successful, I reset my 
	stream. Otherwise, the stream remains full."

	bytesSent _ self
				sendTo: aSocket
				buffer: buffer
				start: 1
				for: stream position.
	bytesSent = stream position
		ifTrue: [stream reset]
		ifFalse: [^self error: bytesSent printString , ' bytes were sent though ' , stream position , ' were slated to go.']! !

!StreamBufferedUDPSocketAccessor methodsFor: 'stream support'!

collection
	^stream collection!

contents
	^buffer!

contentsSpecies
	^stream contentsSpecies!

next
	^stream next!

next: aValue

	^stream next: aValue!

nextPut: aValue 
	"Coerce all bytes to appear as strings."

	^stream nextPut: aValue!

nextPutAll: aValue 
	^stream nextPutAll: aValue!

print: aCollection 
	^stream nextPutAll: aCollection! !
"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

StreamBufferedUDPSocketAccessor class
	instanceVariableNames: ''!


!StreamBufferedUDPSocketAccessor class methodsFor: 'examples'!

broadcast
	"Transcript clear.
	[StreamBufferedUDPSocketAccessor server] forkAt: Processor 
	userInterruptPriority. 

	(Delay forSeconds: 2) wait. 

	[StreamBufferedUDPSocketAccessor broadcast] forkAt: Processor 	userInterruptPriority."

	| broadcastSocket broadcastAddress  bos |
	broadcastSocket _ StreamBufferedUDPSocketAccessor newStreamBufferedUDP.
	broadcastAddress _ IPBroadcastAddress forLANwithHostNamed: 'alpo' port: 6666.
	
	["Note -- if I used '-this host-' as an argument, then the constructed 
	IPBroadcastAddress would be 0.0.0.255 -- unacceptable. I will 
	suggest that the message thisHost return an actual IP address 
	rather than 0.0.0.0"
	bos _ AsciiBinaryStorage write: broadcastSocket. 
	bos nextPut: (Time now).
	"AsciiBinaryStorage write: broadcastSocket."
	Transcript cr; show: broadcastSocket getName printString , ' is broadcasting .....'.
	broadcastSocket sendTo: broadcastAddress.
	Transcript  show: 'finished broadcasting.']
		valueNowOrOnUnwindDo: 
			[Processor yield.  
			broadcastSocket close]!

closeAllInstances
	"StreamBufferedUDPSocketAccessor closeAllInstances"

	StreamBufferedUDPSocketAccessor allInstances do: [:each | each isActive
			ifTrue: [each close]
			ifFalse: [each release]]!

server
	"[StreamBufferedUDPSocketAccessor server] forkAt: Processor 
	userInterruptPriority."

	| listeningAtSocket broadcasterIPSocketAddress anObject |
	StreamBufferedUDPSocketAccessor closeAllInstances.
	listeningAtSocket _ StreamBufferedUDPSocketAccessor newStreamBufferedUDPServerAtPort: 6666.
	broadcasterIPSocketAddress _ IPSocketAddress new.
	[[Sensor blueButtonPressed]
		whileFalse: ["Wait for data arrival or timeout."
			(listeningAtSocket readWaitWithTimeoutMs: 200)
				ifFalse: 
					[listeningAtSocket receiveFrom: broadcasterIPSocketAddress.
					anObject _ AsciiBinaryStorage read: listeningAtSocket.
					[anObject inspect]
						forkAt: Processor userInterruptPriority.
					Transcript cr; show: listeningAtSocket getName printString , ' recieved object '; cr; show: '   ' , anObject printString; cr; show: '  from ' , broadcasterIPSocketAddress printString , '.']]]
		valueNowOrOnUnwindDo: [listeningAtSocket close]! !

!StreamBufferedUDPSocketAccessor class methodsFor: 'instance creation'!

newStreamBufferedUDP
	"Appropriate for clients or for servers with no preassigned ports."

	^super newUDP initialize!

newStreamBufferedUDPServerAtPort: aPortNumber 
	| socket socketAddress |
	socket _ self family: (SocketAddress domainCodeFromName: #afInet)
				type: self sockDgram.
	
	[socketAddress _ IPSocketAddress hostAddress: IPSocketAddress thisHost port: aPortNumber.
	socket bindTo: socketAddress.
	socket initialize]
		valueOnUnwindDo: [socket close].
	^socket! !
-----------------end cut---------------------

baumeist@exunido.uucp (Hubert Baumeister) (09/11/90)

In article <25833@boulder.Colorado.EDU> chase@boulder.Colorado.EDU (Chase Turner) writes:
> ... To my suprise, I've found that I cannot encode simple
>objects using BinaryStorage -- or even with AsciiBinaryStorage.
 ...
>-----detailed description follows--------
>Subject:
>AsciiBinaryStorage unable to encode an instance of Time.
This is not true. Both AsciiBinaryStorage and BinaryStorage function
properly as you can see when using
| ws |
ws _ '' writeStream.
(AsciiBinaryStorage write: ws) nextPut: Time now.
ws contents

The bug is in one of your classes: 
StreamBufferedUDPSocketAccessor>stream support

print: aCollection
	^stream nextPutAll: aCollection

the semantics of print: anObject (as can be found in class Stream) is to 
store a printable description of an object on a stream, so it should read

print: anObject
	^anObject printOn: self

>Also:
>I want to replace AsciiBinaryStorage with BinaryStorage for more efficient encoding.  However, doing so results in another error -- the inability of the ByteString buffer to accept integer bytes.  This is a consequence of the BinaryStorage attempting to c
>onvert all characters to integers and storing on the ReadWriteStream' ByteString collection....what is the alternative?  Should I create a subclass of ByteString which will accept integers?  Or, if I utilize a ByteArray, I cannot use accessor at:put:
>ould I therefore subclass ByteArray and override it's error message?
>Or, if I am to use UninterpertedBytes, how am I to make methods next, nextPut: nextPutAll?

In connection with BOSS there is no problem. If you take AsciiBinaryStorage,
than you have to use a stream on a string and if you use BinaryStorage 
you have to use a stream on a byte array.
The message at:put: can be used with instances of class String and with instances
of class ByteArray. In the case of a string the object has to be a character
and in the case of an byte array the object has to be a byte. If you
now upfront that you would like to put bytes than you can use byteAt:put: 
with strings and with byte arrays.

Hubert
(Hubert Baumeister
 huba@ls5.informatik.uni-dortmund.de)