JavaME TAMZ Socket-based Network Communication in JavaSE and JavaME Department of Computer Science VŠB-Technical University of Ostrava 1 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME C/C++ BSD Sockets in POSIX POSIX functions allow to access network connection in the same way as regular files: There are special functions for opening the connections, the connection is known in POSIX as socket and may be used for different types of communication – inter-process communication on the same machine, different network protocols, etc. Socket- and protocol-specific parameters are set and retrieved by calling special functions Data transfer is done by specific functions or in regular way (read, write) JavaSE copies this approach in Object-oriented way to a degree (methods have similar/same names as POSIX functions). Department of Computer Science VŠB-Technical University of Ostrava by Roman Szturc 2006 & Pavel Moravec 2008 2 TAMZ JavaME Stream Communication (TCP) socket bind connect listen client server accept DATA send/recv close Department of Computer Science VŠB-Technical University of Ostrava 3 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME Datagram Communication (UDP) socket bind connect (a) send/recv (a) read/write Department of Computer Science VŠB-Technical University of Ostrava (b) DATA (b) sendto/recvfrom close 4 by Roman Szturc 2006 & Pavel Moravec 2008 JavaSE TCP/IP Communication (1) Network communication classes in JavaSE can be found in java.net namespace. However, many related classes do not have a common parent other then Object: java.net.Socket – basic client socket for TCP connection. Used to access remote services; we usually supply destination address and port in constructor: new Socket(host, port); java.net.DatagramSocket – socket for sending and receiving UDP datagrams, we supply local port in constructor when necessary: new DatagramSocket([port]); java.net.MulticastSocket – DatagramSocket used for sending multicasts (traffic to a group of computers). java.net.DatagramPacket – block of data to send/receive with DatagramSocket. We wrap it around a buffer in constructor and if necessary specify an InetAddress and port of destination computer: new DatagramPacket(buffer, length, [address, [port]]); TAMZ JavaME JavaSE TCP/IP Communication (2) java.net.InetAddress – representation of IP address. Instance can be created by static methods getByName(hostname), getByAddress(byte[4] addr). Text representation of IP address can be retrieved back by getHostAddress() and getHostName(). java.net.ServerSocket – basic server socket for TCP connection. We need to specify at least the local port the server will be listening at and number of queued requests: new ServerSocket(port [, backlog]); When we want to retrieve a queued connection, we call the accept() method on server socket. java.net.URL – class representing the URL, usually "http://...". java.net.URLConnection – basic abstract class for URL-based connections. Corresponding sub-class is opened using call to new URL(url).openConnection(); Department of Computer Science VŠB-Technical University of Ostrava 6 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME JavaSE Sockets – Remarks The socket operations are blocking, i.e. the thread execution will stop until the socket operation is finished. Since non-blocking sockets do not exist in Java, we can use a workaround by calling setSoTimeout(ms). In case the timeout is reached, java.net.SocketTimeoutException is thrown and can be caught. Several connection/socket parameters can be set/retrieved by calling various set*/get* methods. Both connect and bind methods can be called, if we do not specify the needed parameters in constructor, but we need to specify an InetAddress + port or SocketAddress Department of Computer Science VŠB-Technical University of Ostrava 7 by Roman Szturc 2006 & Pavel Moravec 2008 JavaSE TCP Socket Connection The Socket allows us to connect to given port on a given host. try { s=new Socket(host,port); BufferedReader sis = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter os = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); BufferedReader is = new BufferedReader(new InputStreamReader(s.getInputStream())); String l; do { System.out.println("Type a line to send to server."); l=sis.readLine(); os.write(l); os.newLine(); os.flush(); System.out.println("Server: " + is.readLine()); } while (!l.equals("exit") && !l.equals("down")); s.close(); } catch (IOException e) { System.out.println(e); } JavaSE TCP Server The ServerSocket listens to traffic on given port on localhost. The accept() method returns a socket representing client side. … try { ServerSocket s=new ServerSocket(port); Socket cs; do { cs=s.accept(); BufferedReader is = new BufferedReader (new InputStreamReader(cs.getInputStream())); BufferedWriter os = new BufferedWriter (new OutputStreamWriter(cs.getOutputStream())); do { msg=is.readLine(); os.write(String.valueOf(msg.length())); os.newLine(); os.flush(); } while (!msg.equals("exit") && !msg.equals("down")); cs.close(); } while (!msg.equals("down")); s.close(); } catch (IOException e) { System.out.println(e); } JavaSE TCP Server – MultiThreading Since all requests in previous example must wait until the server calls accept again, the processing is delayed. The solution is to use Threads: public class MyApplication implements Runnable { protected Socket cs; static ServerSocket s; static int port=8000; public static void main(String[] args) { Socket cs; try { s=new ServerSocket(port); do {cs=s.accept(); new Thread(new MyApplication(cs)).start(); } while (true); } catch (IOException e) {if(!e instanceof SocketException){System.out.println(e);}} } public MyApplication(Socket cs) {this.cs=cs;} public void run() { /* Violet bold text from previous slide */ if (msg.equals("down")) s.close(); //Closes main socket to terminate the application } } JavaSE UDP Client The following example shows how we use DatagramSocket to send data to given server's port. Each datagram is independent and may not be delivered String data; //Will be set later, eg. by reading from System.in int port=8000; String server="www.cs.vsb.cz"; … try { DatagramSocket s=new DatagramSocket(); DatagramPacket p = new DatagramPacket(data.getBytes(), data.length(), InetAddress.getByName(server), port); s.send(p); s.receive(p); reply=new String(p.getData(),0,p.getLength()); System.out.println("Reply arrived from "+ p.getAddress() +" : "+ p.getPort()+" > " + reply); s.close(); } catch (IOException e) { System.out.println(e); } JavaSE UDP Server We do not have a specific class for datagram servers, since it is not needed. The following example shows how we use DatagramSocket on server's port. … try { DatagramSocket s=new DatagramSocket(port); DatagramPacket p; String msg; do { p=new DatagramPacket(new byte[512], 512); // new instance each time due to buffer length s.receive(p); msg = new String(p.getData(),0,p.getLength()); System.out.println("Datagram from " + p.getAddress() + " : " + p.getPort() + " > " + msg); p.setData(msg.toUpperCase().getBytes()); p.setLength(msg.length()); s.send(p); } while (!msg.equals("down")); s.close(); } catch (IOException e) { System.out.println(e); } TAMZ JavaME JavaME TCP Socket Connection Establish a socket connection using Connector and read/write data using associated input/output streams, which must be closed as well as the socket when terminating the connection. String uri = "socket://" + name + ":" + port; StreamConnection connection = (StreamConnection) //or SocketConnection Connector.open(uri, Connector.READ_WRITE); InputStream input = connection.openInputStream(); OutputStream output = connection.openOutputStream(); // Send a message. String outgoing = ... output.write(outgoing.getBytes()); output.flush(); // Receive a response. byte buffer[] = new byte[SIZE]; int read = input.read(buffer); Department of Computer Science VŠB-Technical University of Ostrava 13 by Roman Szturc 2006 & Pavel Moravec 2008 JavaME TAMZ JavaME TCP Server The Server Socket listens to traffic on given port on localhost. The MIDP implementations differ in handling of the server sockets. MIDP 1.0 – the socket is reused for incomming connection StreamConnectionNotifier serverSocket = (StreamConnectionNotifier) Connector.open("serversocket://:12345"); StreamConnection conn = serverSocket.acceptAndOpen(); MIDP 2.0 – a new socket is created. ServerSocketConnection scn = (ServerSocketConnection) Connector.open("socket://:12345"); SocketConnection sc = (SocketConnection) scn.acceptAndOpen(); Both the device and the service operator must support servers on mobile devices to make it work (e.g. no private IP addresses!) Department of Computer Science VŠB-Technical University of Ostrava by Roman Szturc 2006 & Pavel Moravec 2008 14 TAMZ JavaME JavaME Server's Request Handler In order to ensure handling of multiple clients, the request handler is executed by an independent thread of execution. public class EchoRequestHandler extends Thread { public void run() { try { InputStream input = serverSocket.getInputStream(); OutputStream output = serverSocket.getOutputStream(); int read; byte data[] = new byte[BUFFER_SIZE]; while ((read = input.read(data)) != -1) { output.write(data, 0, read); output.flush(); } } ... Department of Computer Science VŠB-Technical University of Ostrava 15 by Roman Szturc 2006 & Pavel Moravec 2008 JavaME UDP Client The following example shows how we use DatagramConnection to send data to given server's port. String data; //Will be set later, eg. by reading from System.in int port=8000; String server="www.cs.vsb.cz"; … try { DatagramConnection dc = (DatagramConnection) Connector.open("datagram://" + server + ":" + port); Datagram dg = dc.newDatagram(100); byte[] bytes = message.getBytes(); dc.send(dc.newDatagram(bytes, bytes.length)); dc.receive(dg); if (dg.getLength() > 0) { System.err.println("Message received - " + new String(dg.getData(), 0, dg.getLength())); } } catch (ConnectionNotFoundException cnfe) { //Connection not successful – server is possibly not running, do something about it } catch (IOException e) { e.printStackTrace(); } JavaME UDP Server The following example shows how we use DatagramConnection to to listen on given port. The server changes messages to uppercase. int port=8000; try { DatagramConnection dc = (DatagramConnection)Connector.open("datagram://:" + port); while (true) { Datagram dg = dc.newDatagram(100); dc.receive(dg); address = dg.getAddress(); if (dg.getLength() > 0) { String message = new String(dg.getData(), 0, dg.getLength()).toUpperCase(); System.err.println("Message received: " + message); byte[] bytes = message.getBytes(); dc.send(dc.newDatagram(bytes, bytes.length)); } } } catch (IOException e) { //Binding not successful – another server is possibly running on the same port e.printStackTrace(); } JavaME Servers – Closing Remarks When running servers on mobile devices with JavaME we have to keep in mind following issues: Local socket-based connections between applications may not pose a problem, but the device must be able to run more than one JavaME application/MIDlet at a time (we do not have to use sockets to communicate between threads). Remote applications must know the IP address of the device acting as server, which may pose a problem (in MIDP 2.0 we can call getLocalAddress() on an opened server socket). To be accessible from Internet, the device must have a public IP address (the addresses containing 10.*.*.*, 172.16.*.* – 172.31.*.*, 192.168.*.* and 169.254.*.* are not accessible from Internet, since they are private) TAMZ JavaME Multi Threading MULTI THREADING Department of Computer Science VŠB-Technical University of Ostrava 19 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME Threads There is often need to turn a program into separate, independently running subtasks. Each of these independent subtasks is called a thread, and is programmed as if each thread runs by itself and has the CPU to itself. The thread model is a programming convenience to simplify juggling several operations at the same time within a single program. There is several reasons why to do this. ● ● There can be one thread controlling and responding to a GUI, while another thread carries out the tasks or computations requested, while a third thread does file I/O, all for the same program. Some programs are easier to write if they are split into threads. The classic example is the server part of a client/server. When a request comes in from a client, it is very convenient if the server can spawn a new thread to process that one request. Department of Computer Science VŠB-Technical University of Ostrava 20 by Roman Szturc 2006 & Pavel Moravec 2008 JavaME TAMZ Class Thread The simplest way to create a thread is to inherit from class java.lang.Thread. The most important method for Thread is run(), which is the code that will be executed simultaneously with the other threads in a program. public class CountDownThread extends Thread { private static int threads = 0; private int thread = threads++; public void run() { for (int i = MAX_COUNT; i > 0; i--) System.out.println("thread #" + thread + ": " + i); } } Thread thread = new CountDownThread(); thread.start(); Department of Computer Science VŠB-Technical University of Ostrava 21 by Roman Szturc 2006 & Pavel Moravec 2008 JavaME TAMZ Interface Runnable Sometimes it is impossible or inconvenient to inherit from Thread class. The java.lang.Runnable interface, declaring the run() method, can be implemented in these cases. public class CountDownThread implements Runnable { private static int threads = 0; private int thread = threads++; public void run() { for (int i = MAX_COUNT; i > 0; i--) System.out.println("thread #" + thread + ": " + i); } } Thread thread = new Thread(new CountDownThread()); thread.start(); Department of Computer Science VŠB-Technical University of Ostrava 22 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME The Life Cycle of a Thread A thread goes through several states during its life cycle: ● ● ● ● Creating When a thread is created, it is merely an empty object. No system resources are allocated. The thread in this can be only started. Starting The start() method creates the system resources necessary to run the thread, schedules the thread to run, and calls the thread's run() method. Making a Thread Not Runnable A thread becomes Not Runnable when one of these events occurs: its sleep() or wait() method is invoked. Stopping A program does not stop a thread directly. Rather, a thread arranges for its own death by leaving its run() method. Department of Computer Science VŠB-Technical University of Ostrava 23 by Roman Szturc 2006 & Pavel Moravec 2008 JavaME TAMZ Synchronization There are many interesting situations where separate, concurrently running threads do share data and must consider the state and activities of other threads. Java provides a synchronization mechanism based on monitors. Each object in Java has a lock and a monitor to manage the lock. A block marked as synchronized forces any thread wishing to enter the block to acquire corresponding lock first. If another thread already holds the lock, the acquiring thread will block until the lock will be released. The lock is released when the thread leaves the block. public class Game { private Player black, white, previous; public synchronized void turn(Player player) { if (player == previous) throw new IllegalPlayerException(...); ... Department of Computer Science VŠB-Technical University of Ostrava 24 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME Producer-Consumer Problem The producer-consumer problem is a classic synchronization problem. The producer and consumer processes share a common data. The producer executes a loop in which it puts new items into the data and the consumer executes a loop in which it removes items from the data. Producer Consumer SharedData public interface SharedData { public int getSize(); public void put(int[] data); public int[] get(); } Department of Computer Science VŠB-Technical University of Ostrava 25 by Roman Szturc 2006 & Pavel Moravec 2008 JavaME TAMZ Producer The producer executes a loop in which it puts new items into the data store. public class Producer extends Thread { private SharedData data; public Producer(SharedData data) { this.data = data; } public void run() { for (int i = 0; i < 10; i++) { int[] array = new int[data.getSize()]; System.out.print("put"); for (int j = 0; j < array.length; j++) { array[j] = i; System.out.print(" " + array[j]); } System.out.println(); data.put(array); } } Department of Computer Science by Roman Szturc 2006 & Pavel Moravec 2008 ... VŠB-Technical University of Ostrava 26 JavaME TAMZ Consumer The consumer executes a loop in which it removes items from the data store. public class Consumer extends Thread { private SharedData data; public Consumer(SharedData data) { this.data = data; } public void run() { for (int i = 0; i < 10; i++) { int[] array = data.get(); System.out.print("got"); for (int j = 0; j < array.length; j++) System.out.print(" " + array[j]); System.out.println(); } } ... Department of Computer Science VŠB-Technical University of Ostrava 27 by Roman Szturc 2006 & Pavel Moravec 2008 JavaME TAMZ Naïve Shared Data Traditional implementation of the SharedData interface seems to be all right, but ... public class NaiveSharedData implements SharedData { private int[] data; public NaiveSharedData(int count) { data = new int [count]; } public void put(int[] data) { for (int i = 0; i < data.length; i++) this.data[i] = data[i]; } public int[] get() { int[] data = new int[this.data.length]; for (int i = 0; i < data.length; i++) data[i] = this.data[i]; return data; } ... of Computer Science Department VŠB-Technical University of Ostrava by Roman Szturc 2006 & Pavel Moravec 2008 28 TAMZ JavaME Putting It All Together The producer-consumer problem can be assembled by a few lines of code. SharedData data = new NaiveSharedData(3); Producer producer = new Producer(data); Consumer consumer = new Consumer(data); producer.start(); consumer.start(); Output produced by the program is incorrect, because it contains inconsisten sequence, e.g. 0 0 1. ... put put got got put ... 000 111 001 111 222 Department of Computer Science VŠB-Technical University of Ostrava 29 by Roman Szturc 2006 & Pavel Moravec 2008 JavaME TAMZ Acquiring a Lock The code segments within a program that access the same object from separate, concurrent threads are called critical sections. A critical is identified with the synchronized keyword. A lock is associated with every object that has synchronized code. public class BetterSharedData extends NaiveSharedData { public synchronized void put(int[] data) { super.put(data); } public synchronized int[] get() { return super.get(); } ... put 2 2 2 got 2 2 2 got 2 2 2 Department of Computer Science VŠB-Technical University of Ostrava 30 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME Releasing a Lock (1) A thread releases an object's lock when it enters into wait() method of the object. The wait() method causes the thread to wait until another thread invokes the notify() or the notifyAll() method for the object. public class PerfectSharedData extends BetterSharedData { private boolean read = true; public synchronized void put(int[] data) { try { while (!read) wait(); super.put(data); read = false; notifyAll(); } catch (InterruptedException e) { ... Department of Computer Science VŠB-Technical University of Ostrava 31 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME Releasing a Lock (2) The InterruptedException is thrown by the wait() method if another thread interrupts the current thread. public synchronized int[] get() { int data[] = null; try { while (read) wait(); data = super.get(); read = true; notifyAll(); } catch (InterruptedException e) { e.printStackTrace(); } return data; } Department of Computer Science VŠB-Technical University of Ostrava 32 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME Scheduling Tasks There are two classes, Timer and TimerTask, to facilitate running of tasks in a background thread. ● ● Scheduling one-time tasks Executes a task after specified delay. Scheduling repeating tasks Executes a task at regular intervals. There are two variations for scheduling repeating tasks. ● ● Fixed-delay Each execution of a task is based solely on how long it was since the previous task finished. Fixed-rate Each execution of a task is based on when the first task started and the requested delay between tasks. Department of Computer Science VŠB-Technical University of Ostrava 33 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME Timer Task A subclass of TimerTask defines what is to be done. It declares abstract method run() which has be implemented by the subclass. public class DrawerTask extends TimerTask { private AnimationCanvas canvas; public DrawerTask(AnimationCanvas canvas) { this.canvas = canvas; } public void run() { canvas.drawNextFrame(); } } Department of Computer Science VŠB-Technical University of Ostrava 34 by Roman Szturc 2006 & Pavel Moravec 2008 JavaME TAMZ Animation Canvas The drawNextFrame() is invoked repeatedly by a DrawerTask. public class AnimationCanvas extends Canvas { private Image[] frames = new Image[FRAMES]; int currentFrame = 0; public AnimationCanvas() throws IOException { for (int i = 0; i < frames.length; i++) { frames[i] = Image.createImage("/frame" + (i+1) + ".png"); } } public void drawNextFrame() { currentFrame = (currentFrame+1)%frames.length; repaint(); } ... Department of Computer Science VŠB-Technical University of Ostrava 35 by Roman Szturc 2006 & Pavel Moravec 2008 TAMZ JavaME Timer The timer executes its task reapeatedly, which in turn affects the animation canvas, forcing it to repaint. AnimationCanvas canvas = new AnimationCanvas(); TimerTask task = new DrawerTask(canvas); Timer timer = new Timer(); // Repeat the task each 1 second. timer.schedule(task, 0, 1000); Department of Computer Science VŠB-Technical University of Ostrava 36 by Roman Szturc 2006 & Pavel Moravec 2008