网络联机的过程介绍 笔者先为各位介绍一下基本的java网络程序联机的原理,如图10-4所述在Server端要先开启一个连接阜预备接收Client的联机,当一收到Client端的联机的时候便可以将Server的位置及连接阜号(Socket)传给Client然后Client便取联机的信息再将资料及Socket一起透过网络(Socket记载着ip Address及Port)将讯息送到Server端,就可以开始和作交易啰!
图Client-Server联机示意图
开始撰写网络程序吧!!
我们先撰写一个简单的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("\nClient get datafrom server = "+data);
ta1.append("\n送一个讯息到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("\n收到Server的讯息: "+data);
data = dis.readUTF();
//System.out.println("Client get datafrom server = "+data);
ta1.append("\n收到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("\t You hava entered:"+lb1.getText()+"\n");
ta1.append("\t URL info"+u.toString()+"\n");
InputStreamReader ir = new InputStreamReader(u.openStream());
BufferedReader bf = new BufferedReader(ir);
ta1.append("\t URL stream connection: Read file now...\n");
String aLine;
while((aLine = bf.readLine())!=null)
{
ta1.append(aLine + "\n");
}
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.1\jre\lib\security 中的 java.policy 一些联机权限的设定,另外产生 stub 及 skeleton 的时候也需要使用rmic –v1.1的参数,因为 Perosnal Java 是参照jdk.1.1.x的规格实作出来的。
编译程序Server端的Interface及Service
c:\jdk1.1.8\bin\javac –classpath %pjeeclasspath%;. CustWork.java(1)
c:\jdk1.1.8\bin\javac –classpath %pjeeclasspath%;. CustWorkImpl.java(2)
产生Stub及Skeleton
c:\jdk1.1.8\bin\rmic -classpath %pjeeclasspath%;. CustWorkImpl(3)
编译Server端程序
c:\jdk1.1.8\bin\javac -classpath %pjeeclasspath%;. JSESocketFactory.java(4)
编译Client端程序
c:\jdk1.1.8\bin\javac -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档案也一起移动过去喔。
结论
当网络技术,配合无线网络卡,或是其它的产品,相信会是相当有趣的事情喔。