/* SocketBallsApplication.java Copyright (c) Kari Laitinen http://www.naturalprogramming.com/ 2010-01-22 File created. 2010-02-08 Last modification. This program is a Java application that shows the basics of socket-based communications between two computers. This program works either as a Balls Server or Balls Client. The server is able to send a Ball object to the client whenever the client requests one. The balls are objects of class Ball that is declared at the beginning of this program. Class Ball in this program differs from the Ball class in Ball.java. Serialization is the process of transforming an object into a byte stream before sending it across a communications channel (e.g. a socket connection). On arrival, the byte stream is reassembled into an object and used locally. This program is an example of serialization of Java objects. The serialized Ball objects can be transferred between two computers. */ import java.io.* ; import java.net.* ; import java.awt.* ; import java.awt.event.* ; import javax.swing.* ; import java.awt.geom.* ; import java.util.ArrayList ; import java.util.Random ; class Ball implements Serializable { int ball_center_point_x = 0 ; int ball_center_point_y = 0 ; Color ball_color = Color.red ; int ball_diameter = 100 ; boolean this_ball_is_activated = false ; public Ball( int given_center_point_x, int given_center_point_y, Color given_color ) { ball_center_point_x = given_center_point_x ; ball_center_point_y = given_center_point_y ; ball_color = given_color ; } public void activate_ball() { this_ball_is_activated = true ; } public void deactivate_ball() { this_ball_is_activated = false ; } public int get_ball_center_point_x() { return ball_center_point_x ; } public int get_ball_center_point_y() { return ball_center_point_y ; } public int get_ball_diameter() { return ball_diameter ; } public void move_right() { ball_center_point_x += 3 ; } public void move_left() { ball_center_point_x -= 3 ; } public void move_up() { ball_center_point_y -= 3 ; } public void move_down() { ball_center_point_y += 3 ; } public void move_this_ball( int movement_in_direction_x, int movement_in_direction_y ) { ball_center_point_x = ball_center_point_x + movement_in_direction_x ; ball_center_point_y = ball_center_point_y + movement_in_direction_y ; } public void move_to_position( int new_center_point_x, int new_center_point_y ) { ball_center_point_x = new_center_point_x ; ball_center_point_y = new_center_point_y ; } public void shrink() { // The if-construct ensures that the ball does not become // too small. if ( ball_diameter > 10 ) { ball_diameter -= 6 ; } } public void enlarge() { ball_diameter += 6 ; } public void set_diameter( int new_diameter ) { if ( new_diameter > 5 ) { ball_diameter = new_diameter ; } } public void set_color( Color new_color ) { ball_color = new_color ; } public boolean contains_point( Point given_point ) { int ball_radius = ball_diameter / 2 ; // Here we use the Pythagorean theorem to calculate the distance // from the given point to the center point of the ball. // See the note at the end of this file. int distance_from_given_point_to_ball_center = (int) Math.sqrt( Math.pow( ball_center_point_x - given_point.x, 2 ) + Math.pow( ball_center_point_y - given_point.y, 2 ) ) ; return ( distance_from_given_point_to_ball_center <= ball_radius ) ; } public boolean intersects_rectangle( Rectangle2D given_rectangle ) { Area this_ball_area = new Area( new Ellipse2D.Float( ball_center_point_x - ball_diameter / 2, ball_center_point_y - ball_diameter / 2, ball_diameter, ball_diameter ) ) ; return this_ball_area.intersects( given_rectangle ) ; } public void draw( Graphics graphics ) { graphics.setColor( ball_color ) ; graphics.fillOval( ball_center_point_x - ball_diameter / 2, ball_center_point_y - ball_diameter / 2, ball_diameter, ball_diameter ) ; graphics.setColor( Color.BLACK ) ; graphics.drawOval( ball_center_point_x - ball_diameter / 2, ball_center_point_y - ball_diameter / 2, ball_diameter, ball_diameter ) ; // If this ball is activated, it will have a thick black edge if ( this_ball_is_activated == true ) { graphics.drawOval( ball_center_point_x - ball_diameter / 2 + 1, ball_center_point_y - ball_diameter / 2 + 1, ball_diameter - 2, ball_diameter - 2 ) ; graphics.drawOval( ball_center_point_x - ball_diameter / 2 + 2, ball_center_point_y - ball_diameter / 2 + 2, ball_diameter - 4, ball_diameter - 4 ) ; } } } class BallsPanel extends JPanel { ArrayList balls_to_be_shown = new ArrayList() ; String message_below_the_balls = "No activities." ; Font font_for_text = new Font( "Serif", Font.PLAIN, 14 ) ; protected void set_message_text( String new_message_text ) { message_below_the_balls = new_message_text ; System.out.print( "\n Message " + message_below_the_balls ) ; repaint() ; } protected void add_to_message_text( String continuation_to_message_text ) { message_below_the_balls = message_below_the_balls + " " + continuation_to_message_text ; System.out.print( "\n Message " + message_below_the_balls ) ; repaint() ; } public void paintComponent( Graphics graphics ) { super.paintComponent( graphics ) ; graphics.setColor( Color.BLACK ) ; graphics.fillRect( 0, 0, getWidth(), getHeight() ) ; for ( int ball_index = 0 ; ball_index < balls_to_be_shown.size() ; ball_index ++ ) { ( balls_to_be_shown.get( ball_index ) ).draw( graphics ) ; } graphics.setColor( Color.WHITE ) ; graphics.setFont( font_for_text ) ; graphics.drawString( message_below_the_balls, 10, getHeight() - 40 ) ; } } class BallsServerPanel extends BallsPanel implements Runnable { private Thread thread_to_run_balls_server ; private boolean server_thread_should_be_run = true ; private ObjectOutputStream output_stream ; private ObjectInputStream input_stream ; private int number_of_connections_made = 0 ; private Socket socket_to_client ; private void create_new_ball_objects() { balls_to_be_shown.clear() ; // Let's create balls that have a random color. Random random_number_generator = new Random() ; // The following loops create 9 balls. for ( int ball_center_point_y = 120 ; ball_center_point_y < 600 ; ball_center_point_y += 200 ) { for ( int ball_center_point_x = 124 ; ball_center_point_x < 600 ; ball_center_point_x += 200 ) { Ball new_ball = new Ball( ball_center_point_x, ball_center_point_y, new Color( random_number_generator.nextInt( 0xFFFFFF ) ) ) ; new_ball.set_diameter( 180 ) ; balls_to_be_shown.add( new_ball ) ; } } } public BallsServerPanel() { create_new_ball_objects() ; thread_to_run_balls_server = new Thread( this ) ; thread_to_run_balls_server.start() ; } public void stop_balls_server() { if ( thread_to_run_balls_server != null ) { thread_to_run_balls_server.interrupt() ; } if ( socket_to_client != null ) { try { socket_to_client.close() ; } catch( IOException caught_exception ) { System.out.print( "\n Socket closing problem ??? " ) ; } } server_thread_should_be_run = false ; System.out.print( "\n Server termination attempted." ) ; } public void run() { set_message_text( "Starting server..." ) ; // set up server to receive connections; // process connections try { // Step 1: Create a ServerSocket. ServerSocket server_socket = new ServerSocket( 5040, 100 ); // Step 2: Wait for a connection. set_message_text( "Waiting client..." ) ; // Method accept() waits until a client makes a connection and // returns a Socket object that will be used in the actual // communications operations. socket_to_client = server_socket.accept() ; number_of_connections_made ++ ; add_to_message_text( "Connection " + socket_to_client.getInetAddress().getHostName() ) ; // Step 3: Get input and output streams. // set up output stream for objects output_stream = new ObjectOutputStream( socket_to_client.getOutputStream() ) ; // flush output buffer to send header information output_stream.flush(); // set up input stream for objects input_stream = new ObjectInputStream( socket_to_client.getInputStream() ); add_to_message_text( " Streams OK" ); while ( server_thread_should_be_run == true ) { try { // The following statement waits until the client // sends an object, or the socket is closed by the // stop_balls_server() method. In the latter case // it seems that a SocketException is thrown. String received_string = (String) input_stream.readObject() ; set_message_text( "FROM CLIENT: " + received_string ) ; Random random_number_generator = new Random() ; int random_ball_index = random_number_generator.nextInt( balls_to_be_shown.size() ) ; Ball ball_to_be_sent = balls_to_be_shown.get( random_ball_index ) ; balls_to_be_shown.remove( random_ball_index ) ; output_stream.writeObject( ball_to_be_sent ) ; output_stream.flush() ; if ( balls_to_be_shown.size() == 0 ) { // All balls have been sent. Let's create new balls. create_new_ball_objects() ; set_message_text( "New balls created." ) ; } } catch ( ClassNotFoundException classNotFoundException ) { System.out.print( "\n Unknown object type received" ); } catch ( SocketException caught_socket_exception ) { System.out.print( "\n SocketException caught" ); } try { Thread.sleep( 500 ) ; } catch ( InterruptedException caught_interrupted_exception ) { } System.out.print( " S" ) ; } // After the client sends the text "CLIENT>>> TERMINATE", // the current connection will be closed, and the server starts // waiting for a new connection. System.out.print( "\n Client terminated connection" ) ; output_stream.close() ; input_stream.close() ; socket_to_client.close() ; } // process EOFException when client closes connection catch ( EOFException eofException ) { System.out.println( "Client terminated connection" ) ; } catch ( IOException ioException ) { ioException.printStackTrace(); } set_message_text( "Server thread terminated." ) ; } // end of run() } class BallsClientPanel extends BallsPanel implements Runnable, ActionListener { private Thread thread_to_run_balls_client = null ; private boolean client_thread_should_be_run = true ; private boolean ball_requesting_enabled = false ; private ObjectOutputStream output_stream; private ObjectInputStream input_stream; private String server_address = "127.0.0.1" ; private int server_port_number = 5040 ; JButton get_ball_button = new JButton( "Get Ball" ) ; public BallsClientPanel( String given_server_address, int given_server_port_number ) { server_address = given_server_address ; server_port_number = given_server_port_number ; JPanel operations_panel = new JPanel() ; get_ball_button.addActionListener( this ) ; operations_panel.add( get_ball_button ) ; operations_panel.setBackground( Color.BLACK ) ; setLayout( new BorderLayout() ) ; add( "South", operations_panel ) ; //add( "South", get_ball_button ) ; thread_to_run_balls_client = new Thread( this ) ; thread_to_run_balls_client.start(); } public void enable_ball_requesting() { ball_requesting_enabled = true ; } public void run() { try { // Step 1: Create a Socket to make connection set_message_text( "Connecting..." ); Socket socket = new Socket( InetAddress.getByName( server_address ), server_port_number ) ; // display connection information add_to_message_text( "Connected to: " + socket.getInetAddress().getHostName() ) ; // Step 2: Get the input and output streams for objects output_stream = new ObjectOutputStream( socket.getOutputStream() ); output_stream.flush(); // flush output to send header information input_stream = new ObjectInputStream( socket.getInputStream() ); add_to_message_text( " Streams OK" ); while ( client_thread_should_be_run == true ) { if ( ball_requesting_enabled == true ) { output_stream.writeObject( "Client wants a ball." ) ; output_stream.flush() ; try { Ball new_ball_from_server = (Ball) input_stream.readObject(); if ( balls_to_be_shown.size() >= 9 ) { // We'll delete the old balls. balls_to_be_shown.clear() ; } balls_to_be_shown.add( new_ball_from_server ) ; set_message_text( "FROM SERVER: " + new_ball_from_server ) ; ball_requesting_enabled = false ; } catch ( ClassNotFoundException classNotFoundException ) { System.out.print( "\nUnknown object type received" ); } } else { // No ball requesting command has been given // Let's wait a little bit try { Thread.sleep( 500 ) ; } catch ( InterruptedException caught_interrupted_exception ) { } } } // Step 4: Close connection add_to_message_text( "Closing..." ); output_stream.close(); input_stream.close(); socket.close(); } catch ( EOFException eofException ) { System.out.print( "\nServer terminated connection" ); } catch ( IOException ioException ) { ioException.printStackTrace(); } add_to_message_text( "Thread terminated." ) ; } // end of run() method public void stop_balls_client() { if ( thread_to_run_balls_client != null ) { thread_to_run_balls_client.interrupt() ; } client_thread_should_be_run = false ; } public void actionPerformed( ActionEvent event ) { if ( event.getSource() instanceof JButton ) { if ( event.getSource() == get_ball_button ) { enable_ball_requesting() ; } } } } class SocketBallsFrame extends JFrame implements ActionListener, WindowListener { public static final int FRAME_WIDTH = 660 ; public static final int FRAME_HEIGHT = 720 ; JButton server_button = new JButton( "RUN AS BALLS SERVER" ) ; JButton client_button = new JButton( "RUN AS BALLS CLIENT" ) ; JTextField server_address_field = new JTextField( "127.0.0.1", 30 ) ; JTextField server_port_number_field = new JTextField( "5040", 4 ) ; JPanel operations_panel ; BallsServerPanel balls_server_panel ; BallsClientPanel balls_client_panel ; public SocketBallsFrame() { setTitle( "SELECT APPLICATION OPERATION MODE" ) ; setSize( 500, 150 ) ; operations_panel = new JPanel( new GridLayout( 3, 2, 10, 10 ) ) ; //operations_panel.setBounds( 100, 50, 300, 150 ) ; server_button.addActionListener( this ) ; client_button.addActionListener( this ) ; operations_panel.add( server_button ) ; operations_panel.add( client_button ) ; operations_panel.add( new Label( "Server address (URL):" ) ) ; operations_panel.add( server_address_field ) ; operations_panel.add( new Label( "Server port number: " ) ) ; operations_panel.add( server_port_number_field ) ; getContentPane().add( operations_panel ) ; addWindowListener( this ) ; } public void actionPerformed( ActionEvent event ) { if ( event.getSource() instanceof JButton ) { if ( event.getSource() == server_button ) { balls_server_panel = new BallsServerPanel() ; setTitle( "SOCKET BALLS SERVER" ) ; setSize( FRAME_WIDTH, FRAME_HEIGHT ) ; getContentPane().remove( operations_panel ) ; getContentPane().add( balls_server_panel ) ; // The validate() method is used to cause a container // to lay out its subcomponents again. It should be // invoked when a container's subcomponents are modified // (added to or removed from the container, // or layout-related information changed) after the // container has been displayed. validate() ; } else if ( event.getSource() == client_button ) { balls_client_panel = new BallsClientPanel( server_address_field.getText(), Integer.parseInt( server_port_number_field.getText() ) ) ; setTitle( "SOCKET BALLS CLIENT" ) ; setSize( FRAME_WIDTH, FRAME_HEIGHT ) ; getContentPane().remove( operations_panel ) ; getContentPane().add( balls_client_panel ) ; validate() ; } } } public void windowClosing( WindowEvent event ) { if ( balls_server_panel != null ) { balls_server_panel.stop_balls_server() ; } if ( balls_client_panel != null ) { balls_client_panel.stop_balls_client() ; } setVisible( false ) ; System.exit( 0 ) ; } // Other methods required by the WindowListener interface: public void windowActivated( WindowEvent event ) {} public void windowClosed( WindowEvent event ) {} public void windowDeactivated( WindowEvent event ) {} public void windowDeiconified( WindowEvent event ) {} public void windowIconified( WindowEvent event ) {} public void windowOpened( WindowEvent event ) {} } public class SocketBallsApplication { public static void main( String[] not_in_use ) { SocketBallsFrame socket_balls_frame = new SocketBallsFrame(); // socket_balls_frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); socket_balls_frame.setVisible( true ) ; } }