java對于網絡通訊有着非常強大的支援。不僅可以擷取網絡資源,傳遞參數到遠端伺服器,還可以通過socket對象實作tcp協定,通過datagramsocket對象實作udp協定。同時,對于多點廣播以及代理伺服器也有着非常強大的支援。以下是本人在學習過程中的總結和歸納。
1. java的基本網絡支援
1.1 inetaddress
java中的inetaddress是一個代表ip位址的對象。ip位址可以由位元組數組和字元串來分别表示,inetaddress将ip位址以對象的形式進行封裝,可以更友善的操作和擷取其屬性。inetaddress沒有構造方法,可以通過兩個靜态方法獲得它的對象。代碼如下:
1.2 urldecoder和urlencoder
這兩個類可以别用于将application/x-www-form-urlencoded mime類型的字元串轉換為普通字元串,将普通字元串轉換為這類特殊型的字元串。使用urldecoder類的靜态方法decode()用于解碼,urlencoder類的靜态方法encode()用于編碼。具體使用方法如下。
1.3 url和urlconnection
url可以被認為是指向網際網路資源的“指針”,通過url可以獲得網際網路資源相關資訊,包括獲得url的inputstream對象擷取資源的資訊,以及一個到url所引用遠端對象的連接配接urlconnection。
urlconnection對象可以向所代表的url發送請求和讀取url的資源。通常,建立一個和url的連接配接,需要如下幾個步驟:
a. 建立url對象,并通過調用openconnection方法獲得urlconnection對象;
b. 設定urlconnection參數和普通請求屬性;
c. 向遠端資源發送請求;
d. 遠端資源變為可用,程式可以通路遠端資源的頭字段和通過輸入流來讀取遠端資源傳回的資訊。
這裡需要重點讨論一下第三步:如果隻是發送get方式請求,使用connect方法建立和遠端資源的連接配接即可;如果是需要發送post方式的請求,則需要擷取urlconnection對象所對應的輸出流來發送請求。這裡需要注意的是,由于get方法的參數傳遞方式是将參數顯式追加在位址後面,那麼在構造url對象時的參數就應當是包含了參數的完整url位址,而在獲得了urlconnection對象之後,就直接調用connect方法即可發送請求。
而post方法傳遞參數時僅僅需要頁面url,而參數通過需要通過輸出流來傳遞。另外還需要設定頭字段。以下是兩種方式的代碼。
另外需要注意的是,如果既需要讀取又需要發送,一定要先使用輸出流,再使用輸入流。因為遠端資源不會主動向本地發送請求,必須要先請求資源。
2. 基于tcp協定的網絡程式設計
tcp協定是一種可靠的通絡協定,通信兩端的socket使得它們之間形成網絡虛拟鍊路,兩端的程式可以通過虛拟鍊路進行通訊。java使用socket對象代表兩端的通信端口,并通過socket産生的io流來進行網絡通信。
2.1 serversocket
在兩個通信端沒有建立虛拟鍊路之前,必須有一個通信實體首先主動監聽來自另一端的請求。serversocket對象使用accept()方法用于監聽來自用戶端的socket連接配接,如果收到一個用戶端socket的連接配接請求,該方法将傳回一個與用戶端socket對應的socket對象。如果沒有連接配接,它将一直處于等待狀态。通常情況下,伺服器不應隻接受一個用戶端請求,而應該通過循環調用accept()不斷接受來自用戶端的所有請求。
這裡需要注意的是,對于多次接收用戶端資料的情況來說,一方面可以每次都在用戶端建立一個新的socket對象然後通過輸入輸出通訊,這樣對于伺服器端來說,每次循環所接收的内容也不一樣,被認為是不同的用戶端。另外,也可以隻建立一次,然後在這個虛拟鍊路上通信,這樣在伺服器端一次循環的内容就是通信的全過程。
伺服器端的示例代碼:
2.2 socket
使用socket可以主動連接配接到伺服器端,使用伺服器的ip位址和端口号初始化之後,伺服器端的accept便可以解除阻塞繼續向下執行,這樣就建立了一對互相連接配接的socket。
用戶端示例代碼:
2.3 使用多線程
在複雜的通訊中,使用多線程非常必要。對于伺服器來說,它需要接收來自多個用戶端的連接配接請求,處理多個用戶端通訊需要并發執行,那麼就需要對每一個傳過來的socket在不同的線程中進行處理,每條線程需要負責與一個用戶端進行通信。以防止其中一個用戶端的處理阻塞會影響到其他的線程。對于用戶端來說,一方面要讀取來自伺服器端的資料,另一方面又要向伺服器端輸出資料,它們同樣也需要在不同的線程中分别處理。
具體代碼如下,伺服器端:
用戶端:
2.4 使用協定字元
協定字元用于辨別一些字段的特定功能,用于說明傳輸内容的特性。它可以由使用者自定義。一般情況下,可以定義一個存放這些協定字元的接口。如下:
在字段時可以加上這些字元,如下代碼:
收到發送來的字段時候,也再次拆分成所需要的部分,如下代碼:
3. udp協定的網絡程式設計
udp協定是一種不可靠的網絡協定,它在通訊執行個體的兩端個建立一個socket,但這兩個socket之間并沒有虛拟鍊路,這兩個socket隻是發送和接受資料報的對象,java提供了datagramsocket對象作為基于udp協定的socket,使用datagrampacket代表datagramsocket發送和接收的資料報。
3.1 使用datagramsocket發送、接收資料
datagramsocket本身并不負責維護狀态和産生io流。它僅僅負責接收和發送資料報。使用receive(datagrampacket p)方法接收,使用send(datagrampacket p)方法發送。
這裡需要首先明确的是,datagrampacket對象的構造。datagrampacket的内部實際上采用了一個位元組型數組來儲存資料,它的初始化方法如下:
作為這兩個方法的參數,作用和構造不同的。作為接收方法中的參數,datagrampacket中的數組一個空的數組,用來存放接收到的datagrampacket對象中的數組;而作為發送方法參數,datagrampacket本身含有了目的端的ip和端口,以及存儲了要發送内容的指定了長度的位元組型數組。
另外,datagrampacket對象還提供了setdata(byte[] b)和byte[] b= getdata()方法,用于設定datagrampacket中包含的數組内容和獲得其中包含數組的内容。
使用tcp和udp通訊的編碼差別:
a. 在tcp中,目标ip和端口由socket指定包含;udp中,目标ip由datagrampacket包含指定,datagramsocket隻負責發送和接受。
b. 在tcp中,通訊是通過socket獲得的io流來實作;在udp中,則通過datagramsocket的send和receive方法。
3.2 使用multicastsocket實作多點廣播
multicastsocket是datagramsocket的子類,可以将資料報以廣播形式發送到數量不等的多個用戶端。實作政策就是定義一個廣播位址,使得每個multicastsocket都加入到這個位址中。進而每次使用multicastsocket發送資料報(包含的廣播位址)時,所有加入了這個廣播位址的multicastsocket對象都可以收到資訊。
multicastsocket的初始化需要傳遞端口号作為參數,特别對于需要接受資訊的端來說,它的端口号需要與發送端資料報中包含的端口号一緻。具體代碼如下:
4. 使用代理伺服器
java中可以使用proxy直接建立連接配接代理伺服器,具體使用方法如下:
5. 編碼中的問題總結
a. 雙方初始化套接字以後,就等于建立了連結,表示雙方互相可以知曉對方的狀态。伺服器端可以調用接收到的用戶端套接字進行輸入輸出流操作,用戶端可以調用自身内部的套接字對象進行輸入輸出操作。這樣可以保持輸入輸出的流暢性。例如,用戶端向伺服器端發送消息時,可以隔一段的時間輸入一段資訊,然後伺服器端使用循環不斷的讀取傳過來的輸入流。
b. 對于可能出現阻塞的方法,例如用戶端進行循環不斷讀取來自伺服器端的響應資訊時,如果此時伺服器端并沒有向用戶端進行輸出,那麼讀取的方法将處于阻塞狀态,直到收到資訊為止才向下執行代碼。那麼對于這樣容易産生阻塞的代碼,就需要将它放在一個單獨的線程中處理。
c. 有一些流是順承的。例如,伺服器端在收到用戶端的消息以後,就将消息再通過輸出流向其他所有伺服器發送。那麼,這個來自用戶端的輸入流和發向用戶端的輸出流就是順接的關系,不必對它們分在兩個不同的線程。
d. println()方法對應readline()。
e. 在jframe類中,一般不要将自己的代碼寫進main方法中,可以将代碼寫到自定義的方法中,然後在main方法中調用。
注: 博文來自對《瘋狂java講義》的學習。