Java高級篇——網絡通訊

JAdam html

Java高級篇(二)——網絡通訊

  網絡編程是每一個開發人員工具相中的核心部分,咱們在學習了諸多Java的知識後,也將步入幾個大的方向,Java網絡編程就是其中之一。java

  現在強調網絡的程序不比涉及網絡的更多。除了經典的應用程序,如電子郵件、Web瀏覽器和遠程登錄外,大多數主要的應用程序都有某種程度的內質網絡功能。好比咱們最常使用的IDE(Eclipse/IDEA)與源代碼存儲庫(GitHub等等)進行通訊;再好比Word,能夠從URL打開文件;又或者是咱們玩的衆多聯機遊戲,玩家實時相互對戰等等。Java是第一個從一開始就爲網絡應用而設計的編程語言,最先的兩個實用Java應用的程序之一就是Web瀏覽器,隨着Internet的不斷髮展,Java成爲了惟一適合構建下一代網絡應用程序的語言。(節選自Java NetWork Programming——Elliotte Rusty Harold著編程

1、Client/Server

  爲了實現兩臺計算機的通訊,必需要用一個網絡線路鏈接兩臺計算機。服務器(Server)是指提供信息的計算機或程序,客戶機(Client)是指請求信息的計算機或程序,而網絡用於鏈接服務器與客戶機,實現二者相互通訊。瀏覽器

  下面咱們看一個簡單的通訊例子。服務器

  以下的Server程序是一個服務器端應用程序,使用 Socket 來監聽一個指定的端口。網絡

複製代碼

 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("-----------客戶端啓動-----------");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 鏈接到服務器併發送一個請求,而後等待一個響應。併發

複製代碼

 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 }

複製代碼

  先啓動服務器,運行結果以下:app

  

  再運行客戶端,並輸入要發送的信息,以下:socket

  

  此時Server就接收到了Client發送的消息,以下:編程語言

  

  這就是一個簡單的套接字通訊了,具體分析見下方TCP。

2、TCP

  TCP網絡程序設計是指利用Socket類編寫通訊程序,具體實例參照上例。

  套接字使用TCP提供了兩臺計算機之間的通訊機制。 客戶端程序建立一個套接字,並嘗試鏈接服務器的套接字。當鏈接創建時,服務器會建立一個 Socket 對象。客戶端和服務器如今能夠經過對 Socket 對象的寫入和讀取來進行通訊。java.net.Socket 類表明一個套接字,而且 java.net.ServerSocket 類爲服務器程序提供了一種來監聽客戶端,並與他們創建鏈接的機制。

  兩臺計算機間使用套接字創建TCP鏈接步驟以下:

  • 服務器實例化一個 ServerSocket 對象,表示經過服務器上的端口通訊。

  • 服務器調用 ServerSocket 類的 accept() 方法,該方法將一直等待,直到客戶端鏈接到服務器上給定的端口。

  • 服務器正在等待時,一個客戶端實例化一個 Socket 對象,指定服務器名稱和端口號來請求鏈接。

  • Socket 類的構造函數試圖將客戶端鏈接到指定的服務器和端口號。若是通訊被創建,則在客戶端建立一個 Socket 對象可以與服務器進行通訊。

  • 在服務器端,accept() 方法返回服務器上一個新的 socket 引用,該 socket 鏈接到客戶端的 socket。

  鏈接創建後,經過使用 I/O 流在進行通訊,每個socket都有一個輸出流和一個輸入流,客戶端的輸出流鏈接到服務器端的輸入流,而客戶端的輸入流鏈接到服務器端的輸出流。

  1. InetAddress

  java.net包中的InetAddress類是與IP地址相關的類,利用該類能夠獲取IP地址、主機地址等信息。

InetAddress ip = InetAddress.getLocalHost();
String localName = ip.getHostName();    //獲取本地主機名String localIp = ip.getHostAddress();    //獲取本地主機的ip地址

  2. ServerSocket 

  java.net包中的ServetSocket類用於表示服務器套接字,其主要功能是等待來自網絡上的「請求」,它能夠經過指定的端口來等待鏈接的套接字(如上方實例中的9999)。

  而服務器套接字一次能夠與一個套接字鏈接,若是多臺客戶機同時提出鏈接請求,服務器套接字會將請求鏈接的客戶機存入隊列中,而後從中取出一個套接字,與服務器新建的套接字鏈接起來。若請求鏈接數大於最大容納數,則多出的鏈接請求被拒絕。

  ServerSocket的具體方法可參照API,這裏只對accept()方法進行一個說明。調用accept()方法將返回一個與客戶端Socket對象相連的Socket對象,服務器端的Socket對象使用getOutputStream()方法得到的輸出流對象,將指向客戶端Socket對象使用getInputStream()方法得到的輸入流對象,反之亦然。

  須要注意的是,accept()方法會阻塞線程的繼續執行,直到接受客戶的呼叫,若是沒有客戶呼叫服務器,那麼下面的代碼將不會執行。

3、UDP

  用戶數據包協議(UDP)是網絡信息傳輸的另外一種形式,基於UDP的通訊與基於TCP的通訊不一樣,UDP的信息傳遞更快,但不提供可靠的保證。

  1. DatagramPacket

  java.net包中的DatagramPacket類用來表示數據包,構造方法以下:

DatagramPacket(byte[] buf, int length)
DatagramPacket(byte[] buf, int length, InetAddress address, int port)

  上述構造方法中,第一種指定了數據包的內存空間和大小,第二種不只指定了數據包的內存空間和大小,而且指定了數據包的目標地址和端口。

  2. DatagramSocket

  java.net包中的DatagramSocket類用於表示發送和接受數據包的套接字,構造方法以下:

DatagramSocket()
DatagramSocket(int port)
DatagramSocket(int port, InetAddress addr)

  上述構造方法中,第一種建立數據包套接字並將其綁定到本地主機上任何可用的端口,第二種建立數據包套接字並將其綁定到本地主機上的指定端口,建立數據包套接字並將其綁定到指定的本機地址

 4、實例測試

  下面寫一個完整的實例,題目以下:

  • 創建服務端程序,服務器端程序接收來自客戶端的請求;

  • 從網上下載程序,英語900句,每句佔一行;

  • 服務端讀取該文件,保存到集合或者列表中;

  • 創建客戶端程序,使用」sentence: <編號#>,<編號#>」的格式發生數據。例如:發送」sentense:1,2,3」 , 服務端把相應編號的句子發送給客戶端,並加以呈現;

  • 客戶端須要把服務端發送的句子保存起來,若是已經保存有相應的句子,將再也不保存;

  • 客戶端須要把從服務端獲取的數據存儲到文件中。

   1. 程序結構

  

  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 adamjwh10  *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("-----------服務器啓動-----------");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

  該類爲服務器類。

複製代碼

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("發送:");
            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("服務器推送:" + s);                if(!result.contains(s)) {
                    fw.write(s + "\n");
                }
            }

            sk.shutdownInput();
            ps.close();
            sk.close();
            fw.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

複製代碼

  3. ServerThread

  經過線程實現信息交互。

複製代碼

 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. 運行結果

  先運行服務器

  

  再運行客戶端,並輸入信息

  

  服務器接收到客戶端發來的請求

  

   服務器將請求結果推送給客戶端,這裏客戶端就獲取到了它請求的第174句、第258句及第5句,並輸出到文件中

  

  這裏的測試文件我測試到了162(也就是該文件的第15行),能夠看到在文件中追加了兩行新的句子(174和258),而第5句由於在以前已經出現過了(該文件的第1行),因此沒有追加第5句,完成上述題目。

  

相關文章
相關標籤/搜索