Java高级篇(二)——网络通信
網(wǎng)絡(luò)編程是每個開發(fā)人員工具箱中的核心部分,我們在學(xué)習(xí)了諸多Java的知識后,也將步入幾個大的方向,Java網(wǎng)絡(luò)編程就是其中之一。
如今強(qiáng)調(diào)網(wǎng)絡(luò)的程序不比涉及網(wǎng)絡(luò)的更多。除了經(jīng)典的應(yīng)用程序,如電子郵件、Web瀏覽器和遠(yuǎn)程登陸外,大多數(shù)主要的應(yīng)用程序都有某種程度的內(nèi)質(zhì)網(wǎng)絡(luò)功能。比如我們最常使用的IDE(Eclipse/IDEA)與源代碼存儲庫(GitHub等等)進(jìn)行通信;再比如Word,可以從URL打開文件;又或者是我們玩的眾多聯(lián)機(jī)游戲,玩家實(shí)時(shí)相互對戰(zhàn)等等。Java是第一個從一開始就為網(wǎng)絡(luò)應(yīng)用而設(shè)計(jì)的編程語言,最早的兩個實(shí)用Java應(yīng)用的程序之一就是Web瀏覽器,隨著Internet的不斷發(fā)展,Java成為了唯一適合構(gòu)建下一代網(wǎng)絡(luò)應(yīng)用程序的語言。(節(jié)選自Java NetWork Programming——Elliotte Rusty Harold著)
一、Client/Server
為了實(shí)現(xiàn)兩臺計(jì)算機(jī)的通信,必須要用一個網(wǎng)絡(luò)線路連接兩臺計(jì)算機(jī)。服務(wù)器(Server)是指提供信息的計(jì)算機(jī)或程序,客戶機(jī)(Client)是指請求信息的計(jì)算機(jī)或程序,而網(wǎng)絡(luò)用于連接服務(wù)器與客戶機(jī),實(shí)現(xiàn)兩者相互通信。
下面我們看一個簡單的通信例子。
如下的Server程序是一個服務(wù)器端應(yīng)用程序,使用 Socket 來監(jiān)聽一個指定的端口。
1 import java.io.IOException; 2 import java.io.InputStreamReader; 3 import java.io.Reader; 4 import java.net.ServerSocket; 5 import java.net.Socket; 6 7 public class Server { 8 9 public static void main(String[] args) throws IOException { 10 int port = 9999; 11 12 System.out.println("-----------服務(wù)器啟動-----------"); 13 14 ServerSocket server = new ServerSocket(port); 15 Socket socket = server.accept(); 16 Reader reader = new InputStreamReader(socket.getInputStream()); 17 char chars[] = new char[1024]; 18 int len; 19 StringBuilder builder = new StringBuilder(); 20 while ((len=reader.read(chars)) != -1) { 21 builder.append(new String(chars, 0, len)); 22 } 23 System.out.println("收到來自客戶端的信息: " + builder); 24 reader.close(); 25 socket.close(); 26 server.close(); 27 } 28 29 }? 如下的Client是一個客戶端程序,該程序通過 socket 連接到服務(wù)器并發(fā)送一個請求,然后等待一個響應(yīng)。
1 import java.io.IOException; 2 import java.io.OutputStreamWriter; 3 import java.io.Writer; 4 import java.net.Socket; 5 import java.util.Scanner; 6 7 public class Client { 8 9 public static void main(String[] args) throws IOException { 10 String host = "127.0.0.1"; 11 int port = 9999; 12 13 System.out.println("-----------客戶端啟動-----------"); 14 15 Socket client = new Socket(host, port); 16 Writer writer = new OutputStreamWriter(client.getOutputStream()); 17 Scanner in = new Scanner(System.in); 18 writer.write(in.nextLine()); 19 writer.flush(); 20 writer.close(); 21 client.close(); 22 in.close(); 23 } 24 25 }先啟動服務(wù)器,運(yùn)行結(jié)果如下:
再運(yùn)行客戶端,并輸入要發(fā)送的信息,如下:
此時(shí)Server就接收到了Client發(fā)送的消息,如下:
這就是一個簡單的套接字通信了,具體分析見下方TCP。
二、TCP
TCP網(wǎng)絡(luò)程序設(shè)計(jì)是指利用Socket類編寫通信程序,具體實(shí)例參照上例。
套接字使用TCP提供了兩臺計(jì)算機(jī)之間的通信機(jī)制。 客戶端程序創(chuàng)建一個套接字,并嘗試連接服務(wù)器的套接字。當(dāng)連接建立時(shí),服務(wù)器會創(chuàng)建一個 Socket 對象??蛻舳撕头?wù)器現(xiàn)在可以通過對 Socket 對象的寫入和讀取來進(jìn)行通信。java.net.Socket 類代表一個套接字,并且 java.net.ServerSocket 類為服務(wù)器程序提供了一種來監(jiān)聽客戶端,并與他們建立連接的機(jī)制。
兩臺計(jì)算機(jī)間使用套接字建立TCP連接步驟如下:
-
服務(wù)器實(shí)例化一個 ServerSocket 對象,表示通過服務(wù)器上的端口通信。
-
服務(wù)器調(diào)用 ServerSocket 類的 accept() 方法,該方法將一直等待,直到客戶端連接到服務(wù)器上給定的端口。
-
服務(wù)器正在等待時(shí),一個客戶端實(shí)例化一個 Socket 對象,指定服務(wù)器名稱和端口號來請求連接。
-
Socket 類的構(gòu)造函數(shù)試圖將客戶端連接到指定的服務(wù)器和端口號。如果通信被建立,則在客戶端創(chuàng)建一個 Socket 對象能夠與服務(wù)器進(jìn)行通信。
-
在服務(wù)器端,accept() 方法返回服務(wù)器上一個新的 socket 引用,該 socket 連接到客戶端的 socket。
連接建立后,通過使用 I/O 流在進(jìn)行通信,每一個socket都有一個輸出流和一個輸入流,客戶端的輸出流連接到服務(wù)器端的輸入流,而客戶端的輸入流連接到服務(wù)器端的輸出流。
1. InetAddress
java.net包中的InetAddress類是與IP地址相關(guān)的類,利用該類可以獲取IP地址、主機(jī)地址等信息。
InetAddress ip = InetAddress.getLocalHost(); String localName = ip.getHostName(); //獲取本地主機(jī)名 String localIp = ip.getHostAddress(); //獲取本地主機(jī)的ip地址2. ServerSocket?
java.net包中的ServetSocket類用于表示服務(wù)器套接字,其主要功能是等待來自網(wǎng)絡(luò)上的“請求”,它可以通過指定的端口來等待連接的套接字(如上方實(shí)例中的9999)。
而服務(wù)器套接字一次可以與一個套接字連接,如果多臺客戶機(jī)同時(shí)提出連接請求,服務(wù)器套接字會將請求連接的客戶機(jī)存入隊(duì)列中,然后從中取出一個套接字,與服務(wù)器新建的套接字連接起來。若請求連接數(shù)大于最大容納數(shù),則多出的連接請求被拒絕。
ServerSocket的具體方法可參照API,這里只對accept()方法進(jìn)行一個說明。調(diào)用accept()方法將返回一個與客戶端Socket對象相連的Socket對象,服務(wù)器端的Socket對象使用getOutputStream()方法獲得的輸出流對象,將指向客戶端Socket對象使用getInputStream()方法獲得的輸入流對象,反之亦然。
需要注意的是,accept()方法會阻塞線程的繼續(xù)執(zhí)行,直到接受客戶的呼叫,如果沒有客戶呼叫服務(wù)器,那么下面的代碼將不會執(zhí)行。
三、UDP
用戶數(shù)據(jù)包協(xié)議(UDP)是網(wǎng)絡(luò)信息傳輸?shù)牧硪环N形式,基于UDP的通信與基于TCP的通信不同,UDP的信息傳遞更快,但不提供可靠的保證。
1. DatagramPacket
java.net包中的DatagramPacket類用來表示數(shù)據(jù)包,構(gòu)造方法如下:
DatagramPacket(byte[] buf, int length) DatagramPacket(byte[] buf, int length, InetAddress address, int port)上述構(gòu)造方法中,第一種指定了數(shù)據(jù)包的內(nèi)存空間和大小,第二種不僅指定了數(shù)據(jù)包的內(nèi)存空間和大小,并且指定了數(shù)據(jù)包的目標(biāo)地址和端口。
2. DatagramSocket
java.net包中的DatagramSocket類用于表示發(fā)送和接受數(shù)據(jù)包的套接字,構(gòu)造方法如下:
DatagramSocket() DatagramSocket(int port) DatagramSocket(int port, InetAddress addr)上述構(gòu)造方法中,第一種創(chuàng)建數(shù)據(jù)包套接字并將其綁定到本地主機(jī)上任何可用的端口,第二種創(chuàng)建數(shù)據(jù)包套接字并將其綁定到本地主機(jī)上的指定端口,創(chuàng)建數(shù)據(jù)包套接字并將其綁定到指定的本機(jī)地址。
?四、實(shí)例測試
下面寫一個完整的實(shí)例,題目如下:
- 建立服務(wù)端程序,服務(wù)器端程序接收來自客戶端的請求;
- 從網(wǎng)上下載程序,英語900句,每句占一行;
- 服務(wù)端讀取該文件,保存到集合或者列表中;
- 建立客戶端程序,使用”sentence: <編號#>,<編號#>”的格式發(fā)生數(shù)據(jù)。例如:發(fā)送”sentense:1,2,3” , 服務(wù)端把相應(yīng)編號的句子發(fā)送給客戶端,并加以呈現(xiàn);
- 客戶端需要把服務(wù)端發(fā)送的句子保存起來,如果已經(jīng)保存有相應(yīng)的句子,將不再保存;
- 客戶端需要把從服務(wù)端獲取的數(shù)據(jù)存儲到文件中。
? 1. 程序結(jié)構(gòu)
2. Server.java
該類為客戶端類。
1 import java.io.*; 2 import java.net.ServerSocket; 3 import java.net.Socket; 4 import java.util.ArrayList; 5 import java.util.List; 6 7 /** 8 * 9 * @author adamjwh 10 * 11 */ 12 public class Server { 13 14 public static List<String> sentence; 15 private static String filename = "src/com/adamjwh/jnp/ex6_2/English900.txt"; 16 17 public static void main(String[] args) throws IOException { 18 sentence=new ArrayList<>(); 19 System.out.println("-----------服務(wù)器啟動-----------"); 20 21 FileReader fileReader = new FileReader(filename); 22 BufferedReader br = new BufferedReader(fileReader); 23 24 String inputLine = null; 25 while ((inputLine = br.readLine()) != null) { 26 sentence.add(inputLine); 27 } 28 29 ServerSocket ss = new ServerSocket(9999); 30 while(true){ 31 Socket sk =ss.accept(); 32 ServerThread st = new ServerThread(sk); 33 st.start(); 34 } 35 36 } 37 }2. Client.java
該類為服務(wù)器類。
import java.io.BufferedReader; import java.io.FileWriter; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.Socket; import java.util.Scanner;/*** * @author adamjwh**/ public class Client {private static String filename = "src/com/adamjwh/jnp/ex6_2/result.txt";public static void main(String[] args) {try {Socket sk = new Socket("127.0.0.1", 9999);System.out.println("-----------客戶端啟動-----------");PrintStream ps = new PrintStream(sk.getOutputStream());System.out.print("發(fā)送:");Scanner sn = new Scanner(System.in);String str = sn.nextLine();ps.println(str);InputStream is = sk.getInputStream();InputStreamReader isr = new InputStreamReader(is);BufferedReader br = new BufferedReader(isr);//寫文件FileWriter fw = new FileWriter(filename, true);//讀文件String result = new ReadFile().readFile(filename);String s;while ((s = br.readLine()) != null) {System.out.println("服務(wù)器推送:" + s);if(!result.contains(s)) {fw.write(s + "\n");}}sk.shutdownInput();ps.close();sk.close();fw.close();} catch (Exception e) {e.printStackTrace();}} }3. ServerThread
通過線程實(shí)現(xiàn)信息交互。
1 import java.io.*; 2 import java.net.Socket; 3 4 /** 5 * 6 * @author adamjwh 7 * 8 */ 9 public class ServerThread extends Thread{ 10 Socket sk; 11 public ServerThread(Socket sk){ 12 this.sk= sk; 13 } 14 public void run() { 15 BufferedReader br=null; 16 try{ 17 br = new BufferedReader(new InputStreamReader(sk.getInputStream())); 18 String line = br.readLine(); 19 System.out.println("客戶端:"+line); 20 String[] split = line.split(":"); 21 String[] split1 = split[split.length - 1].split(","); 22 sk.shutdownInput(); 23 24 OutputStream os = sk.getOutputStream(); 25 26 PrintStream bw = new PrintStream(os); 27 28 //給客戶端返回信息 29 for(int i=0;i<split1.length;i++) { 30 bw.print(Server.sentence.get(Integer.parseInt(split1[i])%Server.sentence.size())+"\n"); 31 } 32 bw.flush(); 33 br.close(); 34 sk.close(); 35 } 36 catch(IOException e){ 37 e.printStackTrace(); 38 } 39 } 40 }? 4. 運(yùn)行結(jié)果
先運(yùn)行服務(wù)器
再運(yùn)行客戶端,并輸入信息
服務(wù)器接收到客戶端發(fā)來的請求
? 服務(wù)器將請求結(jié)果推送給客戶端,這里客戶端就獲取到了它請求的第174句、第258句及第5句,并輸出到文件中
這里的測試文件我測試到了162(也就是該文件的第15行),可以看到在文件中追加了兩行新的句子(174和258),而第5句因?yàn)樵谥耙呀?jīng)出現(xiàn)過了(該文件的第1行),所以沒有追加第5句,完成上述題目。
轉(zhuǎn)載于:https://www.cnblogs.com/adamjwh/p/8856516.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的Java高级篇(二)——网络通信的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 图片网址整理大全
- 下一篇: Java枚举根据key获取value