All Downloads are FREE. Search and download functionalities are using the official Maven repository.

processing.net.Client Maven / Gradle / Ivy

Go to download

Processing is a programming language, development environment, and online community. Network library. Send and receive data over the Internet through simple clients and servers.

There is a newer version: 3.3.7
Show newest version
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */

/*
  Client - basic network client implementation
  Part of the Processing project - http://processing.org

  Copyright (c) 2004-2007 Ben Fry and Casey Reas
  The previous version of this code was developed by Hernando Barragan

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2.1 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General
  Public License along with this library; if not, write to the
  Free Software Foundation, Inc., 59 Temple Place, Suite 330,
  Boston, MA  02111-1307  USA
*/

package processing.net;
import processing.core.*;

import java.io.*;
import java.lang.reflect.*;
import java.net.*;

/**
   * ( begin auto-generated from Client.xml )
   * 
   * A client connects to a server and sends data back and forth. If anything 
   * goes wrong with the connection, for example the host is not there or is 
   * listening on a different port, an exception is thrown.
   * 
   * ( end auto-generated )
 * @webref net
 * @brief The client class is used to create client Objects which connect to a server to exchange data. 
 * @instanceName client any variable of type Client
 * @usage Application
 * @see_external LIB_net/clientEvent
 */
public class Client implements Runnable {
  PApplet parent;
  Method clientEventMethod;
  Method disconnectEventMethod;

  Thread thread;
  Socket socket;
  int port;
  String host;

  public InputStream input;
  public OutputStream output;

  byte buffer[] = new byte[32768];
  int bufferIndex;
  int bufferLast;

  boolean disposeRegistered = false;
  
  
  /**
   * @param parent typically use "this"
   * @param host address of the server
   * @param port port to read/write from on the server
   */
  public Client(PApplet parent, String host, int port) {
    this.parent = parent;
    this.host = host;
    this.port = port;

    try {
      socket = new Socket(this.host, this.port);
      input = socket.getInputStream();
      output = socket.getOutputStream();

      thread = new Thread(this);
      thread.start();

      parent.registerMethod("dispose", this);
      disposeRegistered = true;

      // reflection to check whether host sketch has a call for
      // public void clientEvent(processing.net.Client)
      // which would be called each time an event comes in
      try {
        clientEventMethod =
          parent.getClass().getMethod("clientEvent",
                                      new Class[] { Client.class });
      } catch (Exception e) {
        // no such method, or an error.. which is fine, just ignore
      }
      // do the same for disconnectEvent(Client c);
      try {
        disconnectEventMethod =
          parent.getClass().getMethod("disconnectEvent",
                                      new Class[] { Client.class });
      } catch (Exception e) {
        // no such method, or an error.. which is fine, just ignore
      }

    } catch (ConnectException ce) {
      ce.printStackTrace();
      dispose();

    } catch (IOException e) {
      e.printStackTrace();
      dispose();
    }
  }

  
  /**
   * @param socket any object of type Socket
   * @throws IOException
   */
  public Client(PApplet parent, Socket socket) throws IOException {
    this.parent = parent;
    this.socket = socket;

    input = socket.getInputStream();
    output = socket.getOutputStream();

    thread = new Thread(this);
    thread.start();

    // reflection to check whether host sketch has a call for
    // public void disconnectEvent(processing.net.Client)
    try {
      disconnectEventMethod =
        parent.getClass().getMethod("disconnectEvent",
                                    new Class[] { Client.class });
    } catch (Exception e) {
      // no such method, or an error.. which is fine, just ignore
    }
  }


  /**
   * ( begin auto-generated from Client_stop.xml )
   * 
   * Disconnects from the server. Use to shut the connection when you're 
   * finished with the Client.
   * 
   * ( end auto-generated )
   * @webref client:client
   * @brief Disconnects from the server
   * @usage application
   */
  public void stop() {    
    if (disconnectEventMethod != null && thread != null){
      try {
        disconnectEventMethod.invoke(parent, new Object[] { this });
      } catch (Exception e) {
        e.printStackTrace();
        disconnectEventMethod = null;
      }
    }
    if (disposeRegistered) {
      parent.unregisterMethod("dispose", this);
      disposeRegistered = false;
    }
    dispose();
  }


  /**
   * Disconnect from the server: internal use only.
   * 

* This should only be called by the internal functions in PApplet, * use stop() instead from within your own applets. */ public void dispose() { thread = null; try { if (input != null) { input.close(); input = null; } } catch (Exception e) { e.printStackTrace(); } try { if (output != null) { output.close(); output = null; } } catch (Exception e) { e.printStackTrace(); } try { if (socket != null) { socket.close(); socket = null; } } catch (Exception e) { e.printStackTrace(); } } public void run() { while (Thread.currentThread() == thread) { try { while (input != null) { int value; // try to read a byte using a blocking read. // An exception will occur when the sketch is exits. try { value = input.read(); } catch (SocketException e) { System.err.println("Client SocketException: " + e.getMessage()); // the socket had a problem reading so don't try to read from it again. stop(); return; } // read returns -1 if end-of-stream occurs (for example if the host disappears) if (value == -1) { System.err.println("Client got end-of-stream."); stop(); return; } synchronized (buffer) { // todo: at some point buffer should stop increasing in size, // otherwise it could use up all the memory. if (bufferLast == buffer.length) { byte temp[] = new byte[bufferLast << 1]; System.arraycopy(buffer, 0, temp, 0, bufferLast); buffer = temp; } buffer[bufferLast++] = (byte)value; } // now post an event if (clientEventMethod != null) { try { clientEventMethod.invoke(parent, new Object[] { this }); } catch (Exception e) { System.err.println("error, disabling clientEvent() for " + host); e.printStackTrace(); clientEventMethod = null; } } } } catch (IOException e) { //errorMessage("run", e); e.printStackTrace(); } } } /** * ( begin auto-generated from Client_active.xml ) * * Returns true if this client is still active and hasn't run * into any trouble. * * ( end auto-generated ) * @webref client:client * @brief Returns true if this client is still active * @usage application */ public boolean active() { return (thread != null); } /** * ( begin auto-generated from Client_ip.xml ) * * Returns the IP address of the computer to which the Client is attached. * * ( end auto-generated ) * @webref client:client * @usage application * @brief Returns the IP address of the machine as a String */ public String ip() { if (socket != null){ return socket.getInetAddress().getHostAddress(); } return null; } /** * ( begin auto-generated from Client_available.xml ) * * Returns the number of bytes available. When any client has bytes * available from the server, it returns the number of bytes. * * ( end auto-generated ) * @webref client:client * @usage application * @brief Returns the number of bytes in the buffer waiting to be read */ public int available() { return (bufferLast - bufferIndex); } /** * ( begin auto-generated from Client_clear.xml ) * * Empty the buffer, removes all the data stored there. * * ( end auto-generated ) * @webref client:client * @usage application * @brief Clears the buffer */ public void clear() { bufferLast = 0; bufferIndex = 0; } /** * ( begin auto-generated from Client_read.xml ) * * Returns a number between 0 and 255 for the next byte that's waiting in * the buffer. Returns -1 if there is no byte, although this should be * avoided by first cheacking available() to see if any data is available. * * ( end auto-generated ) * @webref client:client * @usage application * @brief Returns a value from the buffer */ public int read() { if (bufferIndex == bufferLast) return -1; synchronized (buffer) { int outgoing = buffer[bufferIndex++] & 0xff; if (bufferIndex == bufferLast) { // rewind bufferIndex = 0; bufferLast = 0; } return outgoing; } } /** * ( begin auto-generated from Client_readChar.xml ) * * Returns the next byte in the buffer as a char. Returns -1 or 0xffff if * nothing is there. * * ( end auto-generated ) * @webref client:client * @usage application * @brief Returns the next byte in the buffer as a char */ public char readChar() { if (bufferIndex == bufferLast) return (char)(-1); return (char) read(); } /** * ( begin auto-generated from Client_readBytes.xml ) * * Reads a group of bytes from the buffer. The version with no parameters * returns a byte array of all data in the buffer. This is not efficient, * but is easy to use. The version with the byteBuffer parameter is * more memory and time efficient. It grabs the data in the buffer and puts * it into the byte array passed in and returns an int value for the number * of bytes read. If more bytes are available than can fit into the * byteBuffer, only those that fit are read. * * ( end auto-generated ) *

Advanced

* Return a byte array of anything that's in the serial buffer. * Not particularly memory/speed efficient, because it creates * a byte array on each read, but it's easier to use than * readBytes(byte b[]) (see below). * * @webref client:client * @usage application * @brief Reads everything in the buffer */ public byte[] readBytes() { if (bufferIndex == bufferLast) return null; synchronized (buffer) { int length = bufferLast - bufferIndex; byte outgoing[] = new byte[length]; System.arraycopy(buffer, bufferIndex, outgoing, 0, length); bufferIndex = 0; // rewind bufferLast = 0; return outgoing; } } /** *

Advanced

* Return a byte array of anything that's in the serial buffer * up to the specified maximum number of bytes. * Not particularly memory/speed efficient, because it creates * a byte array on each read, but it's easier to use than * readBytes(byte b[]) (see below). * * @param max the maximum number of bytes to read */ public byte[] readBytes(int max) { if (bufferIndex == bufferLast) return null; synchronized (buffer) { int length = bufferLast - bufferIndex; if (length > max) length = max; byte outgoing[] = new byte[length]; System.arraycopy(buffer, bufferIndex, outgoing, 0, length); bufferIndex += length; if (bufferIndex == bufferLast) { bufferIndex = 0; // rewind bufferLast = 0; } return outgoing; } } /** *

Advanced

* Grab whatever is in the serial buffer, and stuff it into a * byte buffer passed in by the user. This is more memory/time * efficient than readBytes() returning a byte[] array. * * Returns an int for how many bytes were read. If more bytes * are available than can fit into the byte array, only those * that will fit are read. * * @param bytebuffer passed in byte array to be altered */ public int readBytes(byte bytebuffer[]) { if (bufferIndex == bufferLast) return 0; synchronized (buffer) { int length = bufferLast - bufferIndex; if (length > bytebuffer.length) length = bytebuffer.length; System.arraycopy(buffer, bufferIndex, bytebuffer, 0, length); bufferIndex += length; if (bufferIndex == bufferLast) { bufferIndex = 0; // rewind bufferLast = 0; } return length; } } /** * ( begin auto-generated from Client_readBytesUntil.xml ) * * Reads from the port into a buffer of bytes up to and including a * particular character. If the character isn't in the buffer, 'null' is * returned. The version with no byteBuffer parameter returns a byte * array of all data up to and including the interesting byte. This * is not efficient, but is easy to use. The version with the * byteBuffer parameter is more memory and time efficient. It grabs * the data in the buffer and puts it into the byte array passed in and * returns an int value for the number of bytes read. If the byte buffer is * not large enough, -1 is returned and an error is printed to the message * area. If nothing is in the buffer, 0 is returned. * * ( end auto-generated ) * @webref client:client * @usage application * @brief Reads from the buffer of bytes up to and including a particular character * @param interesting character designated to mark the end of the data */ public byte[] readBytesUntil(int interesting) { if (bufferIndex == bufferLast) return null; byte what = (byte)interesting; synchronized (buffer) { int found = -1; for (int k = bufferIndex; k < bufferLast; k++) { if (buffer[k] == what) { found = k; break; } } if (found == -1) return null; int length = found - bufferIndex + 1; byte outgoing[] = new byte[length]; System.arraycopy(buffer, bufferIndex, outgoing, 0, length); bufferIndex += length; if (bufferIndex == bufferLast) { bufferIndex = 0; // rewind bufferLast = 0; } return outgoing; } } /** *

Advanced

* Reads from the serial port into a buffer of bytes until a * particular character. If the character isn't in the serial * buffer, then 'null' is returned. * * If outgoing[] is not big enough, then -1 is returned, * and an error message is printed on the console. * If nothing is in the buffer, zero is returned. * If 'interesting' byte is not in the buffer, then 0 is returned. * * @param byteBuffer passed in byte array to be altered */ public int readBytesUntil(int interesting, byte byteBuffer[]) { if (bufferIndex == bufferLast) return 0; byte what = (byte)interesting; synchronized (buffer) { int found = -1; for (int k = bufferIndex; k < bufferLast; k++) { if (buffer[k] == what) { found = k; break; } } if (found == -1) return 0; int length = found - bufferIndex + 1; if (length > byteBuffer.length) { System.err.println("readBytesUntil() byte buffer is" + " too small for the " + length + " bytes up to and including char " + interesting); return -1; } //byte outgoing[] = new byte[length]; System.arraycopy(buffer, bufferIndex, byteBuffer, 0, length); bufferIndex += length; if (bufferIndex == bufferLast) { bufferIndex = 0; // rewind bufferLast = 0; } return length; } } /** * ( begin auto-generated from Client_readString.xml ) * * Returns the all the data from the buffer as a String. This method * assumes the incoming characters are ASCII. If you want to transfer * Unicode data, first convert the String to a byte stream in the * representation of your choice (i.e. UTF8 or two-byte Unicode data), and * send it as a byte array. * * ( end auto-generated ) * @webref client:client * @usage application * @brief Returns the buffer as a String */ public String readString() { if (bufferIndex == bufferLast) return null; return new String(readBytes()); } /** * ( begin auto-generated from Client_readStringUntil.xml ) * * Combination of readBytesUntil() and readString(). Returns * null if it doesn't find what you're looking for. * * ( end auto-generated ) *

Advanced

*

* If you want to move Unicode data, you can first convert the * String to a byte stream in the representation of your choice * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. * * @webref client:client * @usage application * @brief Returns the buffer as a String up to and including a particular character * @param interesting character designated to mark the end of the data */ public String readStringUntil(int interesting) { byte b[] = readBytesUntil(interesting); if (b == null) return null; return new String(b); } /** * ( begin auto-generated from Client_write.xml ) * * Writes data to a server specified when constructing the client. * * ( end auto-generated ) * @webref client:client * @usage application * @brief Writes bytes, chars, ints, bytes[], Strings * @param data data to write */ public void write(int data) { // will also cover char try { output.write(data & 0xff); // for good measure do the & output.flush(); // hmm, not sure if a good idea } catch (Exception e) { // null pointer or serial port dead //errorMessage("write", e); //e.printStackTrace(); //dispose(); //disconnect(e); e.printStackTrace(); stop(); } } public void write(byte data[]) { try { output.write(data); output.flush(); // hmm, not sure if a good idea } catch (Exception e) { // null pointer or serial port dead //errorMessage("write", e); //e.printStackTrace(); //disconnect(e); e.printStackTrace(); stop(); } } /** *

Advanced

* Write a String to the output. Note that this doesn't account * for Unicode (two bytes per char), nor will it send UTF8 * characters.. It assumes that you mean to send a byte buffer * (most often the case for networking and serial i/o) and * will only use the bottom 8 bits of each char in the string. * (Meaning that internally it uses String.getBytes) * * If you want to move Unicode data, you can first convert the * String to a byte stream in the representation of your choice * (i.e. UTF8 or two-byte Unicode data), and send it as a byte array. */ public void write(String data) { write(data.getBytes()); } /** * Handle disconnect due to an Exception being thrown. */ /* protected void disconnect(Exception e) { dispose(); if (e != null) { e.printStackTrace(); } } */ /** * General error reporting, all corralled here just in case * I think of something slightly more intelligent to do. */ //public void errorMessage(String where, Exception e) { //parent.die("Error inside Client." + where + "()", e); //e.printStackTrace(System.err); //} }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy