-
Notifications
You must be signed in to change notification settings - Fork 4
[Tutorial 3.2] The Networking Framework
See the readme on the main page for the correct documentation.
[Back to the previous tutorial]([Tutorial 3.1] The Concurrency Framework)
The CCRE is intended to run both on the cRIO and on normal desktop Java SE, among other platforms. However, some platforms (read: the Squawk JVM, on the cRIO) don't do networking in a helpful way and so we need to provide an abstraction to access the network.
In this tutorial, we'll write a simple message-of-the-day server.
You can run all of this either on a robot or on a desktop computer - that's the power of this abstraction! If you put it on the robot, however, you will need to put it in a new thread because the main thread is needed to run the robot.
For full understanding, you will need to understand networking at least a small amount already.
Let's start by binding to TCP port 17:
ServerSocket sock = Network.bind(17);
We now want to accept a connection from a client:
ClientSocket client = sock.accept();
And then send the client our message of the day.
OutputStream outp = client.openOutputStream();
outp.write("To win, you must win.\n".getBytes());
outp.close();
client.close();
And we close down our server:
sock.close();
If you have any experience with networking, you'll know that this isn't the best way to do things, but bear with me.
Now, let's connect to this server.
ClientSocket motd = Network.connect("127.0.0.1", 17); // You can put a different computer's IP address instead of 127.0.0.1 if you want to use another computer.
And then read data and print it:
byte[] data = new byte[512];
InputStream sin = motd.openInputStream();
BufferedReader bread2 = new BufferedReader(new InputStreamReader(sin));
System.out.println(bread2.readLine());
And close everything down:
sin.close();
motd.close();
You can try these now, if you want. You'll see that the client will receive the message of the day from the server.
Now, you will have noticed that you can only connect once, and then you have to restart the server.
Add a while (true)
loop around the lines starting at sock.accept()
and ending at client.close()
. You may need to remove the final sock.close()
line because it won't ever be reached.
Let's also make it do something more useful:
ServerSocket sock = Network.bind(17);
while (true) {
ClientSocket client = sock.accept();
OutputStream outp = client.openOutputStream();
outp.write("To win, you must win.\n".getBytes());
InputStream inp = client.openInputStream();
while (true) {
int c = inp.read();
if (c == -1 || c == '\n') {
break;
}
outp.write(c);
}
inp.close();
outp.close();
client.close();
}
And a similar part on the other end:
BufferedReader bread = new BufferedReader(new InputStreamReader(System.in));
out.write((bread.readLine() + "\n").getBytes());
System.out.println(bread2.readLine());
Try that - after the message is received, type a line and notice how the server echos it back. Now, notice that you can only have one client connected at once - try to connect again and it'll wait until the previous client finishes.
This is obviously suboptimal. The way to do this is to use one thread per client - but this can be complicated to set up. Luckily, we have an easy way to do this!
ConnectionReceiverThread combines everything needed to run a server into a single object.
Let's replace our server with this code:
new ConnectionReceiverThread("My Friendly Server", 17) {
@Override
protected void handleClient(ClientSocket conn) throws Throwable {
OutputStream outp = conn.openOutputStream();
InputStream inp = conn.openInputStream();
outp.write("To win, you must win.\n".getBytes());
while (true) {
int c = inp.read();
if (c == -1 || c == '\n') {
break;
}
outp.write(c);
}
}
}.start();
As you can see, it's the same code to handle each client, but inside the handleClient()
method of a ConnectionReceiverThread. ConnectionReceiverThread takes care of accepting connections and starting new threads for each client.
Sometimes, you give a user the ability to configure the IP address that you connect to, and you want them to be able to modify the port without having two different settings that can be changed. We could use:
ClientSocket motd = Network.connectDynPort("127.0.0.1", 17);
connectDynPort()
can also take a more complex address to solve this issue, such as: "127.0.0.1:1234"
. The 1234 in that address, if present, overrides the port number specified in the argument of connectDynPort()
. This is used by the Cluck communication system to let advanced users reconfigure Cluck remote ports easily.
Sometimes it's important to know the IP address of the current computer for whatever reason - the Poultry Inspector uses this functionality to figure out if it's connected to the robot or not.
CCollection<String> cc = Network.listIPv4Addresses();
This collection includes all local IP addresses, for example: [127.0.0.1, 10.123.142.16]
or [127.0.0.1, 10.15.40.2]
.
Next: [The Storage Framework]([Tutorial 3.3] The Storage Framework)