137
技術社區[雲棲]
Java網絡編程(一)
關於JAVA網絡編程的技術非常繁多,如:SOCKET、RMI、EJB、WEBSERVICE、MQ、中間數據等等方法,但是萬變都是源於基礎中通信原理,有些是輕量級的、有重量級的;有實時調用、有異步調用;這麼多的技術可以說什麼都可以用,關鍵在什麼場合用什麼最適合你,這些技術主要用於多個子係統之間相互通信的方法,如一個大型的軟件應用分多個子係統,它們可能由不同的廠商來完成,這些子係統最終需要整合為一個係統,那麼整合的基礎就是交互,要麼是通過數據交互,要麼是通過接口調用,要麼通過中間數據等等。本文從最基本的網絡編程開始說起,逐步引入SOCKET的編程,其餘的後續逐步加入。
付:學SCOKET一定要學會流,但是流也是JAVA語言上最難的其中之一,不過不用畏懼,因為JAVA語言本身比較簡單,再難也難不倒那裏去,JAVA最好的是設計和架構的思想,當然語言本身也具有一定的魅力,但語言本身我個人認為他不是JAVA長久不衰的資本。
1、JAVA讀網頁流文件
2、IP地址解析
3、最簡單的SCOKET程序
4、通過SCOKET傳送對象
5、SCOKET多線程交互或服務
6、廣播方法
下麵寫入正文:
1、JAVA讀網頁流文件
//下麵的代碼是用於將百度首頁的HTML內容讀取到本地
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; public class URLConnectTest { public static void main(String []agrs) { URLConnection conn = null; BufferedReader reader = null; try { URL url = new URL("https://www.baidu.com"); conn = url.openConnection(); conn.connect(); String contentType = conn.getContentType(); System.out.println("類型&字符集:"+contentType); System.out.println("文本長度:"+conn.getContentLength()); //System.out.println(conn.getDate()); //System.out.println(conn.getLastModified()); //System.out.println(conn.getExpiration()); reader = new BufferedReader(new InputStreamReader(conn.getInputStream(),contentType.substring(contentType.indexOf("charset=")+8))); String str = null; System.out.println("資源文件內容如下:"); while((str = reader.readLine())!=null) { System.out.println(str); } reader.close(); } catch (MalformedURLException e) { e.printStackTrace(); } catch(IOException e) { e.printStackTrace(); }finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } }
這段代碼運行後將會輸出百度首頁的HTML代碼,你可以通過這些方法去挖掘一些網絡資源信息,將其通過一定的規則解析後,得到相應可用數據到本地的數據庫或者參數文件中,為你本地的係統提供一些輔助性的數據(如:天氣預報、相關新聞等等作為本地係統的一種友情提示)。
這段應該很容看懂,不用多說,紅色部分稍微有點點暈,這部分主要是為了保證字符集統一,方便在輸出時解析為對應字符集進行輸出,如:網站輸出是UTF-8,通過通過conn.getContentType()獲取到字符集為:text/html;charset=utf-8 ,此時是將utf-8這部分截取出來說明是需要解析的對應字符集,所以在這裏使用了一個substring操作。
至於細節,這部分代碼就不用多說,非常簡單。
2、IP地址解析
IP地址解析,首先基礎獲取INTERNET對象的一種方法:
InetAddress.getLocalHost() 獲取本地主機的相關信息
InetAddress.getByName("www.baidu.com") 通過名稱獲取相應域名的相關信息的對象
其獲取到的對象類型為:java.net.InetAddress類型,該類型的對象可以通過方法:getAddress()可以獲取到相應地址的byte信息以及getHostAddress直接獲取到IP地址、getHostName獲取到主機名或域名。
下麵先直接給出一段測試代碼,方便查看:
import java.net.InetAddress; import java.net.UnknownHostException; public class InetAddressTest { public static void main(String[] args) { try { displayOneAddress(InetAddress.getLocalHost(), "通過方法獲取本機信息"); displayOneAddress(InetAddress.getByName("localhost"), "通過名稱本機信息"); displaySomeAddress(InetAddress.getAllByName("www.baidu.com"), "通過名稱獲取百度的信息"); } catch (UnknownHostException e) { e.printStackTrace(); } } private static void displaySomeAddress(InetAddress[] address, String file) { for (int i = 0, size = address.length; i < size; i++) { displayOneAddress(address[i], file + "第" + (i + 1) + "個主機。"); } } private static void displayOneAddress(InetAddress address, String title) { System.out.print("/n" + title + "/t"); System.out.println(address); byte[] byte1 = address.getAddress(); if (byte1.length == 4) { System.out.print("IPV4協議。IP地址為:"); } else { System.out.print("IPV6協議。IP地址為:"); } for (int i = 0, size = byte1.length; i < size; i++) { int tmp = (byte1[i] >= 0) ? byte1[i] : (256 + byte1[i]); System.out.print(tmp + "."); } System.out.println("/n"+address.getHostAddress()); System.out.println("主機名:" + address.getHostName()); } }
在我本機運行後將會輸出:
通過方法獲取本機信息 xieyu/192.168.0.111
IPV4協議。IP地址為:192.168.0.111.
192.168.0.111
主機名:xieyu
通過名稱本機信息 localhost/127.0.0.1
IPV4協議。IP地址為:127.0.0.1.
127.0.0.1
主機名:localhost
通過名稱獲取百度的信息第1個主機。 www.baidu.com/119.75.218.45
IPV4協議。IP地址為:119.75.218.45.
119.75.218.45
主機名:www.baidu.com
通過名稱獲取百度的信息第2個主機。 www.baidu.com/119.75.217.56
IPV4協議。IP地址為:119.75.217.56.
119.75.217.56
主機名:www.baidu.com
這裏使用displayOneAddress方法主要就是用於查看一個主機對象下麵的相關信息,以及分別使用那一種解析方法進行解析,即獲取到的地址和實際的地址與256之間的關係;另外使用displaySomeAddress主要為了顯示多個主機信息的情況上麵分別顯示了本機和通過主機名獲取本機、百度(直接對外的,內部負載無法得知)主機信息。
3、最簡單的SCOKET程序
socket編程也是非常古老但是一直沒有過時的一種技術,因為它真的很不錯,現在很多通信協議也都是基於SOCKET為基礎編寫的,尤其是胖客戶端的平台或C/S結構,很多時候需要一種長連接機製來完成,使用SOCKET的確是很好的選擇,這裏首先給一個最簡單的SOCKET程序開個頭吧:
要寫一個SCOKET程序最少要寫兩段代碼來實現,即一個服務端、一個客戶端,而且兩段代碼都要一起運行才能使得運行成功(所謂一起運行就是開兩個窗口別分JAVA兩個JAVA文件或者有集成工具也有其它的辦法,啟動一般是先啟動服務器端,然後再啟動客戶端)。
此時首先構造服務器端的一段簡單代碼:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class NormalServer { public static void main(String []agrs) { ServerSocket server = null; try { server = new ServerSocket(8080); System.out.println("服務器信息為:"+server.getInetAddress().getHostAddress()+"/t"+server.getLocalPort()); while(true) { Socket socket = server.accept();//等待接收一個請求 System.out.println("接收到一個請求:"+socket.getInetAddress()); BufferedReader sin = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter writer = new PrintWriter(socket.getOutputStream(),true); String str; System.out.println("開始讀取數據。"); while(!"baibai".equals(str = sin.readLine())) { System.out.println("/t"+str); } System.out.println("服務器端讀取數據完畢。"); writer.println("服務器端讀取完畢!!!!!!!!!!"); writer.flush(); writer.close(); sin.close(); } } catch (IOException e) { e.printStackTrace(); }finally { if(server != null) { try { server.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
這裏在服務器端開辟了一個8080端口,如果你的8080端口被占用,可以換用其它端口即可,服務端讀取數據並輸出客戶端發送的數據,待客戶端有結束符號“baibai”的時候,就終止讀取,並向客戶端輸出讀取完畢的標誌。
此時來編寫一個客戶端向服務器端發送數據:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; public class NoramlClient { public static void main(String []agrs) { try { Socket socket = new Socket("localhost",8080); //BufferedReader pin = new BufferedReader(new InputStreamReader(System.in)); BufferedReader sin = new BufferedReader(new InputStreamReader(socket.getInputStream())); PrintWriter writer = new PrintWriter(socket.getOutputStream(),true); String line = "謝宇"; writer.println(line); writer.println("baibai"); writer.flush(); System.out.println(sin.readLine()); sin.close(); //pin.close(); writer.close(); socket.close(); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
此時創建一個和服務器端的鏈接,我這裏使用的localhost代表我本地,具體情況可以具體修改,端口也是這樣,在這個SCOKET基礎上開通一個輸入流和輸出流,此時向服務器端發送數據,並得到服務器端返回的數據。
此時將兩個JAVA文件分別編譯成class文件,然後開啟兩個終端,第一個終端先將服務器端的class運行起來,然後再運行第二個class,第二個class可以反複執行。
可能大家代碼都看得懂,估計就是這個“流”有點點暈,在這裏先不提及流的細節,因為這個可能提及起來專門寫幾十頁也寫不完,而就SOCKET這部分首先明確一點,就是這是服務器端還是客戶端,SCOKET對象是誰,好了,分兩種情況說明:
1、服務器端得到的scoket對象是客戶端發送過來的,這個套接字包含了客戶端的相關信息以及連接信息,這個對象客戶創建輸出輸出流,這個輸入流就是輸入到本機內存的意思,而輸出流是輸出到終端的意思,所以服務器端使用輸入流來讀取客戶端的信息,因為輸入流的信息就是讀入到本機內存的,而通過輸出流向客戶端發送信息,就是向一個終端發送信息,這個終端我相信大家用的最多的就是屏幕,而屏幕輸出用得最多的就是System.out.println(),自己看看這段代碼的源碼你會發現它也是由輸出流來完成的,所謂輸出就是輸出到終端(終端可以是網絡、可以是屏幕、可以是外圍設備、可以磁盤文件等等),而他們在很多情況下都有一些默認值,如在WEB編程中的內置對象out,就是向網絡客戶端輸出信息,它也是基於流去實現的。
2、客戶端發送socket也是同樣的道理,相對客戶端,此時的輸出流就是向服務器端輸出了,而輸出流就是 從服務器端獲取到的信息。
不知道我這樣解釋是否能夠聽明白,不過我自己是這樣理解的,對於流的概念還很多,這裏隻是開個頭而已,如上麵提及的System.out.print你可以讓他不輸出到屏幕,隻需要你做一個相應的PringStream對象(假如叫做outer對象),它可以指向其他地方,如一個文件,此時通過設置:System.setOut(outer)來完成設置過程,此時當你使用System.out.println()的時候,它將不會再輸出到屏幕,而是輸出到文件,其餘輸入流也可以;流也有很多種,很多流之間存在一種規則性的轉換方法,用好了有些時候如魚得水,用不好則到處是問題。
4、通過SCOKET傳送對象
未完待續。。。。。。。。。。。
5、SCOKET多線程交互或服務
6、廣播方法
最後更新:2017-04-02 06:51:24