歡迎您光臨本站 註冊首頁

基於Socket的低層次Java網路編程

←手機掃碼閱讀     火星人 @ 2014-03-09 , reply:0
8.3.1 Socket通訊
網路上的兩個程序通過一個雙向的通訊連接實現數據的交換,這個雙向鏈路的一端稱為一個Socket.Socket通常用來實現客戶方和服務方的連接.Socket是TCP/IP協議的一個十分流行的編程界面,一個Socket由一個IP地址和一個埠號唯一確定.
在傳統的UNIX環境下可以操作TCP/IP協議的介面不止Socket一個,Socket所支持的協議種類也不光TCP/IP一種,因此兩者之間是沒有必然聯繫的.在Java環境下,Socket編程主要是指基於TCP/IP協議的網路編程.
8.3.2 Socket通訊的一般過程
使用Socket進行Client/Server程序設計的一般連接過程是這樣的:Server端Listen(監聽)某個埠是否有連接請求,Client端向Server端發出Connect(連接)請求,Server端向Client端發回Accept(接受)消息.一個連接就建立起來了.Server端和Client端都可以通過Send,Write等方法與對方通信.
對於一個功能齊全的Socket,都要包含以下基本結構,其工作過程包含以下四個基本的步驟:
(1) 創建Socket;
(2) 打開連接到Socket的輸入/出流;
(3) 按照一定的協議對Socket進行讀/寫操作;
(4) 關閉Socket.
8.3.3 創建Socket
java在包java.net中提供了兩個類Socket和ServerSocket,分別用來表示雙向連接的客戶端和服務端.這是兩個封裝得非常好的類,使用很方便.其構造方法如下:
Socket(InetAddress address, int port);
Socket(InetAddress address, int port, boolean stream);
Socket(String host, int prot);
Socket(String host, int prot, boolean stream);
Socket(SocketImpl impl)
Socket(String host, int port, InetAddress localAddr, int localPort)
Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
ServerSocket(int port);
ServerSocket(int port, int backlog);
ServerSocket(int port, int backlog, InetAddress bindAddr)
其中address、host和port分別是雙向連接中另一方的IP地址、主機名和埠號,stream指明socket是流socket還是數據報socket,localPort表示本地主機的埠號,localAddr和bindAddr是本地機器的地址(ServerSocket的主機地址),impl是socket的父類,既可以用來創建serverSocket又可以用來創建Socket.count則表示服務端所能支持的最大連接數.例如:
Socket client = new Socket("127.0.01.", 80);
ServerSocket server = new ServerSocket(80);
注意,在選擇埠時,必須小心.每一個埠提供一種特定的服務,只有給出正確的埠,才能獲得相應的服務.0~1023的埠號為系統所保留,例如http服務的埠號為80,telnet服務的埠號為21,ftp服務的埠號為23, 我們在選擇埠號時,最好選擇一個大於1023的數以防止發生衝突.


在創建socket時如果發生錯誤,將產生IOException,在程序中必須對之作出處理.在創建Socket或ServerSocket是必須捕獲或拋出例外.
8.3.8 簡單的Client/Server程序設計
下面我們給出一個用Socket實現的客戶和伺服器交互的典型的C/S結構的演示程序,讀者通過仔細閱讀該程序,會對前面所討論的各個概念有更深刻的認識.程序的意義請參考註釋.
1. 客戶端程序
import java.io.*;
import java.net.*;
public class TalkClient {
public static void main(String args[]) {
try{
Socket socket=new Socket("127.0.0.1",4700);
//向本機的4700埠發出客戶請求
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
//由系統標準輸入設備構造BufferedReader對象
PrintWriter os=new PrintWriter(socket.getOutputStream());
//由Socket對象得到輸出流,並構造PrintWriter對象
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//由Socket對象得到輸入流,並構造相應的BufferedReader對象
String readline;
readline=sin.readLine(); //從系統標準輸入讀入一字元串
while(!readline.equals("bye")){
//若從標準輸入讀入的字元串為 "bye"則停止循環
os.println(readline);
//將從系統標準輸入讀入的字元串輸出到Server
os.flush();
//刷新輸出流,使Server馬上收到該字元串
System.out.println("Client:" readline);
//在系統標準輸出上列印讀入的字元串
System.out.println("Server:" is.readLine());
//從Server讀入一字元串,並列印到標準輸出上
readline=sin.readLine(); //從系統標準輸入讀入一字元串
} //繼續循環
os.close(); //關閉Socket輸出流
is.close(); //關閉Socket輸入流
socket.close(); //關閉Socket
}catch(Exception e) {
System.out.println("Error" e); //出錯,則列印出錯信息
}
}
}
2. 伺服器端程序
import java.io.*;
import java.net.*;
import java.applet.Applet;
public class TalkServer{
public static void main(String args[]) {
try{
ServerSocket server=null;
try{
server=new ServerSocket(4700);
//創建一個ServerSocket在埠4700監聽客戶請求
}catch(Exception e) {
System.out.println("can not listen to:" e);
//出錯,列印出錯信息
}
Socket socket=null;
try{
socket=server.accept();
//使用accept()阻塞等待客戶請求,有客戶
//請求到來則產生一個Socket對象,並繼續執行
}catch(Exception e) {


System.out.println("Error." e);
//出錯,列印出錯信息
}
String line;
BufferedReader is=new BufferedReader(new InputStreamReader(socket.getInputStream()));
//由Socket對象得到輸入流,並構造相應的BufferedReader對象
PrintWriter os=newPrintWriter(socket.getOutputStream());
//由Socket對象得到輸出流,並構造PrintWriter對象
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
//由系統標準輸入設備構造BufferedReader對象
System.out.println("Client:" is.readLine());
//在標準輸出上列印從客戶端讀入的字元串
line=sin.readLine();
//從標準輸入讀入一字元串
while(!line.equals("bye")){
//如果該字元串為 "bye",則停止循環
os.println(line);
//向客戶端輸出該字元串
os.flush();
//刷新輸出流,使Client馬上收到該字元串
System.out.println("Server:" line);
//在系統標準輸出上列印讀入的字元串
System.out.println("Client:" is.readLine());
//從Client讀入一字元串,並列印到標準輸出上
line=sin.readLine();
//從系統標準輸入讀入一字元串
}  //繼續循環
os.close(); //關閉Socket輸出流
is.close(); //關閉Socket輸入流
socket.close(); //關閉Socket
server.close(); //關閉ServerSocket
}catch(Exception e){
System.out.println("Error:" e);
//出錯,列印出錯信息
}
}
}
8.3.9 支持多客戶的client/server程序設計
前面提供的Client/Server程序只能實現Server和一個客戶的對話.在實際應用中,往往是在伺服器上運行一個永久的程序,它可以接收來自其他多個客戶端的請求,提供相應的服務.為了實現在伺服器方給多個客戶提供服務的功能,需要對上面的程序進行改造,利用多線程實現多客戶機制.伺服器總是在指定的埠上監聽是否有客戶請求,一旦監聽到客戶請求,伺服器就會啟動一個專門的服務線程來響應該客戶的請求,而伺服器本身在啟動完線程之後馬上又進入監聽狀態,等待下一個客戶的到來.
ServerSocket serverSocket=null;
boolean listening=true;
try{
serverSocket=new ServerSocket(4700);
//創建一個ServerSocket在埠4700監聽客戶請求
}catch(IOException e) {  }
while(listening){ //永遠循環監聽
new ServerThread(serverSocket.accept(),clientnum).start();
//監聽到客戶請求,根據得到的Socket對象和
客戶計數創建服務線程,並啟動之
clientnum ; //增加客戶計數
}


serverSocket.close(); //關閉ServerSocket
設計ServerThread類
public class ServerThread extends Thread{
Socket socket=null; //保存與本線程相關的Socket對象
int clientnum; //保存本進程的客戶計數
public ServerThread(Socket socket,int num) { //構造函數
this.socket=socket; //初始化socket變數
clientnum=num 1; //初始化clientnum變數
}
public void run() { //線程主體
try{//在這裡實現數據的接受和發送}
8.3.10 據報Datagram通訊
前面在介紹TCP/IP協議的時候,我們已經提到,在TCP/IP協議的傳輸層除了TCP協議之外還有一個UDP協議,相比而言UDP的應用不如TCP廣泛,幾個標準的應用層協議HTTP,FTP,SMTP…使用的都是TCP協議.但是,隨著計算機網路的發展,UDP協議正越來越來顯示出其威力,尤其是在需要很強的實時交互性的場合,如網路遊戲,視頻會議等,UDP更是顯示出極強的威力,下面我們就介紹一下Java環境下如何實現UDP網路傳輸.
8.3.11 什麼是Datagram
所謂數據報(Datagram)就跟日常生活中的郵件系統一樣,是不能保證可靠的寄到的,而面向鏈接的TCP就好比電話,雙方能肯定對方接受到了信息.在本章前面,我們已經對UDP和TCP進行了比較,在這裡再稍作小節:
TCP,可靠,傳輸大小無限制,但是需要連接建立時間,差錯控制開銷大.
UDP,不可靠,差錯控制開銷較小,傳輸大小限制在64K以下,不需要建立連接.
8.3.12 Datagram通訊的表示方法:DatagramSocket;DatagramPacket
包java.net中提供了兩個類DatagramSocket和DatagramPacket用來支持數據報通信,DatagramSocket用於在程序之間建立傳送數據報的通信連接, DatagramPacket則用來表示一個數據報.先來看一下DatagramSocket的構造方法:
DatagramSocket();
DatagramSocket(int prot);
DatagramSocket(int port, InetAddress laddr)
其中,port指明socket所使用的埠號,如果未指明埠號,則把socket連接到本地主機上一個可用的埠.laddr指明一個可用的本地地址.給出埠號時要保證不發生埠衝突,否則會生成SocketException類例外.注意:上述的兩個構造方法都聲明拋棄非運行時例外SocketException,程序中必須進行處理,或者捕獲、或者聲明拋棄.
用數據報方式編寫client/server程序時,無論在客戶方還是服務方,都要建立一個DatagramSocket對象,用來接收或發送數據報,然後使用DatagramPacket類對象作為傳輸數據的載體.下面看一下DatagramPacket的構造方法 :
DatagramPacket(byte buf[],int length);
DatagramPacket(byte buf[], int length, InetAddress addr, int port);
DatagramPacket(byte[] buf, int offset, int length);


DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port);
其中,buf中存放數據報數據,length為數據報中數據的長度,addr和port旨明目的地址,offset指明了數據報的位移量.
在接收數據前,應該採用上面的第一種方法生成一個DatagramPacket對象,給出接收數據的緩衝區及其長度.然後調用DatagramSocket 的方法receive()等待數據報的到來,receive()將一直等待,直到收到一個數據報為止.
DatagramPacket packet=new DatagramPacket(buf, 256);
Socket.receive (packet);
發送數據前,也要先生成一個新的DatagramPacket對象,這時要使用上面的第二種構造方法,在給出存放發送數據的緩衝區的同時,還要給出完整的目的地址,包括IP地址和埠號.發送數據是通過DatagramSocket的方法send()實現的,send()根據數據報的目的地址來尋徑,以傳遞數據報.
DatagramPacket packet=new DatagramPacket(buf, length, address, port);
Socket.send(packet);
在構造數據報時,要給出InetAddress類參數.類InetAddress在包java.net中定義,用來表示一個Internet地址,我們可以通過它提供的類方法getByName()從一個表示主機名的字元串獲取該主機的IP地址,然後再獲取相應的地址信息.
8.3.14 用數據報進行廣播通訊
DatagramSocket只允許數據報發送一個目的地址,java.net包中提供了一個類MulticastSocket,允許數據報以廣播方式發送到該埠的所有客戶.MulticastSocket用在客戶端,監聽伺服器廣播來的數據.
1. 客戶方程序:MulticastClient.java
import java.io.*;
import java.net.*;
import java.util.*;
public class MulticastClient {
public static void main(String args[]) throws IOException
{
MulticastSocket socket=new MulticastSocket(4446);
//創建4446埠的廣播套接字
InetAddress address=InetAddress.getByName("230.0.0.1");
//得到230.0.0.1的地址信息
socket.joinGroup(address);
//使用joinGroup()將廣播套接字綁定到地址上
DatagramPacket packet;
for(int i=0;i<5;i ) {
byte[] buf=new byte[256];
//創建緩衝區
packet=new DatagramPacket(buf,buf.length);
//創建接收數據報
socket.receive(packet); //接收
String received=new String(packet.getData());
//由接收到的數據報得到位元組數組,
//並由此構造一個String對象
System.out.println("Quote of theMoment:" received);
//列印得到的字元串
} //循環5次
socket.leaveGroup(address);
//把廣播套接字從地址上解除綁定
socket.close(); //關閉廣播套接字
}
}


2. 伺服器方程序:MulticastServer.java
public class MulticastServer{
public static void main(String args[]) throws java.io.IOException
{
new MulticastServerThread().start();
//啟動一個伺服器線程
}
}
3. 程序MulticastServerThread.java
import java.io.*;
import java.net.*;
import java.util.*;
public class MulticastServerThread extends QuoteServerThread
//從QuoteServerThread繼承得到新的伺服器線程類MulticastServerThread
{
Private long FIVE_SECOND=5000; //定義常量,5秒鐘
public MulticastServerThread(String name) throws IOException
{
super("MulticastServerThread");
//調用父類,也就是QuoteServerThread的構造函數
}
public void run() //重寫父類的線程主體
{
while(moreQuotes) {
//根據標誌變數判斷是否繼續循環
try{
byte[] buf=new byte[256];
//創建緩衝區
String dString=null;
if(in==null) dString=new Date().toString();
//如果初始化的時候打開文件失敗了,
//則使用日期作為要傳送的字元串
else dString=getNextQuote();
//否則調用成員函數從文件中讀出字元串
buf=dString.getByte();
//把String轉換成位元組數組,以便傳送send it
InetAddress group=InetAddress.getByName("230.0.0.1");
//得到230.0.0.1的地址信息
DatagramPacket packet=new DatagramPacket(buf,buf.length,group,4446);
//根據緩衝區,廣播地址,和埠號創建DatagramPacket對象
socket.send(packet); //發送該Packet
try{
sleep((long)(Math.random()*FIVE_SECONDS));
//隨機等待一段時間,0~5秒之間
}catch(InterruptedException e) { } //異常處理
}catch(IOException e){ //異常處理
e.printStackTrace( ); //列印錯誤棧
moreQuotes=false; //置結束循環標誌
}
}
socket.close( ); //關閉廣播套介面
}
}


[火星人 ] 基於Socket的低層次Java網路編程已經有882次圍觀

http://coctec.com/docs/java/show-post-60070.html