<div dir="ltr">Thank you!<div><br></div><div>We will just be passing a patient account number and getting back a text stream of some sort suitable for display.</div><div><br></div><div>I figured it was Sockets, but so many other things are so easy, that I thought I&#39;d ask before writing my own message protocol!</div>
<div><br></div><div>Thanks again,</div><div><br></div><div>Rob</div><div><br><div class="gmail_quote">On Tue, Sep 23, 2008 at 1:38 PM, Janko Mivšek <span dir="ltr">&lt;<a href="mailto:janko.mivsek@eranova.si">janko.mivsek@eranova.si</a>&gt;</span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">Hi Rob,<div class="Ih2E3d"><br>
<br>
Rob Rothwell wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
We are writing a little utility using Squeak as a processing server on the network, and a VB tray application as a client.<br>
<br>
Should I jump right into Sockets in Squeak, or is there something that can be done at a &quot;higher&quot; level using Swazoo to listen for a message and send back a result?<br>
</blockquote>
<br></div>
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&#39;s SpSocket, to be safely portable in the future.<br>
<br>
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&gt;Open&gt;File list, find .st, right click and &quot;remove line feeds&quot; then &quot;code-file browser&quot;...<br>

<br>
Janko<br><font color="#888888">
<br>
-- <br>
Janko Mivšek<br>
AIDA/Web<br>
Smalltalk Web Application Server<br>
<a href="http://www.aidaweb.si" target="_blank">http://www.aidaweb.si</a><br>
</font><br>&#39;From VisualWorks&reg;, 7.4.1 of May 30, 2006 on September 23, 2008 at 7:31:07 pm&#39;!<br>
<br>
<br>
Object subclass: #LineTCPConnect<br>
 &nbsp; &nbsp; &nbsp; &nbsp;instanceVariableNames: &#39;parent port socket loop content headers body &#39;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;classVariableNames: &#39;&#39;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;poolDictionaries: &#39;&#39;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;category: &#39;BiArt-Core&#39;!<br>
<br>
LineTCPConnect class<br>
 &nbsp; &nbsp; &nbsp; &nbsp;instanceVariableNames: &#39;&#39;!<br>
<br>
&quot;-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- &quot;!<br>
<br>
<br>
!LineTCPConnect class methodsFor: &#39;instance creation&#39; stamp: &#39; 23/9/08 19:31&#39;!<br>
<br>
newFor: anAIDASite<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^self new parent: anAIDASite! !<br>
<br>
!LineTCPConnect class methodsFor: &#39;defaults&#39; stamp: &#39; 23/9/08 19:31&#39;!<br>
<br>
defaultPort<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^8870! !<br>
<br>
&quot;-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- &quot;!<br>
<br>
<br>
LineTCPConnect comment:<br>
&#39;LineTCPConnect is a TCP server listening for connections from flowmeter measuring line sending measurenment data and documents.&#39;!<br>
<br>
!LineTCPConnect methodsFor: &#39;accessing&#39; stamp: &#39; 23/9/08 19:31&#39;!<br>
<br>
body<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^body!<br>
<br>
body: aString<br>
 &nbsp; &nbsp; &nbsp; &nbsp;body := aString!<br>
<br>
content<br>
 &nbsp; &nbsp; &nbsp; &nbsp;&quot;received content&quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^content!<br>
<br>
content: aByteArray<br>
 &nbsp; &nbsp; &nbsp; &nbsp;content := aByteArray!<br>
<br>
headers<br>
 &nbsp; &nbsp; &nbsp; &nbsp;headers isNil ifTrue: [self initHeaders].<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^headers!<br>
<br>
loop<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^loop!<br>
<br>
loop: aProcess<br>
 &nbsp; &nbsp; &nbsp; &nbsp;loop := aProcess!<br>
<br>
parent<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^parent!<br>
<br>
parent: anAIDASite<br>
 &nbsp; &nbsp; &nbsp; &nbsp;parent := anAIDASite!<br>
<br>
port<br>
 &nbsp; &nbsp; &nbsp; &nbsp;port isNil ifTrue: [self port: self class defaultPort].<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^port!<br>
<br>
port: aNumber<br>
 &nbsp; &nbsp; &nbsp; &nbsp;port := aNumber!<br>
<br>
socket<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^socket!<br>
<br>
socket: aSpSocket<br>
 &nbsp; &nbsp; &nbsp; &nbsp;socket := aSpSocket! !<br>
<br>
!LineTCPConnect methodsFor: &#39;start/stop&#39; stamp: &#39; 23/9/08 19:31&#39;!<br>
<br>
start<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self loop notNil ifTrue: [self stop].<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self socket: SpSocket newTCPSocket.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self socket<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;setAddressReuse: true;<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;bindSocketAddress: (SpIPAddress hostName: &#39;<a href="http://0.0.0.0" target="_blank">0.0.0.0</a>&#39; port: self port).<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self socket listenBackloggingUpTo: 10.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self loop: ([[self acceptConnection] repeat] fork)!<br>
<br>
stop<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self loop notNil ifTrue:<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[self loop terminate. self loop: nil].<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self socket notNil ifTrue:<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[self socket close. self socket: nil].! !<br>
<br>
!LineTCPConnect methodsFor: &#39;private-receiving&#39; stamp: &#39; 23/9/08 19:31&#39;!<br>
<br>
acceptConnection<br>
 &nbsp; &nbsp; &nbsp; &nbsp;| stream |<br>
 &nbsp; &nbsp; &nbsp; &nbsp;stream &nbsp;:= self socket accept underlyingSocket readStream binary.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self receiveContentFrom: stream.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self parseContent.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self postReceiveAction.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;stream close.!<br>
<br>
postReceiveAction<br>
 &nbsp; &nbsp; &nbsp; &nbsp;&quot;do whatever appropriate with a received content&quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;Transcript cr; show: &#39;received &#39;, self content size printString, &#39; bytes&#39;.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self parent isNil ifTrue: [^nil]. &quot;for testing purposes&quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self headers size &gt;= 2 ifFalse: [^nil].<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self parent calibrations<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;attachAndCloseFlowMeterMeasurementFile: self headers first key<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;content: self body<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;measuredBy: (AIDASite convert: self headers second key fromCodepage: #UTF8)<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tempAverage: (self valueForHeader: &#39;Tokolice&#39;)<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;tempDifference: (self valueForHeader: &#39;DeltaTokolice&#39;)<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;atmosphericPressure: (self valueForHeader: &#39;AtmTlak&#39;)<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;counterAtEnd: (self valueForHeader: &#39;Stanje&#39;)!<br>
<br>
receiveContentFrom: aStream<br>
 &nbsp; &nbsp; &nbsp; &nbsp;&quot;until peer close a socket, &nbsp;put it in content instvar&quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;| stream |<br>
 &nbsp; &nbsp; &nbsp; &nbsp;stream := WriteStream on: ByteString new.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;[[aStream atEnd] whileFalse:<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[stream nextPut: aStream next asCharacter] ]<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;on: Error<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;do: [:ex | ].<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self content: stream contents! !<br>
<br>
!LineTCPConnect methodsFor: &#39;initialize-release&#39; stamp: &#39; 23/9/08 19:31&#39;!<br>
<br>
initHeaders<br>
 &nbsp; &nbsp; &nbsp; &nbsp;headers := OrderedCollection new! !<br>
<br>
!LineTCPConnect methodsFor: &#39;private-parsing&#39; stamp: &#39; 23/9/08 19:31&#39;!<br>
<br>
parseContent<br>
 &nbsp; &nbsp; &nbsp; &nbsp;| stream |<br>
 &nbsp; &nbsp; &nbsp; &nbsp;stream := self content readStream.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self readHeadersFrom: stream.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self readBodyFrom: stream.!<br>
<br>
readBodyFrom: aStream<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self body: aStream upToEnd!<br>
<br>
readHeadersFrom: aStream<br>
 &nbsp; &nbsp; &nbsp; &nbsp;| line assoc |<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self initHeaders.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;[aStream atEnd] whileFalse:<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[line := aStream upTo: Character cr. aStream next. &quot;skip lf&quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;assoc := Association new.<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;assoc key: (line readStream upTo: $:).<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;assoc value: (line readStream upTo: $: ; upToEnd) trimBlanks.<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;self headers add: assoc.<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;aStream peek = Character cr ifTrue:<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;[aStream next; next. &quot;skip crl lf&quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;^self headers].<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;]!<br>
<br>
valueForHeader: aString<br>
 &nbsp; &nbsp; &nbsp; &nbsp;&quot;value from headers with that name&quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;&quot; &#39;DeltaTokolice 0.69700&#39;-&gt;&#39;&#39; &quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;| header |<br>
 &nbsp; &nbsp; &nbsp; &nbsp;header := self headers detect: [:each | (aString, &#39;*&#39;) match: each key] ifNone: [^nil].<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^((header key readStream upToSeparator; upToEnd) copyReplaceAll: &#39;.&#39; with: &#39;,&#39;)<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;asFixedPoint: 4! !<br>
<br>&#39;From VisualWorks&reg;, 7.4.1 of May 30, 2006 on September 23, 2008 at 7:31:11 pm&#39;!<br>
<br>
<br>
TestCase subclass: #LineTCPConnectTest<br>
 &nbsp; &nbsp; &nbsp; &nbsp;instanceVariableNames: &#39;&#39;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;classVariableNames: &#39;&#39;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;poolDictionaries: &#39;&#39;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;category: &#39;GeoMer-Tests&#39;!<br>
<br>
LineTCPConnectTest class<br>
 &nbsp; &nbsp; &nbsp; &nbsp;instanceVariableNames: &#39;&#39;!<br>
<br>
!LineTCPConnectTest methodsFor: &#39;testing&#39; stamp: &#39; 23/9/08 19:31&#39;!<br>
<br>
testParsing<br>
 &nbsp; &nbsp; &nbsp; &nbsp;| connect client |<br>
 &nbsp; &nbsp; &nbsp; &nbsp;connect := LineTCPConnect new port: 5678. &quot;to be different than default&quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;connect start.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;[client := SpSocket connectToServerOnHost: &#39;<a href="http://0.0.0.0" target="_blank">0.0.0.0</a>&#39; port: connect port.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;client write: self content.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;client close. (Delay forMilliseconds: 100) wait.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;] ensure: [client close. connect stop].<br>
 &nbsp; &nbsp; &nbsp; &nbsp;connect parseContent.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self assert: connect headers first = (&#39;file.dat&#39;-&gt; &#39;&#39;).<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self assert: connect headers last = (&#39;content-length&#39;-&gt; self body size printString).<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self assert: connect body = self body.!<br>
<br>
testSending<br>
 &nbsp; &nbsp; &nbsp; &nbsp;| connect client |<br>
 &nbsp; &nbsp; &nbsp; &nbsp;connect := LineTCPConnect new port: 5678. &quot;to be different than default&quot;<br>
 &nbsp; &nbsp; &nbsp; &nbsp;connect start.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;[client := SpSocket connectToServerOnHost: &#39;<a href="http://0.0.0.0" target="_blank">0.0.0.0</a>&#39; port: connect port.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;client write: self content.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;client close.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;(Delay forMilliseconds: 100) wait.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self assert: connect content = self content.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;] ensure: [client close. connect stop].!<br>
<br>
testStartup<br>
 &nbsp; &nbsp; &nbsp; &nbsp;| connect |<br>
 &nbsp; &nbsp; &nbsp; &nbsp;connect := LineTCPConnect new.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;connect start.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;[self assert: connect socket notNil.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self assert: connect loop notNil]<br>
 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ensure: [connect stop].<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self assert: connect socket isNil.<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self assert: connect loop isNil.! !<br>
<br>
!LineTCPConnectTest methodsFor: &#39;setup&#39; stamp: &#39; 23/9/08 19:31&#39;!<br>
<br>
body<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^&#39;body of the message&#39;.<br>
!<br>
<br>
content<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^&#39;file.dat&#39;, self crlf,<br>
 &nbsp; &nbsp; &nbsp; &nbsp;&#39;content-length: &#39;, self body size printString, self crlf,<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self crlf,<br>
 &nbsp; &nbsp; &nbsp; &nbsp;self body!<br>
<br>
crlf<br>
 &nbsp; &nbsp; &nbsp; &nbsp;^String with: Character cr with: Character lf! !<br>
<br></blockquote></div><br></div></div>