5.2 Stream Sockets

Instead of giving detailed explanations, we will follow an example tailored to understand how stream sockets for the Internet. For this purpose, we use the class Open.socket.

5.2.1 Initiating a Connection

Before any data between the server and the client can be exchanged, they must initiate a connection. How to obtain a connection is shown in Figure 5.1. We will perform the different steps shown in the figure as an example.

Figure 5.1: Initiating a Stream Connection.

The starting point for server and client are the topmost ovals on the two sides of the figure. We first turn our attention to the Server object. Basic initialization is performed by:

Server = {New Open.socket init}

After execution of this message, Server has initialized the local data structures it needs.

After locally initializing the socket we have to provide it with a global name. We use this global name for connecting our client to this server. Feed:

{Server bind(port:{Browse})}

The Internet protocol services generate a so-called port number. The port number is shown in the browser window. We refer to this number as OurPort later on.

The combination of the host computer on which Oz is running and the generated port number gives a unique address within the Internet domain. So any other party in the Internet domain may use this pair to refer unambiguously to this socket.

As next step, we signal that our socket is willing to accept connections from another socket. To do so, we feed:

{Server listen}

Now we are ready to accept our connection. Typing:

{Browse H#": "#P}
{Server accept(host:H port:P)}

does nothing at the moment. But if a connection request is signaled at port OurPort the connection is accepted. In this case H is bound to a string giving the name of the computer the connection request was received from. In the same way P is bound to the respective port.

The next step connects the Client to the Server.

Client = {New Open.socket init}

initializes our client object.

By typing

{Client connect(host:localhost port:OurPort)}

our client connects to the port OurPort on the same computer (thus the virtual string localhost).

Since our server suspended on a connection request at port OurPort it now resumes, and the name of the host and the port appears in the Browser window.

5.2.2 Convenient Connection Establishment

This was the whole story in detail, but for convenience we provide two methods, which perform the complete connection establishment.

Instead of the various method applications, the following suffices for the server:

{Server server(host:H port:P)}

Here P is bound to the automatically generated port number. After a connection has been accepted on this port, H is bound to the string containing the name of the connecting host.

Symmetrically, for the client side, one simply can use:

{Client client(host: HostToConnectTo 

5.2.3 Exchanging Data

Now we can await data at our server by feeding

{Server read(list:{Browse})}

Sending data from our client can be accomplished via

{Client write(vs:'Hello, good'#" old Server!")}

and in the browser window (on the server side) the message appears.

By simply flipping Server and Client in the last two expressions fed, you can send data from the server to the client.

Notice, however, that in this example server and client are created in the same operating system process on the same host computer. Instead we could have used two different processes and two different host computers even running two different operating systems.

5.2.4 Concurrency Issues

Both reading and writing to a socket might not be possible immediately. Possible reasons include:

  1. The data to be written is not yet bound to a virtual string.

  2. There is no data to be read at the moment.

  3. The socket can not transfer data over the network at the moment.

In this case access to the socket has to be synchronized in some way. The following invariant is kept: all read messages are performed in the order they were received by the object. The same holds for all write messages. But there is no order between reads or writes. Both the read and the write method reduce only when the request can be served.

If you want to make sure that both all read and write requests are finished, you can use the method flush.

5.2.5 Disconnecting

If there is no further data to be exchanged between client and server, they should be disconnected. The simplest way to do so is by feeding

{Server close}
{Client close}

Suppose that the server closes before the client, and the client is still sending data to the server. In this case the data will still arrive at the socket. And only after some time the data will be thrown away, consuming valuable memory resources. To prevent from this, you can signal that you are not interested in receiving any messages, simply by typing (before closing the object!)

{Server shutDown(how: [receive])}

If we are not interested in sending data anymore, we can inform the operating system that we are indeed not willing to send anything more, thus

{Server shutDown(how: [send])}

Both applications could have been combined into

{Server shutDown(how: [receive send])}

5.2.6 Exceptions

The methods of the class Open.socket might raise exceptions of the same format as already described for the class Open.file, see Section 3.1.

Christian Schulte
Version 1.4.0 (20080702)