Netcat[20] is most often used to make a TCP connection from a script or a command line, which is great for testing servers; but, it can also receive connections from the command line, which is how we’ll use it here, to test our client program.
What we want to do is receive a connection on a socket, get a notification when a connection is established, and then write out whatever data we receive. If you’re using the Docker container environment, you’ll also want to make sure to set up this socket inside the same container that contains our Scala Native build environment. If that container isn’t running, first boot it back up with docker run -it -p 8080:8080 --name scala-native rwhaling/scala-native-env. Then, inside the container, run:
| $ nc -l -v 127.0.0.1 8080 |
Although this only prints a blank line of output, we’re now receiving connections, and the container has mapped to port 8080 on the host.
Now, we should be able to connect to this port/socket combination, either from inside or outside of the container. Finally, run this:
| $ nc -v 127.0.0.1 8080 |
| Connection to 127.0.0.1 8080 port [tcp/http-alt] succeeded! |
Now, if you type in a line of text, you should see it copied from one shell session to the other. This works from either the client or the server, reflecting the bidirectional, streaming nature of the TCP protocol.
If you want, take some time to experiment with netcat, or look over the manual page if you want. Once you’re comfortable, we can test out our Scala code. Make sure the netcat client session is shut down and the server is listening for a connection. Then, run our program with ./tcpTest 127.0.0.1 8080. If everything worked well, you should see output like this on the client:
| $ ./target/scala-2.11/tcpclient-out 127.0.0.1 8081 |
| looking up address: 127.0.0.1 port: 8081 |
| about to perform lookup |
| lookup returned 0 |
| got addrinfo: flags 2, family 0, socktype 1, protocol 6 |
| creating socket |
| socket returned fd 3 |
| connecting |
| connect returned 0 |
And you’ll see this on the server:
| $ nc -v -l 127.0.0.1 8080 |
| hello? is anybody there? |
Right now, the client is hanging, waiting for a response from the server. Now, we can actually type a response into the netcat terminal, which will send it back to the client; after that, the client will close the connection, as expected. So we’d see this on the server:
| $ nc -v -l 127.0.0.1 8081 |
| hello? is anybody there? |
| yep!! |
And here’s how the full exchange on the client now looks:
| $ ./target/scala-2.11/tcpclient-out 127.0.0.1 8081 |
| looking up address: 127.0.0.1 port: 8081 |
| about to perform lookup |
| lookup returned 0 |
| got addrinfo: flags 2, family 0, socktype 1, protocol 6 |
| creating socket |
| socket returned fd 3 |
| connecting |
| connect returned 0 |
| I got a response: yep!! |
| done |
So, what have we done so far? We’ve used UNIX system calls to create a TCP socket, connect to a remote server, send a message, and read a response. Our makeConnection() function can connect to any IP address or hostname, and our handleConnection() function signature can handle any pattern or format of input and output. We could implement just about any TCP client protocol on top of this framework, but for now, we’ll focus on HTTP, the protocol that powers the world wide web.