2013年7月5日 星期五

object transfer over Internet in Java

Here is a Client/Server connection sample for remote function calls in Java.
Map data structures are used to wrap parameters and results.
All serializable types are supported in function calls which include String, Vector, and ImageIcon.
Multithreading is used to allow multiple client requests simultaneously.


/*--------------------------------------------
$ java ClientMap4
connecting...
socketOut
socketIn
cmd:login1
writeObject(input:login1)
readObject()->output
output:LOGIN1
output1:user
output2:passwd
cmd:
*/
import java.util.concurrent.*;  // Executors
import java.util.*; // Vector
import java.net.*; // Socket, ServerSocket
import java.io.*; // InputStream,InputStreamReader,BufferedReader
   // OutputStream,PrintWriter
import javax.swing.ImageIcon;

public class ClientMap4
{
  final int defaultPort = 1234;

  static BufferedReader consoleIn;  // 控制台輸入流
  static PrintStream consoleOut;  // 控制台輸出流

  static Socket skt;
  static ObjectInputStream  socketIn;  // 連線插座輸入流
  static ObjectOutputStream socketOut;  // 連線插座輸出流

  @SuppressWarnings("unchecked")
  public static void main(String args[]) throws Exception
  {
    int port = 1234;

    if(args.length==1)    // 命令列有給埠號參數
      port = new Integer(args[0]).intValue(); // 則依命令列埠號

    consoleOut = System.err;
    consoleIn  = new BufferedReader(new InputStreamReader(System.in));
        consoleOut.printf("connecting...\n");

    skt = new Socket("localhost",port);

        consoleOut.printf("socketOut\n");
    OutputStream socketOs = skt.getOutputStream();
    socketOut = new ObjectOutputStream(socketOs);

        consoleOut.printf("socketIn\n");
    InputStream socketIs = skt.getInputStream();
    socketIn = new ObjectInputStream(socketIs);

    Map<String, Object> packetOut;
    Map<String, Object> packetIn;


    while(true)
    {
      String cmd,parm1="user",parm2="passwd";
      String output=null,output1=null,output2=null;

      // 測試字串陣列容器
      Vector<String> parm = new Vector<String>();
      parm.add(parm1);
      parm.add(parm2);

      // 測試結果集容器
      Vector<Vector<String>> vecResultSet = new Vector<Vector<String>>();
      Vector<String> record1 = new Vector<String>();
      record1.add("user_id_1");
      record1.add("passwod_1");
      record1.add("email_1");
      Vector<String> record2 = new Vector<String>();
      record2.add("user_id_2");
      record2.add("passwod_2");
      record2.add("email_2");
      vecResultSet.add(record1);
      vecResultSet.add(record2);

      // 測試圖片
      ImageIcon pic = new ImageIcon("test.jpg");

        consoleOut.printf("cmd:");
      cmd = consoleIn.readLine();
      packetOut = new HashMap<String, Object>();
      packetOut.put("cmd",cmd);
      packetOut.put("parm1",parm1);
      packetOut.put("parm2",parm2);
      packetOut.put("parm",parm);
      packetOut.put("vecResultSet",vecResultSet);
      packetOut.put("pic", pic);

        consoleOut.printf("writeObject(input:%s)\n",cmd);
      socketOut.writeObject(packetOut);
      socketOut.flush();

        consoleOut.printf("readObject()->output\n");
      packetIn = (Map<String, Object>) socketIn.readObject();
      output  = (String) packetIn.get("output");
      output1 = (String) packetIn.get("output1");
      output2 = (String) packetIn.get("output2");

        consoleOut.printf("output:%s\n",output);
        consoleOut.printf("output1:%s\n",output1);
        consoleOut.printf("output2:%s\n",output2);
    }
  }
}





/*--------------------------------------------------
$ java ServerMap4
ListenTask: thread:pool-1-thread-1 (9) waiting at port:1234
ServiceTask: thread: pool-1-thread-1 (9) serving /127.0.0.1:59626
ServiceTask: socketOut
ServiceTask: socketIn
ServiceTask: end of constructor
ListenTask: thread:pool-1-thread-1 (9) waiting at port:1234
        begin running
        waiting input
        cmd=<login1>
        parm1=<user>
        parm2=<passwd>
        parm=<[user, passwd]>
        vecResultSet=<[[user_id_1, passwod_1, email_1], [user_id_2, passwod_2, email_2]]>
        pic=width:728,height:90,write to 'test2.jpg'
        output=<LOGIN1>
        output1=<user>
        output2=<passwd>
        waiting input
*/
import java.util.concurrent.*;  // Executors
import java.util.*; // 用到 Vector
import java.net.*; // 用到 Socket, ServerSocket
import java.io.*; // 用到 InputStream,InputStreamReader,BufferedReader
   // OutputStream,PrintWriter
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;

class ServiceTask implements Runnable
{
  BufferedReader consoleIn;  // 控制台輸入流
  PrintStream consoleOut;  // 控制台輸出流

  Socket skt;
  ObjectInputStream  socketIn;  // 連線插座輸入流
  ObjectOutputStream socketOut;  // 連線插座輸出流

  String host;
  String port;

  public ServiceTask(Socket skt) throws IOException
  {
    // 取得螢幕輸出流
    consoleOut = System.err;

    // 由連線插座,取得主機,埠號
    this.skt = skt;
    host = skt.getInetAddress().toString();
    port = String.valueOf(skt.getPort());
    consoleOut.printf("ServiceTask: thread: %s (%d) serving %s:%s\n",
     Thread.currentThread().getName(),
     Thread.currentThread().getId(), host,port);

    // 由連線插座,取得插座輸出入資料流
    consoleOut.printf("ServiceTask: socketOut\n");
    OutputStream socketOs = skt.getOutputStream();
    socketOut = new ObjectOutputStream(socketOs);

    consoleOut.printf("ServiceTask: socketIn\n");
    InputStream socketIs = skt.getInputStream();
    socketIn = new ObjectInputStream(socketIs);

    consoleOut.printf("ServiceTask: end of constructor\n");
  }

  @SuppressWarnings("unchecked")
  public void run()
  {
        consoleOut.printf("\tbegin running\n");

    Map<String, Object> packetOut;
    Map<String, Object> packetIn;

    // 測試字串陣列容器,結果集容器,圖片
    Vector<String> parm;
    Vector<Vector<String>> vecResultSet;
    ImageIcon pic;

    try
    {
      // 進行多次對話,直到輸出為QUIT為止
      while(true)
      {
        consoleOut.printf("\twaiting input\n");
        packetIn = (Map<String, Object>) socketIn.readObject();
        String cmd = (String) packetIn.get("cmd");
        String parm1 = (String) packetIn.get("parm1");
        String parm2 = (String) packetIn.get("parm2");
        parm = (Vector<String>) packetIn.get("parm");
        vecResultSet = (Vector<Vector<String>>)packetIn.get("vecResultSet");
        pic = (ImageIcon) packetIn.get("pic");

         consoleOut.printf("\tcmd=<%s>\n",cmd);
         consoleOut.printf("\tparm1=<%s>\n",parm1);
         consoleOut.printf("\tparm2=<%s>\n",parm2);
         consoleOut.printf("\tparm=<%s>\n",parm);
         consoleOut.printf("\tvecResultSet=<%s>\n",vecResultSet);
         consoleOut.printf("\tpic=width:%d,height:%d,write to '%s'\n",pic.getIconWidth(),pic.getIconHeight(),"test2.jpg");

         // save image for comparison
         Image img = pic.getImage();
         BufferedImage bi = new BufferedImage(img.getWidth(null),img.getHeight(null),BufferedImage.TYPE_INT_RGB);
         Graphics2D g2 = bi.createGraphics();
         g2.drawImage(img, 0, 0, null);
         g2.dispose();
         ImageIO.write(bi, "jpg", new File("test2.jpg"));

        String output = cmd.toUpperCase();
        String output1 = parm1;
        String output2 = parm2;
        packetOut = new HashMap<String, Object>();
        packetOut.put("output",output);
        packetOut.put("output1",output1);
        packetOut.put("output2",output2);
        socketOut.writeObject(packetOut);
        socketOut.flush();
         consoleOut.printf("\toutput=<%s>\n",output);
         consoleOut.printf("\toutput1=<%s>\n",output1);
         consoleOut.printf("\toutput2=<%s>\n",output2);

        if(cmd.equals("quit")) break; // 遇到quit指令結束
      }
    }
    catch(Exception e)
    {
      consoleOut.printf("ServiceTask: exception:%s\n",e);
    }
    finally
    {
      // 關閉插座資料流和插座本身
      try
      {
        socketIn.close();
        socketOut.close();
        skt.close();
      }
      catch(IOException e)
      {
       consoleOut.printf("ServiceTask: socket close error:%s\n",e);
      }
    }
  }
}

class ListenTask implements Runnable
{
  int listenPort;   // 監聽埠號
  ServerSocket listenSocket;  // 監聽插座
  PrintStream consoleOut;  // 控制台輸出流

  ExecutorService pool;  // 緒池

  public ListenTask(ExecutorService pool, int port) throws IOException
  {
    this.listenPort = port;
    this.pool = pool;

    consoleOut = System.err;

    // 建立監聽插座,可能丟例外
    listenSocket = new ServerSocket(listenPort);

    Runnable listenTask = this;
    Future f = pool.submit(listenTask);
  }

  // 監聽緒工作
  public void run()
  {
    Socket connectedSocket=null;

    try
    {
      while(true)
      {
        // 等待新連線
        consoleOut.printf("ListenTask: thread:%s (%d) waiting at port:%d\n",
         Thread.currentThread().getName(),
         Thread.currentThread().getId(),listenPort);

        connectedSocket = listenSocket.accept();

        // 啟動專屬緒,為新連線服務
        Runnable serviceTask = new ServiceTask(connectedSocket);
        pool.execute(serviceTask);
      }
    }
    catch(IOException e)
    {
     consoleOut.printf("ListenTask: accept raised IOException:%s\n",e);
      System.exit(0);
    }
  }
}

public class ServerMap4
{
  final int defaultPort = 1234;
  final ExecutorService pool;

  public ServerMap4(int port)
  {
    pool = Executors.newCachedThreadPool();  // 建立緒池
    //pool = Executors.newFixedThreadPool(10);

    try
    {
      if(port < 1024) port = defaultPort;
      ListenTask listenTask = new ListenTask(pool,port); // 開監聽緒
    }
    catch(IOException e)
    {
      System.err.printf("ServerMap4: %s\n", e);
    }
  }

  public static void main(String args[]) throws Exception
  {
    int port = -1;

    if(args.length==1)    // 命令列有給埠號參數
      port = new Integer(args[0]).intValue(); // 則依命令列埠號

    new ServerMap4(port);  // 啟動監聽及服務緒
  }
}