Zero Configuration Networking: Using the Java APIs, Part 3
Pages: 1, 2, 3
Example 8-8. GameBoard.java
import java.nio.*;
import java.nio.channels.SocketChannel;
import java.net.InetSocketAddress;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import com.apple.dnssd.*;
public class GameBoard extends JFrame implements ResolveListener, Runnable
{
private TicTacToe tictactoe;
private String name, host;
private int port;
SocketChannel channel;
// If we're passed in a SocketChannel, it means we received an
// incoming connection, so we should start receiving clicks from it.
// If channel is null, it means our user initiated an outgoing connection,
// so we'll get a serviceResolved callback to tell us when to proceed.
public GameBoard(TicTacToe t, String n, SocketChannel c)
{
super(n);
tictactoe = t;
name = n;
channel = c;
tictactoe.activeGames.put(n, this);
getContentPane( ).setLayout(new GridLayout(3,3,6,6));
getContentPane( ).setBackground(Color.BLACK);
for (int i = 0; i<9; i++) getContentPane( ).add(new SquareGUI(this, i));
setSize(200,200);
setVisible(true);
if (channel != null) new Thread(this).start( );
}
public void operationFailed(DNSSDService service, int errorCode)
{
System.out.println("DNS-SD operation failed " + errorCode);
System.exit(-1);
}
// When serviceResolved is called, we send our name to the other end
// and then fire off our thread to start receiving the opponent's clicks.
public void serviceResolved(DNSSDService resolver, int flags, int ifIndex,
String fullName, String theHost, int thePort, TXTRecord txtRecord)
{
host = theHost;
port = thePort;
ByteBuffer buffer = ByteBuffer.allocate(4 + 128);
CharBuffer charBuffer = buffer.asCharBuffer( );
buffer.putInt(0, tictactoe.myName.length( ));
charBuffer.position(2);
charBuffer.put(tictactoe.myName);
try
{
InetSocketAddress socketAddress = new InetSocketAddress(host, port);
channel = SocketChannel.open(socketAddress);
channel.write(buffer);
new Thread(this).start( );
}
catch (Exception e) { e.printStackTrace( ); }
resolver.stop( );
}
// The GameBoard's run( ) method just sits in a loop receiving
// clicks from the opponent and marking the indicated squares.
public void run( )
{
try
{
while (true)
{
ByteBuffer buffer = ByteBuffer.allocate(4);
channel.read(buffer);
int n = buffer.getInt(0);
if (n >= 0 && n < 9)
{
try { SwingUtilities.invokeAndWait(new SquareMarker(n)); }
catch (Exception e) { e.printStackTrace( ); }
}
}
}
catch (Exception e) { } // Connection reset by peer!
}
// When we get a message from the opponent, we make a SquareMarker
// object and run it on the event-dispatching thread so it can
// safely do Swing calls to update the user interface
class SquareMarker implements Runnable
{
private int num;
public SquareMarker(int n) { num = n; }
public void run( )
{
SquareGUI s = (SquareGUI)getContentPane( ).getComponent(num);
s.setText("<html><h1><font color='blue'>O</font></h1></html>");
s.setEnabled(false);
}
}
// Each GameBoard contains nine JButtons displayed in a 3x3 grid
class SquareGUI extends JButton implements ActionListener
{
private int num;
public SquareGUI(GameBoard b, int n) { num = n; addActionListener(this); }
public void actionPerformed(ActionEvent event)
{
// Mark our square with an X
setText("<html><h1><font color='red'>X</font></h1></html>");
setEnabled(false);
// And tell the other end to mark the square too
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.putInt(0, num);
try { channel.write(buffer); }
catch (Exception e) { e.printStackTrace( ); }
}
}
}
Example 8-9. Makefile to build Tic-Tac-Toe example
run: TicTacToe.jar
java -jar TicTacToe.jar &
clean:
rm -rf classes TicTacToe.jar
TicTacToe.jar: classes/TicTacToe.class
@echo "Main-Class: TicTacToe" > Main-Class.txt
jar cmf Main-Class.txt TicTacToe.jar -C classes .
@rm Main-Class.txt
# Building TicTacToe.class causes javac automatically
# to find and build other necessary classes too
classes/TicTacToe.class: TicTacToe.java GameBoard.java
mkdir -p classes
javac -encoding UTF8 -d classes TicTacToe.java
You have now seen how to implement a basic service in Java and advertise it using DNS-SD. The TicTacToe application registers and browses for services of type _tic-tac-toe-ex._tcp. You have resolved services and provided the underlying plumbing to send and receive messages. The remainder of the code managed the GUI elements.
With just a few lines of code, you can add DNS-SD advertising and browsing to your own Java programs.
Daniel H. Steinberg is the editor for the new series of Mac Developer titles for the Pragmatic Programmers. He writes feature articles for Apple's ADC web site and is a regular contributor to Mac Devcenter. He has presented at Apple's Worldwide Developer Conference, MacWorld, MacHack and other Mac developer conferences.
Stuart Cheshire is currently a Senior Scientist with Apple Computer, specializing in Internet Protocols.
View catalog information for Zero Configuration Networking: The Definitive Guide
Return to ONJava.com.