Building the server application is a more complex exercise than building the client. First, much of the implementation must be done in scripts, because the VisualAge TCP/IP parts are aimed at building client applications.
Your server application must always be listening for a call and it must provide some mechanism for halting communications. With these requirements and structure in mind, let's go ahead and build the TCP/IP server application.
In the VisualAge Organizer window, create a new visual part in MyTCPSampleApp named MyTCPSampleServerView. When the Composition Editor window opens, switch to the Public Interface Editor.
The server application needs to manipulate the data that is received, send it back to the client, and keep track of when it should halt communications. In this section you will define attributes for these items and have the VisualAge script generator build some of the necessary scripts.
Switch to the Attributes page and add the attributes as follows:
Now that the attributes are defined you can generate the default scripts. From the File menu, select Generate Default Scripts. When the Generate Default Scripts window appears, select Generate all.
Switch to the Script Editor.
The default setter scripts for the attributes can be used without any changes. However, the getter scripts need to be changed to establish default values for the attributes. Make these changes as follows:
Change the closeServer script so it looks like the following:
closeServer "Return the value of closeServer." closeServer isNil ifTrue: [ self closeServer: false]. ^closeServer
Change the dataReceived script so it looks like the following:
dataReceived "Return the value of dataReceived." dataReceived isNil ifTrue: [ self dataReceived: ' ']. ^dataReceived
Change the dataSent script so it looks like ths following:
dataSent "Return the value of dataSent." dataSent isNil ifTrue: [ self dataSent: ' ']. ^dataSent
Add the following script to allow the user to halt communications:
stopServer "Stop the server" self closeServer: true
Add the following script that will run when the user starts the server:
startServerName: aServerName usingPort: aPortNumber "Start a TcpIp server, aServerName, using port, aPortNumber" | host address result clientSocket dataSet svrSocket | "Create the host" (host := AbtTCPInetHost getHostByName: aServerName) isCommunicationsError ifTrue: [Transcript cr; show: (host codesAsString). ^self]. Transcript cr; show: 'Host name obtained'. "Create the port" (address := AbtTCPPort usingHost: host portNumber: aPortNumber) isCommunicationsError ifTrue: [Transcript cr; show: address codesAsString. ^self]. Transcript cr; show: 'Host port obtained'.
"Create the socket" (svrSocket := AbtSocket newStreamUsingPort: address) isCommunicationsError ifTrue: [Transcript cr; show: svrSocket codesAsString. ^self]. Transcript cr; show: 'Host socket obtained'. "Bind" (result := svrSocket bind) isCommunicationsError ifTrue: [Transcript cr; show: result codesAsString. ^self]. Transcript cr; show: 'Host bind ok'. (result := svrSocket listen: 10) isCommunicationsError ifTrue: [Transcript cr; show: (result codesAsString). ^self]. Transcript cr; show: 'Listen ok'.
"Continue listening until the server is stopped or an error occurs" [ self closeServer ] whileFalse: [ Transcript cr; show: 'Still listening. . .'. (clientSocket := svrSocket accept) isCommunicationsError ifTrue: [Transcript cr; show: clientSocket codesAsString. ^self]. Transcript cr; show: 'Accept ok'. (clientSocket isConnected) ifTrue: [ (result := clientSocket receive) isCommunicationsError ifTrue: [ Transcript cr; show: result codesAsString. ^self] ifFalse: [ self dataReceived: (result asString). Transcript cr; show: 'Server receive ok'. self dataSent: (dataReceived asUppercase). (result := clientSocket sendData: dataSent asBytes) isCommunicationsError ifTrue: [ Transcript cr; show: result codesAsString. ^self] ifFalse: [ Transcript cr; show: 'Server send ok with data:'; cr; show: (self dataReceived)]. ]. "end of receive ok" ]. "end of connected ok"
"close the connection" (result := clientSocket soclose) isCommunicationsError ifTrue: [ Transcript cr; show: 'Client'; cr; show: (result codesAsString). ^self] ifFalse: [ Transcript cr; show: 'Client close ok']. ]. "end of closeServer = true loop" "close the server's socket" (result := svrSocket soclose) isCommunicationsError ifTrue: [ Transcript cr; show: result codesAsString. ^self] ifFalse: [ Transcript cr; show: 'Server close ok'. ^self].
Switch to the Composition Editor. Create a view, using Text
parts, Label parts, and Push Button parts that looks likes the
following:
Open the settings for the Port Text part and under the converter attribute, change its Data type to Integer.
Make the connections as follows:
The server will now open the specified socket whenever the user selects the Start push button.
When you are finished making the connections, your part will look like this
in the Composition Editor:
Your server is now complete so save the part and close the Composition Editor.
Test your parts as follows:
You should see the following in your Transcript window.
Host name obtained Host port obtained Host socket obtained Host bind ok Listen ok Still listening. . .
You should see the same characters in upper case in the Data received Text part of the client. The same strings should also be visible in the server window. You should see the following added to your Transcript window:
Accept ok Server receive ok Server send ok with data: <<the string you typed>> Client close ok Still listening. . .
('ECONNREFUSED')
Close your test windows.