HomeForumResearchGuide
These programs demonstrate how to create a TCP server and a TCP client. The client sends some data to the server, and the server prints the data out.

We start with the server side. Create a new file Server.dn and open it in a text editor. We're going to use the TCPServerSocket interface, the per-stream TCPSocket interface, and the command-line output interface, so we'll start with this code in our new file:

component provides App requires io.Output out, net.TCPServerSocket,
				net.TCPSocket {

}

The App interface has one function called main. Any runnable component will implement App, indicating it has a main method that Dana can launch.

We now need to implement our App interface, which we'll do like this:

component provides App requires io.Output out, net.TCPServerSocket,
				net.TCPSocket {

	void streamHandler(TCPSocket s)
		{
		char buf[]
		while ((buf = s.recv(64)).arrayLength > 0)
			out.print("$buf")
		s.disconnect()
		}

	int App:main(AppParam params[])
		{
		TCPServerSocket host = new TCPServerSocket()
		host.bind(TCPServerSocket.ANY_ADDRESS, 2020)

		out.println("Waiting for clients...")

		while (true)
			{
			TCPSocket client = new TCPSocket()

			if (client.accept(host))
				asynch::streamHandler(client)
			}

		return 0
		}
	}

This program instantiates a new TCP server socket, and binds it to the local host at port 2020. The ANY_ADDRESS constant means that we'll listen for both IPv4 and IPv6 connections. We then use a loop to accept new client connections. For each connection, we first instantiate a new TCPSocket, then we accept a new connection on that socket. If we successfully accept a new connection, we then launch a new thread for our streamHandler() helper function, and pass the new socket as a parameter.

The last thing we need to do is fill in the details of the streamHandler() function; here we just use a loop to read chunks of 64 bytes from the socket, and print those bytes to the command line. The TCPSocket.recv() function will return zero if there was an error reading a non-zero number of bytes; if this happens we assume the socket is closed from the client side and we stop reading new data.

Next we'll write the client side. Create a new file Client.dn and open it in a text editor. Here we'll just use the TCPSocket interface, and use it to connect to our server and send some data:

component provides App requires io.Output out, net.TCPSocket {

	int App:main(AppParam params[])
		{
		TCPSocket client = new TCPSocket()

		if (client.connect("127.0.0.1", 2020))
			{
			client.send(new char[](params[0].string, "\n"))
			client.disconnect()
			}

		return 0
		}
	}

Our client program assumes that we provide a command-line parameter as our data to send. It then tries to connect to the local host address, at the same port on which the server is listening. If the connection is successful, we then send our data on the connected TCP socket, and close the connection.

We can now compile both programs using the Dana compiler. Open a command-prompt in the directory containing your files and type:

dnc Server.dn

dnc Client.dn

Pressing enter after each command. This will compile your components.

We run both programs using Dana's interpreter. We'll now need two different command line windows in the same directory; in the first one we can type:

dana Server

And press enter.

In the other command line window we type:

dana Client hello!

And press enter. You should see your message printed out at the server.

The full list of standard interfaces that you can use in requires directives can be viewed at the Dana API pages along with documentation of each one. In this example we used: