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)