ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

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.