Swazoo Client/Server?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Swazoo Client/Server?

Rob Rothwell
We are writing a little utility using Squeak as a processing server on the network, and a VB tray application as a client.

Should I jump right into Sockets in Squeak, or is there something that can be done at a "higher" level using Swazoo to listen for a message and send back a result?

Just wondering...

Rob

_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: Swazoo Client/Server?

Janko Mivšek
Hi Rob,

Rob Rothwell wrote:
> We are writing a little utility using Squeak as a processing server on
> the network, and a VB tray application as a client.
>
> Should I jump right into Sockets in Squeak, or is there something that
> can be done at a "higher" level using Swazoo to listen for a message and
> send back a result?

It depends a lot of your app. But if there is not a HTTP protocol
involved, you better go with sockets directly. I propose that you get
used of Sport's SpSocket, to be safely portable in the future.

I attached such a little server I made recently, named LineTCPConnect
and this is kind of Swazoo lite. This is a fileout from VW and should be
easily imported to Squeak. Just Word menu>Open>File list, find .st,
right click and "remove line feeds" then "code-file browser"...

Janko

--
Janko Mivšek
AIDA/Web
Smalltalk Web Application Server
http://www.aidaweb.si

'From VisualWorks®, 7.4.1 of May 30, 2006 on September 23, 2008 at 7:31:07 pm'!


Object subclass: #LineTCPConnect
        instanceVariableNames: 'parent port socket loop content headers body '
        classVariableNames: ''
        poolDictionaries: ''
        category: 'BiArt-Core'!

LineTCPConnect class
        instanceVariableNames: ''!

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


!LineTCPConnect class methodsFor: 'instance creation' stamp: ' 23/9/08 19:31'!

newFor: anAIDASite
        ^self new parent: anAIDASite! !

!LineTCPConnect class methodsFor: 'defaults' stamp: ' 23/9/08 19:31'!

defaultPort
        ^8870! !

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


LineTCPConnect comment:
'LineTCPConnect is a TCP server listening for connections from flowmeter measuring line sending measurenment data and documents.'!

!LineTCPConnect methodsFor: 'accessing' stamp: ' 23/9/08 19:31'!

body
        ^body!

body: aString
        body := aString!

content
        "received content"
        ^content!

content: aByteArray
        content := aByteArray!

headers
        headers isNil ifTrue: [self initHeaders].
        ^headers!

loop
        ^loop!

loop: aProcess
        loop := aProcess!

parent
        ^parent!

parent: anAIDASite
        parent := anAIDASite!

port
        port isNil ifTrue: [self port: self class defaultPort].
        ^port!

port: aNumber
        port := aNumber!

socket
        ^socket!

socket: aSpSocket
        socket := aSpSocket! !

!LineTCPConnect methodsFor: 'start/stop' stamp: ' 23/9/08 19:31'!

start
        self loop notNil ifTrue: [self stop].
        self socket: SpSocket newTCPSocket.
        self socket
                setAddressReuse: true;
                bindSocketAddress: (SpIPAddress hostName: '0.0.0.0' port: self port).
        self socket listenBackloggingUpTo: 10.
        self loop: ([[self acceptConnection] repeat] fork)!

stop
        self loop notNil ifTrue:
                [self loop terminate. self loop: nil].
        self socket notNil ifTrue:
                [self socket close. self socket: nil].! !

!LineTCPConnect methodsFor: 'private-receiving' stamp: ' 23/9/08 19:31'!

acceptConnection
        | stream |
        stream  := self socket accept underlyingSocket readStream binary.
        self receiveContentFrom: stream.
        self parseContent.
        self postReceiveAction.
        stream close.!

postReceiveAction
        "do whatever appropriate with a received content"
        Transcript cr; show: 'received ', self content size printString, ' bytes'.
        self parent isNil ifTrue: [^nil]. "for testing purposes"
        self headers size >= 2 ifFalse: [^nil].
        self parent calibrations
                attachAndCloseFlowMeterMeasurementFile: self headers first key
                content: self body
                measuredBy: (AIDASite convert: self headers second key fromCodepage: #UTF8)
                tempAverage: (self valueForHeader: 'Tokolice')
                tempDifference: (self valueForHeader: 'DeltaTokolice')
                atmosphericPressure: (self valueForHeader: 'AtmTlak')
                counterAtEnd: (self valueForHeader: 'Stanje')!

receiveContentFrom: aStream
        "until peer close a socket,  put it in content instvar"
        | stream |
        stream := WriteStream on: ByteString new.
        [[aStream atEnd] whileFalse:
                [stream nextPut: aStream next asCharacter] ]  
                        on: Error
                        do: [:ex | ].
        self content: stream contents! !

!LineTCPConnect methodsFor: 'initialize-release' stamp: ' 23/9/08 19:31'!

initHeaders
        headers := OrderedCollection new! !

!LineTCPConnect methodsFor: 'private-parsing' stamp: ' 23/9/08 19:31'!

parseContent
        | stream |
        stream := self content readStream.
        self readHeadersFrom: stream.
        self readBodyFrom: stream.!

readBodyFrom: aStream
        self body: aStream upToEnd!

readHeadersFrom: aStream
        | line assoc |
        self initHeaders.
        [aStream atEnd] whileFalse:
                [line := aStream upTo: Character cr. aStream next. "skip lf"
                assoc := Association new.
                assoc key: (line readStream upTo: $:).
                assoc value: (line readStream upTo: $: ; upToEnd) trimBlanks.
                self headers add: assoc.
                aStream peek = Character cr ifTrue:
                        [aStream next; next. "skip crl lf"
                        ^self headers].
                ]!

valueForHeader: aString
        "value from headers with that name"
        " 'DeltaTokolice 0.69700'->'' "
        | header |
        header := self headers detect: [:each | (aString, '*') match: each key] ifNone: [^nil].
        ^((header key readStream upToSeparator; upToEnd) copyReplaceAll: '.' with: ',')
                asFixedPoint: 4! !

'From VisualWorks®, 7.4.1 of May 30, 2006 on September 23, 2008 at 7:31:11 pm'!


TestCase subclass: #LineTCPConnectTest
        instanceVariableNames: ''
        classVariableNames: ''
        poolDictionaries: ''
        category: 'GeoMer-Tests'!

LineTCPConnectTest class
        instanceVariableNames: ''!

!LineTCPConnectTest methodsFor: 'testing' stamp: ' 23/9/08 19:31'!

testParsing
        | connect client |
        connect := LineTCPConnect new port: 5678. "to be different than default"
        connect start.
        [client := SpSocket connectToServerOnHost: '0.0.0.0' port: connect port.
        client write: self content.
        client close. (Delay forMilliseconds: 100) wait.
        ] ensure: [client close. connect stop].
        connect parseContent.
        self assert: connect headers first = ('file.dat'-> '').
        self assert: connect headers last = ('content-length'-> self body size printString).
        self assert: connect body = self body.!

testSending
        | connect client |
        connect := LineTCPConnect new port: 5678. "to be different than default"
        connect start.
        [client := SpSocket connectToServerOnHost: '0.0.0.0' port: connect port.
        client write: self content.
        client close.
        (Delay forMilliseconds: 100) wait.
        self assert: connect content = self content.
        ] ensure: [client close. connect stop].!

testStartup
        | connect |
        connect := LineTCPConnect new.
        connect start.
        [self assert: connect socket notNil.
        self assert: connect loop notNil]
                ensure: [connect stop].
        self assert: connect socket isNil.
        self assert: connect loop isNil.! !

!LineTCPConnectTest methodsFor: 'setup' stamp: ' 23/9/08 19:31'!

body
        ^'body of the message'.
!

content
        ^'file.dat', self crlf,
        'content-length: ', self body size printString, self crlf,
        self crlf,
        self body!

crlf
        ^String with: Character cr with: Character lf! !

_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: Swazoo Client/Server?

Michael Rueger-6
Janko Mivšek wrote:

> It depends a lot of your app. But if there is not a HTTP protocol
> involved, you better go with sockets directly. I propose that you get
> used of Sport's SpSocket, to be safely portable in the future.

better even SocketStream, although not sure how portable that is.

Michael
_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: Swazoo Client/Server?

Rob Rothwell
In reply to this post by Janko Mivšek
Thank you!

We will just be passing a patient account number and getting back a text stream of some sort suitable for display.

I figured it was Sockets, but so many other things are so easy, that I thought I'd ask before writing my own message protocol!

Thanks again,

Rob

On Tue, Sep 23, 2008 at 1:38 PM, Janko Mivšek <[hidden email]> wrote:
Hi Rob,


Rob Rothwell wrote:
We are writing a little utility using Squeak as a processing server on the network, and a VB tray application as a client.

Should I jump right into Sockets in Squeak, or is there something that can be done at a "higher" level using Swazoo to listen for a message and send back a result?

It depends a lot of your app. But if there is not a HTTP protocol involved, you better go with sockets directly. I propose that you get used of Sport's SpSocket, to be safely portable in the future.

I attached such a little server I made recently, named LineTCPConnect and this is kind of Swazoo lite. This is a fileout from VW and should be easily imported to Squeak. Just Word menu>Open>File list, find .st, right click and "remove line feeds" then "code-file browser"...

Janko

--
Janko Mivšek
AIDA/Web
Smalltalk Web Application Server
http://www.aidaweb.si

'From VisualWorks®, 7.4.1 of May 30, 2006 on September 23, 2008 at 7:31:07 pm'!


Object subclass: #LineTCPConnect
       instanceVariableNames: 'parent port socket loop content headers body '
       classVariableNames: ''
       poolDictionaries: ''
       category: 'BiArt-Core'!

LineTCPConnect class
       instanceVariableNames: ''!

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


!LineTCPConnect class methodsFor: 'instance creation' stamp: ' 23/9/08 19:31'!

newFor: anAIDASite
       ^self new parent: anAIDASite! !

!LineTCPConnect class methodsFor: 'defaults' stamp: ' 23/9/08 19:31'!

defaultPort
       ^8870! !

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


LineTCPConnect comment:
'LineTCPConnect is a TCP server listening for connections from flowmeter measuring line sending measurenment data and documents.'!

!LineTCPConnect methodsFor: 'accessing' stamp: ' 23/9/08 19:31'!

body
       ^body!

body: aString
       body := aString!

content
       "received content"
       ^content!

content: aByteArray
       content := aByteArray!

headers
       headers isNil ifTrue: [self initHeaders].
       ^headers!

loop
       ^loop!

loop: aProcess
       loop := aProcess!

parent
       ^parent!

parent: anAIDASite
       parent := anAIDASite!

port
       port isNil ifTrue: [self port: self class defaultPort].
       ^port!

port: aNumber
       port := aNumber!

socket
       ^socket!

socket: aSpSocket
       socket := aSpSocket! !

!LineTCPConnect methodsFor: 'start/stop' stamp: ' 23/9/08 19:31'!

start
       self loop notNil ifTrue: [self stop].
       self socket: SpSocket newTCPSocket.
       self socket
               setAddressReuse: true;
               bindSocketAddress: (SpIPAddress hostName: '0.0.0.0' port: self port).
       self socket listenBackloggingUpTo: 10.
       self loop: ([[self acceptConnection] repeat] fork)!

stop
       self loop notNil ifTrue:
               [self loop terminate. self loop: nil].
       self socket notNil ifTrue:
               [self socket close. self socket: nil].! !

!LineTCPConnect methodsFor: 'private-receiving' stamp: ' 23/9/08 19:31'!

acceptConnection
       | stream |
       stream  := self socket accept underlyingSocket readStream binary.
       self receiveContentFrom: stream.
       self parseContent.
       self postReceiveAction.
       stream close.!

postReceiveAction
       "do whatever appropriate with a received content"
       Transcript cr; show: 'received ', self content size printString, ' bytes'.
       self parent isNil ifTrue: [^nil]. "for testing purposes"
       self headers size >= 2 ifFalse: [^nil].
       self parent calibrations
               attachAndCloseFlowMeterMeasurementFile: self headers first key
               content: self body
               measuredBy: (AIDASite convert: self headers second key fromCodepage: #UTF8)
               tempAverage: (self valueForHeader: 'Tokolice')
               tempDifference: (self valueForHeader: 'DeltaTokolice')
               atmosphericPressure: (self valueForHeader: 'AtmTlak')
               counterAtEnd: (self valueForHeader: 'Stanje')!

receiveContentFrom: aStream
       "until peer close a socket,  put it in content instvar"
       | stream |
       stream := WriteStream on: ByteString new.
       [[aStream atEnd] whileFalse:
               [stream nextPut: aStream next asCharacter] ]
                       on: Error
                       do: [:ex | ].
       self content: stream contents! !

!LineTCPConnect methodsFor: 'initialize-release' stamp: ' 23/9/08 19:31'!

initHeaders
       headers := OrderedCollection new! !

!LineTCPConnect methodsFor: 'private-parsing' stamp: ' 23/9/08 19:31'!

parseContent
       | stream |
       stream := self content readStream.
       self readHeadersFrom: stream.
       self readBodyFrom: stream.!

readBodyFrom: aStream
       self body: aStream upToEnd!

readHeadersFrom: aStream
       | line assoc |
       self initHeaders.
       [aStream atEnd] whileFalse:
               [line := aStream upTo: Character cr. aStream next. "skip lf"
               assoc := Association new.
               assoc key: (line readStream upTo: $:).
               assoc value: (line readStream upTo: $: ; upToEnd) trimBlanks.
               self headers add: assoc.
               aStream peek = Character cr ifTrue:
                       [aStream next; next. "skip crl lf"
                       ^self headers].
               ]!

valueForHeader: aString
       "value from headers with that name"
       " 'DeltaTokolice 0.69700'->'' "
       | header |
       header := self headers detect: [:each | (aString, '*') match: each key] ifNone: [^nil].
       ^((header key readStream upToSeparator; upToEnd) copyReplaceAll: '.' with: ',')
               asFixedPoint: 4! !

'From VisualWorks®, 7.4.1 of May 30, 2006 on September 23, 2008 at 7:31:11 pm'!


TestCase subclass: #LineTCPConnectTest
       instanceVariableNames: ''
       classVariableNames: ''
       poolDictionaries: ''
       category: 'GeoMer-Tests'!

LineTCPConnectTest class
       instanceVariableNames: ''!

!LineTCPConnectTest methodsFor: 'testing' stamp: ' 23/9/08 19:31'!

testParsing
       | connect client |
       connect := LineTCPConnect new port: 5678. "to be different than default"
       connect start.
       [client := SpSocket connectToServerOnHost: '0.0.0.0' port: connect port.
       client write: self content.
       client close. (Delay forMilliseconds: 100) wait.
       ] ensure: [client close. connect stop].
       connect parseContent.
       self assert: connect headers first = ('file.dat'-> '').
       self assert: connect headers last = ('content-length'-> self body size printString).
       self assert: connect body = self body.!

testSending
       | connect client |
       connect := LineTCPConnect new port: 5678. "to be different than default"
       connect start.
       [client := SpSocket connectToServerOnHost: '0.0.0.0' port: connect port.
       client write: self content.
       client close.
       (Delay forMilliseconds: 100) wait.
       self assert: connect content = self content.
       ] ensure: [client close. connect stop].!

testStartup
       | connect |
       connect := LineTCPConnect new.
       connect start.
       [self assert: connect socket notNil.
       self assert: connect loop notNil]
               ensure: [connect stop].
       self assert: connect socket isNil.
       self assert: connect loop isNil.! !

!LineTCPConnectTest methodsFor: 'setup' stamp: ' 23/9/08 19:31'!

body
       ^'body of the message'.
!

content
       ^'file.dat', self crlf,
       'content-length: ', self body size printString, self crlf,
       self crlf,
       self body!

crlf
       ^String with: Character cr with: Character lf! !



_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: Swazoo Client/Server?

Rob Rothwell
In reply to this post by Janko Mivšek
On Tue, Sep 23, 2008 at 1:38 PM, Janko Mivšek <[hidden email]> wrote:
It depends a lot of your app. But if there is not a HTTP protocol involved, you better go with sockets directly. I propose that you get used of Sport's SpSocket, to be safely portable in the future.

I attached such a little server I made recently, named LineTCPConnect and this is kind of Swazoo lite. This is a fileout from VW and should be easily imported to Squeak. Just Word menu>Open>File list, find .st, right click and "remove line feeds" then "code-file browser"...

I managed to get this into Squeak, with a little pretty-print help to fix up the missing linefeeds!

Now I just have to learn how to actually read from the Socket, since the tests die during

stream  := self socket accept underlyingSocket readStream binary.

in

acceptConnection
| stream |
stream  := self socket accept underlyingSocket readStream binary.
self receiveContentFrom: stream. 
self parseContent.
self postReceiveAction.
stream close.

since squeak has no Socket>>readStream method, which is apparently what the underlyingSocket is in Squeak!

I see why Michael said "better even SocketStream," though I don't know how to use that either!

Thanks...I at least know what to try to figure out now...

Rob

_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida