開始撰寫網絡程序吧!!
我們先撰寫一個簡單的Server吧!!
import java.io.*;
import java.net.*;
public class netServer {
String clientMessage;
ServerSocket sSocket; //宣告使用ServerSocket的類別
Socket csocket; //取得Client聯機封包的Socket,各為別想太多就是Client端的聯機
public netServer() {
try {
sSocket = new ServerSocket(900,200); //開啟Port 900,並限定Client聯機的數量為200,若開啟正常則開始等待Client端的聯機
csocket = sSocket.accept(); //Client端已經聯機上了取得Socket
System.out.println("取得一個Client端從 "+csocket.getInetAddress().getHostAddress()); //顯示出Client IP
}
catch(IOException ioe) {
System.out.println("開啟Server異常!!!");
}
}
public static void main(String[] args) {
new netServer();
}
}
執行
編譯成功後打java netServer畫面如下圖所示就會開始等待接受Client端的要求啰!!
圖netServer開始等待Client端發出要求
接著我們切換到DOS模式(再開一個DOS窗口)輸入telnet 127.0.0.1 900(注重空格)畫面會一閃就過去了。接著我們觀察Server的畫面是否有出現取得一個Client端從 127.0.0.1如下圖圖10-4所示。接著我們就開始撰寫PocketPC中的Client吧!!
圖netServer開始等待Client端發出要求
注重
目前Server的程序皆在PC端執行喔!!待下個Client范例開始才移到PocketPC中執行。
打造個Hello NetWork吧!! 皆下來我們撰寫個輸入您的姓名,Server會打對您打招呼的程序吧!!這個程序的原理是在Server端執行一個無線回圈,然後提供服務如接受到Client傳來的訊息之後就傳回一個訊息Hello的訊息給Client,另外我們為了達到一次能夠同時服務多個使用者所以我們使用了Thread的技術。
//此為Server端程序
import java.io.*;
import java.net.*;
public class netHelloServer {
String clientMessage; ServerSocket sSocket; //宣告使用ServerSocket的類別
Socket csocket; //取得Client聯機封包的Socket,各為別想太多就是Client端的聯機
public netHelloServer() {
try {
sSocket = new ServerSocket(900,200); //開啟Port 900,並限定Client聯機的數量為200,若開啟正常則開始等待Client端的聯機
while(true) {
csocket=sSocket.accept();
Thread t=new MyThread(csocket);
t.start();
}
}
catch(IOException ioe) {
System.out.println("開啟Server異常!!!");
}
}
public static void main(String[] args) {
new netHelloServer();
}
}
class MyThread extends Thread { Socket conn; String tempStr; DataInputStream dis; DataOutputStream dos; //建構者
public MyThread(Socket inSocket) { conn = inSocket; }
public void run() {
try{
System.out.println("Server go connect from:"+conn.getInetAddress().getHostName());
dos = new DataOutputStream(conn.getOutputStream());
dos.writeUTF("輸入您的姓名的資料"); //get the request conntent
dis = new DataInputStream(conn.getInputStream());
tempStr = dis.readUTF(); //響應一下
dos.writeUTF("您輸入的資料為"+tempStr+"系統正在為您預備中請稍後..."); //復雜的動作可以寫在這邊
dos.writeUTF(tempStr+" 您好...");
System.out.println("Server recivews data ="+tempStr);
conn.close();//斷線
}
catch(IOException e) {
System.out.println(e);
}
}
}
//Client端的程序
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
public class HelloClient extends Frame {
Label lb1 = new Label("請輸入姓名");
TextField tf1 = new TextField("",14);
Label lb2 = new Label("請輸入Server的IP");
TextField tf2 = new TextField("",14);
TextArea ta1 = new TextArea("");
Button BTn1 = new Button("送出訊息");
Button btn2 = new Button("離開");
Panel pl1 = new Panel();
Panel pl2 = new Panel();
static Socket csocket = null;
MouseClickevent bce = new MouseClickevent();
public HelloClient() { //窗口設定
btn1.addMouseListener (bce);
btn2.addMouseListener (bce);
pl1.setLayout(new GridLayout(2,1));
pl1.add(lb1); pl1.add(tf1); pl1.add(lb2); pl1.add(tf2);
add(pl1,BorderLayout.NORTH);
add(ta1,BorderLayout.CENTER);
pl2.add(btn1); pl2.add(btn2);
add(pl2,BorderLayout.SOUTH);
setSize(240,320);
setVisible(true);
}
public static void main(String[] args) {
new HelloClient();
} //以下為按鈕事件處理
class MouseClickevent implements MouseListener {
public void mouseClicked(MouseEvent e) {}
public void mousePressed(MouseEvent e) {
if(e.getSource() == btn1) {
if(!tf2.getText().equals("")&&!tf1.getText().equals(""))
try{
//csocket = new Socket("127.0.0.1",900);
csocket = new Socket(tf2.getText(),900);
DataInputStream dis = new DataInputStream(csocket.getInputStream());
String data = dis.readUTF();
//System.out.println("
Client get datafrom server = "+data);
ta1.append("
送一個訊息到Server: "+data); //write response to server
DataOutputStream dos = new DataOutputStream(csocket.getOutputStream()); //dos.writeUTF("Bye! all done");
dos.writeUTF(tf1.getText());
data = "";
data = dis.readUTF();
//System.out.println("Client get datafrom server = "+data);
ta1.append("
收到Server的訊息: "+data);
data = dis.readUTF();
//System.out.println("Client get datafrom server = "+data);
ta1.append("
收到Server的訊息: "+data); csocket.close();
}
catch(IOException IOe) {
System.out.println(e);
}
}
else if(e.getSource() == btn2) {
//System.out.println("Btn2 is Clicked");
System.exit(1);
}
}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
}
編譯及激活程序 Server端 執行方法在PC端編譯及執行 javac –classpath %pjeeclasspath%;. netHelloServer.java(編譯) java –classpath %pjeeclasspath%;. netHelloServer(執行)
圖 netServer開始等待Client端發出要求
注重
Server激活之後就不要關掉了,可以測試Client端的聯機。
Client端 請各位讀者照著下面輸入指令,記得您的PocketPC要連上網絡唷!! javac –classpath %pjeeclasspath%;. Clinet.java(Enter) java –classpath %pjeeclasspath%;. Client(Enter)
圖Client的仿真器畫面
圖 在PDA中的畫面
注重!!
1. 各位讀者可以從程序中注重到Server端及Client端中的Read及Write是互相對稱的,希望大家在撰寫程序中需要這個要點。
2. 配合無線網絡卡、是藍芽技術、及行動通訊等技術,這就是相當實用的應用程序了,感謝人類創造了網絡還有Java。
一個URL的類別需要下面幾個兩個主要的參數: 協議的定義(如http或是FTP)等 資源的名稱(包含網址或是port) 關於URL的部分筆者只有為各位作簡單的介紹,接著附上個讀取網頁的范例,當然啦!!先玩玩看啰!!然後再討論一下吧。
//httptest.java
import java.awt.*;
import java.awt.event.*;
import java.net.*;
import java.io.*;
public class httptest extends Frame
{
private Label lb1 = new Label("URL");
private TextField tf1 = new TextField(10);
private TextArea ta1 = new TextArea();
private Button btn1 = new Button("Get");
Panel p1 = new Panel();
public httptest()
{
p1.add(lb1);
p1.add(tf1);
p1.add(btn1);
//sp1.add(ta1);
add(p1,BorderLayout.NORTH);
add(ta1,BorderLayout.CENTER);
btn1.addActionListener(new btn1ActionEvent());
setSize(240,320); //for iPAQ
setVisible(true);
}
public static void main(String args[])
{
new httptest();
}
class btn1ActionEvent implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
String URLStr;
try
{
//開啟一個新的URL對象
URL u = new URL(tf1.getText());
ta1.append(" You hava entered:"+lb1.getText()+"
");
ta1.append(" URL info"+u.toString()+"
");
InputStreamReader ir = new InputStreamReader(u.openStream());
BufferedReader bf = new BufferedReader(ir);
ta1.append(" URL stream connection: Read file now...
");
String aLine;
while((aLine = bf.readLine())!=null)
{
ta1.append(aLine + "
");
}
bf.close();
ir.close();
}
catch(MalformedURLException mue)
{
//System.out.println("網址不正確");
}
catch(IOException ioe)
{
//System.out.println("Error "+ioe);
}
}
}
}
編譯及執行程序
javac –classpath %pjeeclasspath%;. httptest.java(編譯)
pjava –classpath %pjeeclasspath%;. httptest (執行)
接著輸入個網址,記得要連上網絡啊!!筆者是輸http://www.javatwo.net,接著會跑出很多很多東西唷!!
圖 在仿真器中的畫面(1)
圖 在仿真器中的畫面(2)
相信大家都覺得很希奇,為什麼無法看看到如我們浏覽器一般的漂亮圖樣及文字呢?因為我們必須要在為這些取得的字符串還有檔案作剖析(parse)的動作,如讀到了圖形的字節就需要使用使用繪圖軟件打開,讀到音樂的字節就需要使用媒體播放工具來開啟,這是可一門相當大的學問也是相當的苦工唷。
分布式的應用程序
在為各位讀著介紹下一個更炫更酷的RMI技術之前筆者先為各位介紹一些分布式應用程序的觀念,另外附上一個網絡程序與數據庫連接的范例架構如下圖所示,我們將SocketServer中間安置許多真實的程序邏輯,最後再透過JDBC連接到數據庫,如此我們可以將數據庫的聯機降至最低,至於Client端聯機的處理則就需要靠各位撰寫程序的技巧啰!!這是經常看到的多層式(N-tier)架構或是分布式的應用程序皆是以此技巧發展出來的筆者所附上的范例程序是屬於三層式架構(3-tier)。
圖(三層式)的系統架構
Server端的應用程序
皆下來的程序滿有趣而且也很實用的,希望各位讀者會喜歡。下面附上的Server端中具有連接數據庫查詢及提供Socket服務的Server,各位讀者可以在PC端執行。
//netDBServer.java
import java.sql.*;
import java.net.*;
import java.io.*;
import java.util.*;
import org.hsql.util.*;
public class netDBServer
{
String clientMessage;
ServerSocket sSocket;
//宣告使用ServerSocket的類別
Socket csocket;
//取得Client聯機封包的Socket,各為別想太多就是Client端的聯機
public netDBServer()
{
try
{
sSocket = new ServerSocket(900,200);
//開啟Port 900,並限定Client聯機的數量為200,若開啟正常則開始等待Client端的聯機
while(true)
{
csocket=sSocket.accept();
Thread t=new MyThreadDB(csocket);
t.start();
}
}
catch(IOException ioe)
{
System.out.println("開啟Server異常!!!");
}
}
public static void main(String[] args)
{
new netDBServer();
}
}
class MyThreadDB extends Thread
{
Socket conn;
String tempStr;
DataInputStream dis;
DataOutputStream dos;
int ikey;
String val;
static Connection con = null;
String sTestData[]= {
"create table Place (Code integer,Name varchar(255))",
"create index iCode on Place (Code)",
"delete from place",
"insert into Place values (4900,'Langenthal')",
"insert into Place values (8000,'Zurich')",
"insert into Place values (3000,'Berne')",
"insert into Place values (1200,'Geneva')",
"insert into Place values (6900,'Lugano')",
"create table Customer (Nr integer,Name varchar(255),Place integer)",
"create index iNr on Customer (Nr)",
"delete from Customer",
"insert into Customer values (1,'Meier',3000)",
"insert into Customer values (2,'Mueller',8000)",
"insert into Customer values (3,'Devaux',1200)",
"insert into Customer values (4,'Rossi',6900)",
"insert into Customer values (5,'Rickli',3000)",
"insert into Customer values (6,'Graf',3000)",
"insert into Customer values (7,'Mueller',4900)",
"insert into Customer values (8,'May',1200)",
"insert into Customer values (9,'Berger',8000)",
"insert into Customer values (10,'D''Ascoli',6900)",
"insert into Customer values (11,'Padruz',1200)",
"insert into Customer values (12,'Hug',4900)"
};
static boolean DBCheckInit;
//建構者
public MyThreadDB(Socket inSocket)
{
conn = inSocket;
}
public void run()
{
try{
System.out.println
("Server go connect from:"+conn.getInetAddress().getHostName());
dos = new DataOutputStream(conn.getOutputStream());
dos.writeUTF("輸入查詢的資料");
//get the request conntent
dis = new DataInputStream(conn.getInputStream());
tempStr = dis.readUTF();
//響應一下
try{
new org.hsql.jdbcDriver();
Class.forName("org.hsql.jdbcDriver").newInstance();
}catch (Exception e){System.out.println(e);}
try{
con = DriverManager.getConnection("jdbc:HypersonicSQL:test","sa","");
Statement select = con.createStatement();
ResultSet result = select.executeQuery("Select Code,Name FROM Place");
//初始化數據庫
System.out.println("初始化數據庫");
for(int i=0;i>sTestData.length;i++)
select.executeQuery(sTestData[i]);
ResultSet result =
select.executeQuery("Select Code,Name FROM Place Where Code ="+tempStr);
System.out.println("Got results:");
while(result.next()){
ikey = result.getInt(1);
if(result.wasNull()){ikey=-1;}
val = result.getString(2);
if(result.wasNull()){val=null;}
System.out.println("Code = "+ikey);
System.out.println("Name = "+val);
}
}catch(Exception e)
{
System.out.println(e);
}/*finally
{
if(con!=null){try{con.close();}
catch(Exception e){e.printStackTrace();}}
}*/
//dos.writeUTF("您查詢的資料為"+tempStr+"系統正在為您查詢中請稍後...");
dos.writeUTF("您查詢的資料為"+val+"系統正在為您查詢中請稍後...");
System.out.println("Server recivews data ="+tempStr);
conn.close();//斷線
}
catch(IOException e)
{System.out.println(e);}
}
}
Client端的應用程序
Client端的應用程序筆者並沒有作窗口程序的設計,只是提供一個雛形給大家使用。
//Client.java
import java.net.*;
import java.io.*;
public class Client
{
public Client()
{
try{
//請配合Server的IP修改
Socket csocket = new Socket("192.168.0.1",900);
DataInputStream dis = new DataInputStream(csocket.getInputStream());
String data = dis.readUTF();
System.out.println("Client get datafrom server = "+data);
//write response to server
DataOutputStream dos = new DataOutputStream(csocket.getOutputStream());
dos.writeUTF("3000");
data = "";
data = dis.readUTF();
System.out.println("Client get datafrom server = "+data);
data = dis.readUTF();
System.out.println("Client get datafrom server = "+data);
csocket.close();
}catch(IOException e)
{
System.out.println(e);
}
}
public static void main(String[] args)
{
new Client();
}
}
編譯及執行
Server端
javac –classpath %pjeeclasspath%;hsql.jar netDBServer(編譯)
pjava –classpath %pjeeclasspath%;hsql.jar netDBServer(執行)
注重
-classpath之後的套件位置請依照情況修改另外別忘記了唷~
Client端 javac –classpath %pjeeclasspath% Client.java (編譯)
pjava –classpath %pjeeclasspath% Client (執行)
並且移到我們的PocketPC中執行
圖 Server端激活之後收到Client訊息的畫面
圖 PDA中的畫面
希望這個小架構能夠經過各位的修改及調整之後給各位不管是對於研究或是事業上能夠幫上一點小小的忙,接著我們就預備進入RMI章節了唷~
Remote Method Invocation(RMI)
RMI,是Java所提供的分布式架構的機制,可以用來撰寫成是和成是之間的通訊方法,當然你也可以透過Socket來撰寫分散的程序(如前一個段落我們所介紹的),但是在使用這些函數需要做相當多繁瑣的調整,另外再使用Socket通訊的時候,只能夠傳遞資料,但是是若使用RMI技術不僅可以傳遞資料也能夠傳遞對象呢!!所以各位只要好好的發揮您的想象力,想想您的程序架構就可以了!
RMI原理介紹
我們先看一下下面的聯機是意圖吧!!在分散室的架構中我們將程序分為Client及Server兩端,再使用RMI的機制的時候Client會透過Server的Stub(向是Server的分身)透過RMI的一些運算找到Server的Skeleton,接著Client端就透過控制Server的分身就能夠達到使用Server的函示及資源了。
圖 RMI聯機示意圖
RMI聯機過程說明
如圖所示,雖然使用起來RMI感覺是相當的輕易的,但是其實他包含了下面幾個重要的動作,Client先透過Server的Stub聯機的一個注冊中心,接著注冊中心將Stub和Server的Skeleton聯機,然後Client端就可以使用Server端的資源啰。
圖 RMI聯機示意圖
// CustWork.java 定義Server端提供服務的接口
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface CustWork extends Remote{
public String getSvrMsg() throws RemoteException;
}
// CustWorkImpl.java 這是Server端的服務
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.Calendar;
import java.rmi.server.*;
public class CustWorkImpl implements CustWork{
public CustWorkImpl() throws RemoteException{
UnicastRemoteObject.eXPortObject(this);
}
public String getSvrMsg() throws RemoteException{
String threadName = Thread.currentThread().getName();
String curTime = Calendar.getInstance().getTime().toString();
System.out.println(threadName+" : Aclient connected at "+ curTime);
return (threadName + " :Message from Server at "+curTime);
}
}
// JSESocketFactory在程序中激活RMI的注冊中心及Server服務
import java.rmi.registry.*;
import java.rmi.*;
import java.rmi.RMISecurityManager;
public class JSESocketFactory
{
public static void main(String[] args){
try {
//小技巧
Registry regObj = LocateRegistry.createRegistry(1099);
CustWorkImpl obj = new CustWorkImpl();
Naming.rebind("myServer", obj);
System.out.println
("myImpl created and bound in the registry to the name myServer");
}
catch (Exception e)
{
System.out.println("myImpl.main: an exception occurred:");
e.printStackTrace();
}
}
}
//RMIClient.java Client端的程序
//程序中有關IP的部分請填入Server的IP Address
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.RMISecurityManager;
public class RMIClient
{
static String servMsg ="";
public static void main(String[] args)
{
try{
if(System.getSecurityManager() == null)
System.setSecurityManager(new RMISecurityManager());
CustWork serRef =
(CustWork) Naming.lookup("rmi://192.168.0.1:1099/myServer");
servMsg = serRef.getSvrMsg();
System.out.println(servMsg);
}catch(Exception e)
{
e.printStackTrace();
}
}
}
編譯及執行程序
注重!!
此部分的編譯方法為jdk1.1.8的編譯方法,若想在計算機中當Server且和J2SE聯機的話,你需要設定C:jdk1.3.1jrelibsecurity 中的 java.policy 一些聯機權限的設定,另外產生 stub 及 skeleton 的時候也需要使用rmic –v1.1的參數,因為 Perosnal Java 是參照jdk.1.1.x的規格實作出來的。
編譯程序Server端的Interface及Service
c:jdk1.1.8injavac –classpath %pjeeclasspath%;. CustWork.java(1)
c:jdk1.1.8injavac –classpath %pjeeclasspath%;. CustWorkImpl.java(2)
產生Stub及Skeleton
c:jdk1.1.8in
mic -classpath %pjeeclasspath%;. CustWorkImpl(3)
編譯Server端程序
c:jdk1.1.8injavac -classpath %pjeeclasspath%;. JSESocketFactory.java(4)
編譯Client端程序
c:jdk1.1.8injavac -classpath %pjeeclasspath%;. RMIClient.java(5)
執行程序
注重您需要開兩個DOS窗口來執行這兩只程序
激活RMI Server程序 pjava -classpath %pjeeclasspath%;. JSESocketFactory(1)
圖 RMI Server激活畫面
激活RMI Client程序
pjava -classpath %pjeeclasspath%;. RMIClient
圖 RMI Client激活畫面
另外將Client端移植到WinCE中千萬別忘了把XXXX_Stub檔案也一起移動過去喔。
結論
當網絡技術,配合無線網絡卡,或是其它的產品,相信會是相當有趣的事情喔。