導讀:
摘要 本文闡述了使用JAVA程式設計語言對基於客戶/伺服器模式的應用編寫網路通訊程式,討論了SOCKET機制、輸入輸出資料流以及程式實現代碼。
關鍵詞 JAVA,網路,SOCKET,APPLET
網路上的系統結構多為客戶/伺服器模式,伺服器端負責資料和映像等的儲存、維護、管理以及傳遞,用戶端則負責人機介面的操作、送出需求及顯示收回的資料。
下面介紹一下如何使用JAVA來進行網路編程:
1) 由於用戶端通過IE同伺服器建立聯絡,所以用戶端使用Applet,伺服器端使用Application;
2) 伺服器應設定成多線程,應答多個客戶的請求;
3) 兩端通訊使用SOCKET機制。
1 Java中輸入/輸出流概念:
過濾流DataInputStream 和DataOutputStream 除了分別作為FilterInputStream 和FilterOutputStream的子類外,還分別實現了介面DataInput 和DataOutput。介面DataInput 中定義的方法主要包括從流中讀取基本類型的資料、讀取一行資料、或者讀取指定長度的位元組數,如readBoolean() readInt()、readLine()、readFully()等。介面DataOutput中定義的方法主要是向流中寫入基本類型的資料或者寫入一定長度的位元組數組,如writeChar()、writeDouble() DataInputStream可以從所串連的輸入資料流中讀取與機器無關的基本類型資料,用以實現一種獨立於具體平台的輸入方式;DataInputStream 可以向所串連的輸出資料流寫入基本類型的資料。
2 Socket 機制
Socket是面向客戶/伺服器模型設計的,網路上的兩個程式通過一個雙向的通訊串連實現資料的交換,這個雙向鏈路的一端稱為一個Socket。 Socket通常用來實現客戶方和服務方的串連。客戶程式可以向Socket寫請求,伺服器將處理此請求,然後通過Socket將結果返回給使用者。
Socket通訊機制提供了兩種通訊方式:有聯結和無聯結方式,分別面向不同的應用需求。使用有聯結方式時,通訊鏈路提供了可靠的,全雙工系統的位元組流服務。在該方式下,通訊雙方必須建立一個聯結過程並建立一條通訊鏈路,以後的網路通訊操作完全在這一對進程之間進行,通訊完畢關閉此聯結過程。使用無聯結方式時其系統開銷比無聯結方式小,但通訊鏈路提供了不可靠的資料報服務,不能保證信源所傳輸的資料一定能夠到達信宿。在該方式下,通訊雙方不必建立一個聯結過程和建立一條通訊鏈路,網路通訊操作在不同的主機和進程之間轉寄進行。
3 Java語言
Java語言的優點主要表現在:簡單、物件導向、多線程、分布性、體繫結構中立、安全性等方面。
(1) 簡單性
Java與C++語言非常相近,但Java比C++簡單,它拋棄了C++中的一些不是絕對必要的功能,如標頭檔、預先處理檔案、指標、結構、運算子多載、多重繼承以及自動強迫同型。 Java實現了自動的垃圾收集,簡化了記憶體管理的工作。這使程式設計更加簡便,同時減少了出錯的可能。
(2) 物件導向
Java提供了簡單的類機制和動態構架模型。對象中封裝了它的狀態變數和方法,很好地實現了模組化和資訊隱藏;而類則提供了一類對象的原型,通過繼承和重載機制,子類可以使用或重新定義父類或超類所提供的方法,從而既實現了代碼的複用,又提供了一種動態解決方案。
Java是一種完全物件導向的程式設計語言,它除了數組、布爾和字元三個基礎資料型別 (Elementary Data Type)外的其它類都是對象,它不再支援全域變數。在Java中,如果不建立新類就無法建立程式,Java程式在運行時必須先建立一個類的執行個體,然後才能提交運行。
Java同樣支援繼承特性,Java的類可以從其它類中繼承行為,但Java只支援類的單重繼承,即每個類只能從一個類中繼承。
Java支援介面,介面允許程式員定義方法但又不立即實現,一個類可以實現多個介面,利用介面可以得到多重繼承的許多優點而又沒有多重繼承的問題。
(3) 多線程
多線程使應用程式可以同時進行不同的操作,處理不同的事件。在多線程機制中,不同的線程處理不同的任務,他們之間互不干涉,不會由於一處等待影響其他部分,這樣容易實現網路上的即時互動操作。
Java程式可以有多個執行線程,如可以讓一個線程進行複雜的計算,而讓另一個線程與使用者進行互動,這樣使用者可以在不中斷計算線程的前提下與系統進行互動。多線程保證了較高的執行效率。
(4) 分布性
Java是面向網路的語言。通過它提供的類庫可以處理TCP/IP協議,使用者可以通過URL地址在網路上很方便的訪問其他對象。
(5) 體繫結構中立
Java是一種網路語言,為使Java程式能在網路的任何地方運行,Java解譯器產生與體繫結構無關的位元組碼結構的檔案格式。Java為了做到結構中立,除產生機器無關的位元組碼外,還制定了完全統一的語言文本,如Java的基礎資料型別 (Elementary Data Type)不會隨目標機的變化而變化,一個整型總是32位,一個長整型總是64位。
為了使Java的應用程式能不依賴於具體的系統,Java語言環境還提供了用於訪問底層作業系統功能的類組成的包,當程式使用這些包時,可以確保它能運行在各種支援Java的平台上。
java.lang:一般的語言套件。其中包括用於字串處理、多線程、異常處理和數字函數等的類,該包是實現Java程式運行平台的基本包
java.util: 工具 + 生產力包。其中包括雜湊表、堆棧、時間和日期等
java.io: 基於流模型的輸入/輸出包。該包用統一的流模型實現了各種格式的輸入/輸出,包括檔案系統、網路和裝置的輸入/輸出等
java.net: 網路包。該包支援TCP/IP協議,其中提供了socket、URL和WWW的編程介面
java.awt: 抽象視窗工具集。其中實現了可以跨平台的圖形化使用者介面組件,包括視窗、菜單、捲軸和對話方塊等
java.applet: 支援applet程式設計的基本包
(6) 安全性
用於網路、分布環境下的Java必須要防止病毒的入侵,Java不支援指標,一切對記憶體的訪問都必須通過對象的執行個體變數來實現,這樣就防止了程式員使用欺騙手段訪問對象的私人成員,同時也避免了指標操作中容易產生的錯誤。
4 JAVA工具
(1) JDK
1) Java編譯器
Java編譯器將Java原始碼檔案編譯成可執行檔Java位元組碼。Java原始碼檔案的副檔名為 .java,Java編譯器把這種副檔名的檔案編譯成副檔名為.class的檔案。源檔案中的每個類在編譯後都將產生一個class檔案,這意味一個Java原始碼檔案可能編譯產生多個class檔案。
2) Java解譯器
Java解譯器對編譯產生的位元組碼格式的可執行程式的運行提供支援,它是運行非圖形Java程式的命令列工具。
3) Appletviewer
它是Java Applet的簡單測試載入器,可使用它來測試Java Applet程式,而不需要WWW瀏覽器的支援。
(2) Visual J++
Visual J++ 整合了可視化介面設計、互動式調試、代碼編輯、線上說明資訊和介紹如何快速掌握該開發環境的實用嚮導等多項功能,同時具有能充分利用Active X和COM新技術的優勢。利用Visual J++可建立互動性很強的Internet應用程式,是難得的Java 開發系統。
5 客戶機/伺服器通訊的實現:
(1) Application 同Applet 的通訊
兩端通過Socket機制進行串連:
1) 用戶端的編程流程:
開啟Socket,建立一個通訊端;
為通訊端建立一個輸入和輸出資料流;
根據伺服器協議從通訊端讀入或向通訊端寫入;
清除通訊端和輸入/輸出流;
2)伺服器端的編程流程:
開啟Server Socket,建立一個伺服器型通訊端和一個普通通訊端,伺服器型通訊端在指定連接埠為用戶端請求的Socket 服務;
使用ServerSocket類的accept()方法使伺服器型通訊端處於監聽狀態並把監聽結果返回給普通通訊端;
為該普通通訊端建立輸入和輸出資料流;
從輸入和輸出資料流中讀入或寫入位元組流,進行相應的處理,並將結果返回給用戶端;
在用戶端和伺服器工作結束後關閉所有的對象,如伺服器型的通訊端,普通通訊端,輸入和輸出資料流。
正是由於Java系統具有基於Socket的靈活通訊機制,因而其應用程式能自由地開啟和訪問網路上的對象,就象在本地檔案系統中一樣。
(2) Applet之間的通訊:
Applet之間的通訊使用Applet Context類的getApplet()方法。
只要在程式中加入
Applet oneapplet=getAppletContext().getApplet(“first”);便可使用name為first的Applet中的方法了。
在該課題中大量使用了該種通訊方法,因為專門同伺服器端通訊的 Applet中包含接收資訊方法和發送資訊方法,所有用戶端的Applet都要使用負責通訊的Applet中的方法,所以用戶端的Applet同負責通訊的Applet必須進行通訊。
6 程式
//伺服器端程式S.java 負責與用戶端通訊
import java.io.*;
import java.net.*;
import java.lang.*;
import T2;
class ThreadEchoHandler extends Thread //建立線程
{
T2 theT2=new T2();
Socket incoming;
int counter;
ThreadEchoHandler(Socket i,int c)
{ incoming=i;
counter=c; }
public void run()
{
try
{
DataInputStream in=new DataInputStream(incoming.getInputStream());
DataOutputStream out=new DataOutputStream(incoming.getOutputStream());
System.out.println ("hello");
boolean done=false;
while(!done)
{ String aa="";
String str=in.readUTF(); //從用戶端得到字串
//在此加入各自的服務程式
System.out.println (str);
theT2.pass(str); //解碼
theT2.tongji(); //修改監控庫中的資訊
aa=theT2.guan(); //操縱資料庫
System.out.println ("string z is:"+aa);
if(aa.compareTo("null")!=0 )
//若是查詢資料庫,返回查詢後的結果
{ //若不是查詢資料庫,不向用戶端輸出資訊
out.writeUTF(aa);
out.flush(); }
}//while
incoming.close(); //線程關閉
}//try
catch(IOException e)
{System.out.println(e);}
}//end run
}
//----------------------------------------
class S
{
public static void main(String[] args)
{
int i=1;
try
{
ServerSocket s=new ServerSocket(1111);
for(;;)
{
Socket incoming=s.accept();
System.out.println("connect: "+i);
new ThreadEchoHandler(incoming,i).start();
i++;
}
}
catch(Exception e)
{ System.out.println(e); }
}
}
//用戶端通訊小應用程式 Echo.java
import java.io.*;
import java.net.*;
import java.awt.*;
import java.applet.*;
public class Echo extends Applet
{
TextArea ta;
Socket echoSocket;
DataOutputStream os;
DataInputStream is;
String Line;
public void init()
{
setBackground(Color.white);
ta=new TextArea(5,80);
ta.setEditable(false);
add(ta);
try
{echoSocket=new Socket("10.102.4.41",1111);} //與伺服器建立串連
catch(IOException e)
{System.out.println("error");}
}
public void st(String stri) //發送字串的方法
{
try
{ DataOutputStream os=new DataOutputStream(echoSocket.getOutputStream());
DataInputStream is=new DataInputStream(echoSocket.getInputStream());
os.writeUTF(""+ stri ); //向伺服器輸送string
os.flush();
}
catch(IOException e)
{System.out.println(" error:"+e); }
}
public String st1() //接收字串的方法
{
String Line="";
try
{ DataOutputStream os=new DataOutputStream(echoSocket.getOutputStream());
DataInputStream is=new DataInputStream(echoSocket.getInputStream());
Line=is.readUTF(); //從伺服器讀來的資訊
ta.appendText(""+Line); //在文本域中輸出資訊
}
catch(IOException e)
{System.out.println(" error:"+e); }
return Line;
}
}
7 程式調試心得:
1) 在建立Socket串連時,兩端的連接埠號碼必須設為一致,否則建立不了串連。伺服器端必須有主機IP地址或主機名稱參數。
2) 串連建立好之後應確定輸入和輸出資料流。起初程式中用的是DataInputStream和PrintStream,結果只能傳輸英文,傳輸中文時產生亂碼,將PrintStream改為DataOutputStream,使用readUTF()和writeUTF()方法後,中文傳輸問題得到解決。
3) 如果一個使用某連接埠的程式沒有關閉,另一個程式就不能使用這個連接埠。
4) 開始進行通訊的程式均為 Application,因不符合客戶機/伺服器機制,應將用戶端的Application改為Applet。其轉化的主要步驟如下:
建立一個包含APPLET標籤的HTML檔案;
去掉應用程式中的main()方法;
類名應繼承Applet類,而不是Frame類,並在程式開頭加入
import java.applet.*;語句;
用init()方法代替Application程式中的構造方法,當瀏覽器建立Applet類對象的時候,它自動執行init()方法;
如Application中預設使用了BorderLayout布局管理器,應在Applet的init()方法中重新設定;
如果Application中有setTitle()方法,必須將其去掉,如Application中使用了菜單,在Applet 中用按鈕來替換。
5) 懂得了在一程式中如何引用自訂的類中的方法和變數,在程式開頭加入import 類名;在程式中加入 類名 執行個體=new 類名(); 然後使用
執行個體.方法(),執行個體.變數即可。
參考文獻:
[1] 廖雷等,Java程式設計教程,中國電力出版社,2003
[2] Warton,Java多線程編程初步,電腦報,2004.4.10
[3] 張立等,基於Client/Server模式的資料庫應用軟體的設計與實現,電腦應用研究,1999(4)
本文轉自
http://www.jsjlw.cn/(igtcdyrzsppnyj550t0dht55)/n716c13.aspx