Java程序员从笨鸟到菜鸟之(十三)java网络通信编程
首先聲明一下,剛開始學習Java網絡通信編程就對他有一種畏懼感,因為自己對網絡一竅不通,所以。。。呵呵。。你懂得,昨天又仔細的學習了一遍,感覺其實java網絡編程也沒想象的那么難,不信,咱一起看看。。。呵呵。。
?????網絡編程就是在兩個或兩個以上的設備(例如計算機)之間傳輸數據。程序員所作的事情就是把數據發送到指定的位置,或者接收到指定的數據,這個就是狹義的網絡編程范疇。在發送和接收數據時,大部分的程序設計語言都設計了專門的API實現這些功能,程序員只需要調用即可。所以,基礎的網絡編程可以和打電話一樣簡單
一:首先看一下網絡通訊的兩種方式
1.TCP(傳輸控制協議)方式
TCP方式就類似于撥打電話,使用該種方式進行網絡通訊時,需要建立專門的虛擬連接,然后進行可靠的數據傳輸,如果數據發送失敗,則客戶端會自動重發該數據
2.?UDP(用戶數據報協議)方式
?????UDP方式就類似于發送短信,使用這種方式進行網絡通訊時,不需要建立專門的虛擬連接,傳輸也不是很可靠,如果發送失敗則客戶端無法獲得
?這兩種傳輸方式都是實際的網絡編程中進行使用,重要的數據一般使用TCP方式進行數據傳輸,而大量的非核心數據則都通過UDP方式進行傳遞,在一些程序中甚至結合使用這兩種方式進行數據的傳遞。由于TCP需要建立專用的虛擬連接以及確認傳輸是否正確,所以使用TCP方式的速度稍微慢一些,而且傳輸時產生的數據量要比UDP稍微大一些。
總結一下UDP和TCP協議的區別
-使用UDP時,每個數據報中都給出了完整的地址信息,因此無需要建立發送方和接收方的連接。?
–對于TCP協議,由于它是一個面向連接的協議,在socket之間進行數據傳輸之前必然要建立連接,所以在TCP中多了一個連接建立的時間?
–使用UDP傳輸數據時是有大小限制的,每個被傳輸的數據報必須限定在64KB之內。?
–TCP沒有這方面的限制,一旦連接建立起來,雙方的socket就可以按統一的格式傳輸大量的數據。?
–UDP是一個不可靠的協議,發送方所發送的數據報并不一定以相同的次序到達接收方。?
–TCP是一個可靠的協議,它確保接收方完全正確地獲取發送方所發送的全部數據?
-TCP在網絡通信上有極強的生命力,例如遠程連接(Telnet)和文件傳輸(FTP)都需要不定長度的數據被可靠地傳輸。?
—相比之下UDP操作簡單,而且僅需要較少的監護,因此通常用于局域網高可靠性的分散系統中client/server應用程序?
二:基于url的網絡編程
1.創建一個URL
為了表示URL,?java.Net中實現了類URL。我們可以通過下面的構造方法來初始化一個URL對象:
(1)?public?URL?(String?spec);
通過一個表示URL地址的字符串可以構造一個URL對象
URL?urlBase=new?URL("http://www.?263.net/")
? (2)?public?URL(URL?context,?String?spec);
通過基URL和相對URL構造一個URL對象。
URL?net263=new?URL?("http://www.263.net/");
URL?index263=new?URL(net263,?"index.html")
(3)?public?URL(String?protocol,?String?host,?String?file);
new?URL("http",?"www.gamelan.com",?"/pages/Gamelan.net.?html");
(4)?public?URL(String?protocol,?String?host,?int?port,?String?file);
URL?gamelan=new?URL("http",?"www.gamelan.com",?80,?"Pages/Gamelan.network.html");
注意:類URL的構造方法都聲明拋棄非運行時例外(MalformedURLException),因此生成URL對象時,我們必須要對這一例外進行處理,通常是用try-catch語句進行捕獲。格式如下:
try{
URL?myURL=?new?URL(…)
}catch?(MalformedURLException?e){
… }
2.?解析一個URL
一個URL對象生成后,其屬性是不能被改變的,但是我們可以通過類URL所提供的方法來獲取這些屬性:
public?String?getProtocol()?獲取該URL的協議名。
public?String?getHost()?獲取該URL的主機名。
public?int?getPort()?獲取該URL的端口號,如果沒有設置端口,返回-1。
public?String?getFile()?獲取該URL的文件名。
public?String?getRef()?獲取該URL在文件中的相對位置。
public?String?getQuery()?獲取該URL的查詢信息。
public?String?getPath()?獲取該URL的路徑
??public?String?getAuthority()?獲取該URL的權限信息
public?String?getUserInfo()?獲得使用者的信息
?public?String?getRef()獲得該URL的錨
3.從URL讀取WWW網絡資源
當我們得到一個URL對象后,就可以通過它讀取指定的WWW資源。這時我們將使用URL的方法openStream(),其定義為:
InputStream?openStream();
方法openSteam()與指定的URL建立連接并返回InputStream類的對象以從這一連接中讀取數據。
URL?url?=?new?URL("http://www.baidu.com");
//使用openStream得到一輸入流并由此構造一個BufferedReader對象
BufferedReader?br?=?new?BufferedReader(new?InputStreamReader( url.openStream()));
String?line?=?null;
while(null?!=?(line?=?br.readLine()))
{
System.out.println(line);
}
br.close();
三:客戶端網絡編程步驟
按照前面的基礎知識介紹,無論使用TCP方式還是UDP方式進行網絡通訊,網絡編程都是由客戶端和服務器端組成
1.客戶端網絡編程步驟
????????客戶端(Client)是指網絡編程中首先發起連接的程序,客戶端一般實現程序界面和基本邏輯實現,在進行實際的客戶端編程時,無論客戶端復雜還是簡單,以及客戶端實現的方式,客戶端的編程主要由三個步驟實現:
1、?建立網絡連接
客戶端網絡編程的第一步都是建立網絡連接。在建立網絡連接時需要指定連接到的服務器的IP地址和端口號,建立完成以后,會形成一條虛擬的連接,后續的操作就可以通過該連接實現數據交換了。
2、?交換數據
連接建立以后,就可以通過這個連接交換數據了。交換數據嚴格按照請求響應模型進行,由客戶端發送一個請求數據到服務器,服務器反饋一個響應數據給客戶端,如果客戶端不發送請求則服務器端就不響應。
根據邏輯需要,可以多次交換數據,但是還是必須遵循請求響應模型。
3、?關閉網絡連接
在數據交換完成以后,關閉網絡連接,釋放程序占用的端口、內存等系統資源,結束網絡編程。
????????最基本的步驟一般都是這三個步驟,在實際實現時,步驟2會出現重復,在進行代碼組織時,由于網絡編程是比較耗時的操作,所以一般開啟專門的現場進行網絡通訊。
2.服務器端網絡編程步驟
????????服務器端(Server)是指在網絡編程中被動等待連接的程序,服務器端一般實現程序的核心邏輯以及數據存儲等核心功能。服務器端的編程步驟和客戶端不同,是由四個步驟實現,依次是:
1、?監聽端口
服務器端屬于被動等待連接,所以服務器端啟動以后,不需要發起連接,而只需要監聽本地計算機的某個固定端口即可。
這個端口就是服務器端開放給客戶端的端口,服務器端程序運行的本地計算機的IP地址就是服務器端程序的IP地址。
2、?獲得連接
當客戶端連接到服務器端時,服務器端就可以獲得一個連接,這個連接包含客戶端的信息,例如客戶端IP地址等等,服務器端和客戶端也通過該連接進行數據交換。
一般在服務器端編程中,當獲得連接時,需要開啟專門的線程處理該連接,每個連接都由獨立的線程實現。
3、?交換數據
服務器端通過獲得的連接進行數據交換。服務器端的數據交換步驟是首先接收客戶端發送過來的數據,然后進行邏輯處理,再把處理以后的結果數據發送給客戶端。簡單來說,就是先接收再發送,這個和客戶端的數據交換數序不同。
其實,服務器端獲得的連接和客戶端連接是一樣的,只是數據交換的步驟不同。
當然,服務器端的數據交換也是可以多次進行的。
在數據交換完成以后,關閉和客戶端的連接。
4、?關閉連接
當服務器程序關閉時,需要關閉服務器端,通過關閉服務器端使得服務器監聽的端口以及占用的內存可以釋放出來,實現了連接的關閉。
四:一個基礎的網絡類——InetAddress類
????該類的功能是代表一個IP地址,并且將IP地址和域名相關的操作方法包含在該類的內部。
????關于該類的使用,下面通過一個基礎的代碼示例演示該類的使用,代碼如下:
[java]?view plaincopy print?注:InetAddress?類沒有明顯的構造函數。為生成一個InetAddress對象,必須運用一個可用的工廠方法。?
–工廠方法(factory?method)僅是一個類中靜態方法返回一個該類實例的約定。對于InetAddress,三個方法?getLocalHost(?)、getByName(?)以及getAllByName(?)可以用來創建InetAddress的實例?
?如果這些方法不能解析主機名,它們引發一個UnknownHostException異常。?
五:TCP編程
在Java語言中,對于TCP方式的網絡編程提供了良好的支持,在實際實現時,以java.net.Socket類代表客戶端連接,以java.net.ServerSocket類代表服務器端連接。在進行網絡編程時,底層網絡通訊的細節已經實現了比較高的封裝,所以在程序員實際編程時,只需要指定IP地址和端口號碼就可以建立連接了。
在客戶端網絡編程中,首先需要建立連接,在Java?API中以java.net.Socket類的對象代表網絡連接
客戶端
1)?建立Socket連接?
Socket?socket2?=?new?Socket(“www.sohu.com”,80);
2)按照“請求-響應”模型進行網絡數據交換
在Java語言中,數據傳輸功能由Java?IO實現,也就是說只需要從連接中獲得輸入流和輸出流即可,然后將需要發送的數據寫入連接對象的輸出流中,在發送完成以后從輸入流中讀取數據即可。示例代碼如下:
?OutputStream?os?=?socket1.getOutputStream();?//獲得輸出流
?InputStream?is?=?socket1.getInputStream();?????//獲得輸入流
這里獲得的只是最基本的輸出流和輸入流對象,還可以根據前面學習到的IO知識,使用流的嵌套將這些獲得到的基本流對象轉換成需要的裝飾流對象,從而方便數據的操作。
3)關閉網絡連接
?socket1.close();
服務器端
首先需要說明的是,客戶端的步驟和服務器端的編寫步驟不同,所以在學習服務器端編程時注意不要和客戶端混淆起來。
1)監聽端口
ServerSocket?ss?=?new?ServerSocket(10000);
2)獲得連接
當有客戶端連接到達時,建立一個和客戶端連接對應的Socket連?接對象,從而釋放客戶端連接對于服務器端端口的占用
Socket?socket?=?ss.accept();
該代碼實現的功能是獲得當前連接到服務器端的客戶端連接。需要說明的是accept和前面IO部分介紹的read方法一樣,都是一個阻塞方法,也就是當無連接時,該方法將阻塞程序的執行,直到連接到達時才執行該行代碼。另外獲得的連接會在服務器端的該端口注冊,這樣以后就可以通過在服務器端的注冊信息直接通信,而注冊以后服務器端的端口就被釋放出來,又可以繼續接受其它的連接了。
3)按照“請求-響應”模型進行網絡數據交換
這里獲得的Socket類型的連接就和客戶端的網絡連接一樣了,只是服務器端需要首先讀取發送過來的數據,然后進行邏輯處理以后再發送給客戶端,也就是交換數據的順序和客戶端交換數據的步驟剛好相反
?InputStream?is?=?ss.getInputStream();?????//獲得輸入流
OutputStream?os?=?ss.getOutputStream();?//獲得輸出流
4)關閉服務器端連接
ss.close();
以上就是基本的TCP類型的服務器和客戶端代碼實現的步驟,下面以一個簡單的echo(回聲)服務實現為例子,介紹綜合使用示例,實現的代碼如下:
[java]?view plaincopy print?UDP編程
?????UDP(User?Datagram?Protocol),中文意思是用戶數據報協議使用該種方式無需建立專用的虛擬連接,由于無需建立專用的連接,所以對于服務器的壓力要比TCP小很多,所以也是一種常見的網絡編程方式。但是使用該種方式最大的不足是傳輸不可靠,當然也不是說經常丟失,就像大家發短信息一樣,理論上存在收不到的可能
?????在Java?API中,實現UDP方式的編程,包含客戶端網絡編程和服務器端網絡編程,主要由兩個類實現,分別是:
l?DatagramSocket
DatagramSocket類實現“網絡連接”,包括客戶端網絡連接和服務器端網絡連接。雖然UDP方式的網絡通訊不需要建立專用的網絡連接,但是畢竟還是需要發送和接收數據,DatagramSocket實現的就是發送數據時的發射器,以及接收數據時的監聽器的角色。類比于TCP中的網絡連接,該類既可以用于實現客戶端連接,也可以用于實現服務器端連接。
l?DatagramPacket
DatagramPacket類實現對于網絡中傳輸的數據封裝,也就是說,該類的對象代表網絡中交換的數據。在UDP方式的網絡編程中,無論是需要發送的數據還是需要接收的數據,都必須被處理成DatagramPacket類型的對象,該對象中包含發送到的地址、發送到的端口號以及發送的內容等。其實DatagramPacket類的作用類似于現實中的信件,在信件中包含信件發送到的地址以及接收人,還有發送的內容等,郵局只需要按照地址傳遞即可。在接收數據時,接收到的數據也必須被處理成DatagramPacket類型的對象,在該對象中包含發送方的地址、端口號等信息,也包含數據的內容。和TCP方式的網絡傳輸相比,IO編程在UDP方式的網絡編程中變得不是必須的內容,結構也要比TCP方式的網絡編程簡單一些。
UDP客戶端編程涉及的步驟也是4個部分:建立連接、發送數據、接收數據和關閉連接。
1)建立連接:
DatagramSocket?ds?=?new?DatagramSocket();
該客戶端連接使用系統隨機分配的一個本地計算機的未用端口號
?當然,可以通過制定連接使用的端口號來創建客戶端連接。
??DatagramSocket?ds?=?new?DatagramSocket(5000);
一般在建立客戶端連接時沒有必要指定端口號碼。
2)發送數據
在發送數據時,需要將需要發送的數據內容首先轉換為byte數組,然后將數據內容、服務器IP和服務器端口號一起構造成一個DatagramPacket類型的對象,這樣數據的準備就完成了了,發送時調用網絡連接對象中的send方法發送該對象即可
代碼示例:
[java]?view plaincopy print?
?
??????在該示例代碼中,不管發送的數據內容是什么,都需要轉換為byte數組,然后將服務器端的IP地址構造成InetAddress類型的對象,在準備完成以后,將這些信息構造成一個DatagramPacket類型的對象,在UDP編程中,發送的數據內容、服務器端的IP和端口號,都包含在DatagramPacket對象中。在準備完成以后,調用連接對象ds的send方法把DatagramPacket對象發送出去即可。
3)UDP客戶端編程中接收數據
?????首先構造一個數據緩沖數組,該數組用于存儲接收的服務器端反饋數據,該數組的長度必須大于或等于服務器端反饋的實際有效數據的長度。然后以該緩沖數組為基礎構造一個DatagramPacket數據包對象,最后調用連接對象的receive方法接收數據即可。接收到的服務器端反饋數據存儲在DatagramPacket類型的對象內部
示例代碼:
[java]?view plaincopy print?
?
代碼講解?:??首先構造緩沖數組data,這里設置的長度1024是預估的接收到的數據長度,要求該長度必須大于或等于接收到的數據長度,然后以該緩沖數組為基礎,構造數據包對象,使用連接對象ds的receive方法接收反饋數據,由于在Java語言中,除String以外的其它對象都是按照地址傳遞,所以在receive方法內部可以改變數據包對象receiveDp的內容,這里的receiveDp的功能和返回值類似。數據接收到以后,只需要從數據包對象中讀取出來就可以了,使用DatagramPacket對象中的getData方法可以獲得數據包對象的緩沖區數組,但是緩沖區數組的長度一般大于有效數據的長度,換句話說,也就是緩沖區數組中只有一部分數據是反饋數據,所以需要使用DatagramPacket對象中的getLength方法獲得有效數據的長度,則有效數據就是緩沖數組中的前有效數據長度個內容,這些才是真正的服務器端反饋的數據的內容
4)關閉連接
??ds.close();
UDP方式服務器端網絡編程
1)首先UDP方式服務器端網絡編程需要建立一個連接,該連接監聽某個端口:
?DatagramSocket?ds?=?new?DatagramSocket(10010);
由于服務器端的端口需要固定,所以一般在建立服務器端連接時,都指定端口號
2)接收客戶端發送過來的數據
其接收的方法和客戶端接收的方法一直,其中receive方法的作用類似于TCP方式中accept方法的作用,該方法也是一個阻塞方法,其作用是接收數據。
ds.receive()
接收到客戶端發送過來的數據以后,服務器端對該數據進行邏輯處理,然后將處理以后的結果再發送給客戶端,在這里發送時就比客戶端要麻煩一些,因為服務器端需要獲得客戶端的IP和客戶端使用的端口號,這個都可以從接收到的數據包中獲得。示例代碼如下:
?????//獲得客戶端的IP
?????InetAddress?clientIP?=?receiveDp.getAddress();
?????????//獲得客戶端的端口號
?????????Int?clientPort?=?receiveDp.getPort();
3)關閉連接
ds.close()
??????? 好了,占時就總結到這吧,總結的不是很全面,但很基礎,應該適合初學者學習,由于本人也是初學者的小菜鳥,所有很多東西可能都涉及不到,希望大家見諒!
?
?本文來自:曹勝歡博客專欄。轉載請注明出處:http://blog.csdn.net/csh624366188
總結
以上是生活随笔為你收集整理的Java程序员从笨鸟到菜鸟之(十三)java网络通信编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Java程序员从笨鸟到菜鸟之(十二)ja
- 下一篇: Java程序员从笨鸟到菜鸟之(七十)细谈