Java--Socket通訊

下面內容是Java開發內容的高級知識點,須要對Java中的面向對象、IO、多線程、以及網絡相關知識有必定的基礎。(知識永遠都有深度,本章節長期更新內容)html

 

一、網絡基礎知識java

 

網絡通訊的條件:一、兩個通訊的端都要有各自的IP地址做爲惟一標識,簡單的來講IP地址用來區分不一樣機器(計算機)。二、語言要相通。三、如何辨別不一樣程序的通訊須要端口號來區別,簡單的來講端口號就是用來標識不一樣的應用程序。

TCP/IP是目前世界上使用最普遍的協議,是以TCP和IP爲基礎的不一樣層次上多個協議的集合,也稱TCP/IP協議族 或 TCP/IP 協議棧。

TCP:Transmission Control Protocol 傳輸控制協議
IP:Internet Protocol 互聯網協議

TCP/IP模型編程

 

>>IP和端口<<
一、用於區分不一樣應用程序。
二、端口號範圍爲0~65535,其中0~1023爲系統所保留。若是自定義端口號,因此建議用1024以後的端口號。
三、IP地址和端口號組成了所謂的Socket,Socket是網絡上運行程序之間雙向通訊鏈路的終結點,是TCP和UDP的基礎。api


經常使用的端口號須要記一下:http:80    ftp:21    telnet:23數組

 

——————————Java中的網絡支持—————————
針對網絡通訊的不一樣層次,Java提供的網絡功能有四大類:
>>一、InetAddress:用於標識網絡上的硬件資源
>>二、URL:統一資源定位符 經過URL能夠直接讀取或寫入網絡上的數據
>>三、Socket:使用TCP協議實現網絡通訊的Socket相關的類。
>>四、Datagram:使用UDP協議,將數據保存在數據報中,經過網絡進行通訊。服務器

 


二、InetAddress類網絡

查看I-net-Address的API文檔,發現沒有構造方法,也就是不能經過new來建立。因此確定有靜態的方法來建立。多線程

 1 import java.net.InetAddress;
 2 import java.net.UnknownHostException;
 3 import java.util.Arrays;
 4 
 5 public class Test1{
 6     public static void main(String[] args) throws UnknownHostException{
 7         // 獲取本機的InetAdresss實例
 8         InetAddress address = InetAddress.getLocalHost();
 9         System.out.println("計算機名:"+address.getHostName()+"\nIP地址:"+address.getHostAddress());
10 
11         // 獲取字節數組形式的IP地址
12         byte[] bytes = address.getAddress();
13         System.out.println("字節數組形式的IP:"+Arrays.toString(bytes));
14         System.out.println(address);
15 
16         // 也能夠經過機器名來獲取InewAdress
17         InetAddress address2 = InetAddress.getByName("MacBook-Air-2.local");
18         System.out.println("經過計算機名字建立的InetAddress對象:"+address2);
19         System.out.println("計算機名:"+address2.getHostName());
20         System.out.println("IP地址:"+address2.getHostAddress());
21 
22         // 也能夠經過IP地址來獲取InewAdress
23         InetAddress address3 = InetAddress.getByName("192.168.1.102");
24         System.out.println("經過計算機IP地址建立的InetAddress對象:"+address3);
25         System.out.println("計算機名:"+address3.getHostName());
26         System.out.println("IP地址:"+address3.getHostAddress());
27 
28 
29     }
30 }

輸出結果:異步

 

 
三、URLsocket

 URL(Uniform Resource Locator)統一資源定位符,表示Internet上某一資源的地址。 俗稱就是網址。

 URL由兩部分組成:協議名稱+資源名稱。

 在Java.net包中,提供了URL類來表示URL。

 1 import java.net.MalformedURLException;
 2 import java.net.URL;
 3 
 4 public class Test1{
 5     public static void main(String[] args){
 6         
 7         try {
 8             // 建立一個URL實例
 9             URL imoocURL = new URL("http://www.imooc.com");
10             URL url = new URL(imoocURL,"/index.html?username=tom#test");
11             // ?後面表示參數,#後面表示的是錨點
12 
13             // 建立URL對象以後,能夠根據這個對象獲取相關的信息
14             System.out.println("協議:"+url.getProtocol());
15             System.out.println("主機:"+url.getHost());
16             // 若是未指定端口號,則使用默認的端口號,此時getPort()方法返回值爲-1
17             System.out.println("端口:"+url.getPort());
18             System.out.println("文件路徑:"+url.getPath());
19             System.out.println("文件名:"+url.getFile());
20             System.out.println("相對路徑:"+url.getRef());// 實際上就是#錨點後面的內容
21             System.out.println("查詢字符串:"+url.getQuery());
22 
23         } catch (MalformedURLException e) {
24             e.printStackTrace();
25         }
26 
27 
28     }
29 }

輸出:

 下面再經過URL讀取網頁內容:

 1 import java.net.MalformedURLException;
 2 import java.net.URL;
 3 import java.io.InputStream;
 4 import java.io.InputStreamReader;
 5 import java.io.BufferedReader;
 6 import java.io.IOException;
 7 
 8 /*
 9  * 使用URL讀取網頁頁面內容
10  */
11 public class Test1{
12     public static void main(String[] args){
13         
14         try {
15             // 建立一個URL實例
16             URL url = new URL("http://www.baidu.com");
17             // 經過URL的openStream方法獲取URL對象所表示的資源的字節輸入流
18             InputStream is = url.openStream();
19             // 將字節輸入流轉換爲字符輸入流
20             InputStreamReader isr = new InputStreamReader(is,"utf-8");// 若是沒有指明編碼可能會出現中文亂碼
21             // 爲字符輸入流添加緩衝
22             BufferedReader br = new BufferedReader(isr);
23             String data = br.readLine();// 讀取數據
24             while(data != null){
25                 System.out.println(data);// 輸出數據
26                 data = br.readLine();
27             }
28             // 最後按照上面對象倒序關閉
29             br.close();
30             isr.close();
31             is.close();
32         } catch (MalformedURLException e) {
33             e.printStackTrace();
34         } catch (IOException e) {
35             e.printStackTrace();
36         } 
37     }
38 }

輸入:

 


四、TCP編程

4-一、Socket簡介

TCP協議是面向鏈接、可靠的、有序的,以字節流的方式發送數據
基於TCP協議實現網絡通訊的類:
>>一、客戶端的Socket類
>>二、服務器端的ServerSocket類

基於Socket的TCP通訊模型

 

 Socket通訊實現步驟:一、建立ServerSocket和Socket。二、打開鏈接到Socket的輸入/輸出流。三、按照協議對Socket進行讀/寫操做。四、關閉輸入輸出流、關閉Socket。
經過在線API文檔:http://tool.oschina.net/apidocs/apidoc?api=jdk-zh 中查詢到的結果:

4-二、編程實現基於TCP的Socket服務器端和客戶端的通訊

服務器端:
    一、建立ServerSocket對象,綁定監聽端口。
    二、經過accept()方法監聽客戶端請求。
    三、連接創建後,經過輸入流讀取客戶端發送的請求信息。
    四、經過輸出流向客戶端發送響應信息。
    五、關閉相關資源。

 1 package com.heyang;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 import java.io.InputStreamReader;
 7 import java.net.ServerSocket;
 8 import java.net.Socket;
 9 
10 /*
11  * 基於TCP協議的Socket通訊,實現用戶登陸
12  * 服務器端
13  */
14 public class Server {
15     public static void main(String[] args) {
16         try {
17             // 一、建立一個服務器Socket,即ServerSocket,指定綁定的端口,並監聽此端口
18             ServerSocket serverSocket = new ServerSocket(8888);
19             // 二、調用()方法開始監聽,等待客戶端的鏈接
20             System.out.println("***服務器即將啓動,等待客戶端的鏈接***");
21             Socket socket = serverSocket.accept();// 就會處於阻塞的狀態,等待監聽
22             // 三、獲取輸入流,病讀取客戶端信息
23             InputStream is = socket.getInputStream();// 字節輸入流
24             // 將字節流轉換爲字符流
25             InputStreamReader isr = new InputStreamReader(is);
26             // 爲輸入流添加緩衝
27             BufferedReader br = new BufferedReader(isr);
28             String info = null;
29             while((info = br.readLine())!=null){
30                 System.out.println("我是服務器,讀取客戶端發過來的信息:"+info);
31             }
32             socket.shutdownInput();//關閉輸入流
33             
34             // 關閉資源
35             br.close();
36             isr.close();
37             is.close();
38             socket.close();
39             serverSocket.close();
40             
41         } catch (IOException e) {
42             // TODO Auto-generated catch block
43             e.printStackTrace();
44         }
45     }
46 }



客戶端:
    一、建立Socket對象,指明須要鏈接的服務器的地址和端口號。
    二、鏈接創建後,經過輸出流向服務器端發送請求信息。
    三、經過輸入流獲取服務器響應的信息。
    四、關閉相關資源。

 1 package com.heyang;
 2 
 3 import java.io.IOException;
 4 import java.io.OutputStream;
 5 import java.io.PrintWriter;
 6 import java.net.Socket;
 7 import java.net.UnknownHostException;
 8 
 9 /*
10  * 客戶端
11  */
12 public class Client {
13     public static void main(String[] args) {
14         // 一、建立客戶端Socket,指定服務器地址和端口
15         try {
16             Socket  socket = new Socket("localhost", 8888);
17             // 二、獲取輸出流,向服務器端發送信息
18             OutputStream os = socket.getOutputStream();// 獲取字節輸出流
19             // 將輸出流包裝爲打印流
20             PrintWriter pw = new PrintWriter(os);
21             pw.write("用戶名:admin 密碼:123");
22             pw.flush();
23             socket.shutdownInput();//關閉輸出流
24             
25             // 三、關閉資源
26             pw.close();
27             os.close();
28             socket.close();
29             
30         } catch (UnknownHostException e) {
31             // TODO Auto-generated catch block
32             e.printStackTrace();
33         } catch (IOException e) {
34             // TODO Auto-generated catch block
35             e.printStackTrace();
36         }
37     }
38 }

輸出:

 

 4-三、完善客戶端登錄以後服務器響應客戶端

代碼邏輯深化分析:

  在前面簡單的代碼的基礎上,咱們須要完善客戶端登錄以後服務器響應客戶端的邏輯。

  事實上,站在客戶端的角度,對外(這裏指的是服務器)發出數據流,也就是須要用輸出流來輸出數據流。

  反過來,站在服務器端的角度,就要不斷的監聽外部(這裏指的是外部客戶端)輸入進來的數據流,因此就須要輸入流來接收輸入進來的數據流。

  那麼,當服務器端收到客戶端傳輸過來的數據流以後,就應該響應,那麼這時候,站在服務器端的角度,要對外進行響應,就須要用輸出流來輸出響應回饋的信息,這時候就和客戶端一開始的輸出流代  碼一致了。

  回到客戶端,因爲須要接收和讀取服務器端的發出的響應,就須要輸入流來接收服務器發過來的回饋信息了,這時候就和服務器端一開始的輸入流代碼一致了。

 

服務器代碼:

 1 package com.heyang;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 import java.io.InputStreamReader;
 7 import java.io.OutputStream;
 8 import java.io.PrintWriter;
 9 import java.net.ServerSocket;
10 import java.net.Socket;
11 
12 /*
13  * 基於TCP協議的Socket通訊,實現用戶登陸
14  * 服務器端
15  */
16 public class Server {
17     public static void main(String[] args) {
18         try {
19             // 一、建立一個服務器Socket,即ServerSocket,指定綁定的端口,並監聽此端口
20             ServerSocket serverSocket = new ServerSocket(8888);
21             // 二、調用()方法開始監聽,等待客戶端的鏈接
22             System.out.println("***服務器即將啓動,等待客戶端的鏈接***");
23             Socket socket = serverSocket.accept();// 就會處於阻塞的狀態,等待監聽
24             // 三、獲取輸入流,病讀取客戶端信息
25             InputStream is = socket.getInputStream();// 字節輸入流
26             // 將字節流轉換爲字符流
27             InputStreamReader isr = new InputStreamReader(is);
28             // 爲輸入流添加緩衝
29             BufferedReader br = new BufferedReader(isr);
30             String info = null;
31             while((info = br.readLine())!=null){
32                 System.out.println("我是服務器,讀取客戶端發過來的信息:"+info);
33             }
34             socket.shutdownInput();//關閉輸入流
35             
36             // 四、做爲服務器端,就須要響應客戶端的請求,使用輸出流來響應
37             OutputStream os = socket.getOutputStream();
38             PrintWriter pw = new PrintWriter(os);
39             pw.write("歡迎您!");
40             pw.flush();//調用flush()方法將緩衝輸出
41             
42             
43             // 五、關閉資源
44             pw.close();
45             os.close();
46             br.close();
47             isr.close();
48             is.close();
49             socket.close();
50             serverSocket.close();
51             
52         } catch (IOException e) {
53             // TODO Auto-generated catch block
54             e.printStackTrace();
55         }
56     }
57 }

客戶端代碼:

 1 package com.heyang;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 import java.io.InputStreamReader;
 7 import java.io.OutputStream;
 8 import java.io.PrintWriter;
 9 import java.net.Socket;
10 import java.net.UnknownHostException;
11 
12 /*
13  * 客戶端
14  */
15 public class Client {
16     public static void main(String[] args) {
17         // 一、建立客戶端Socket,指定服務器地址和端口
18         try {
19             Socket  socket = new Socket("localhost", 8888);
20             // 二、獲取輸出流,向服務器端發送信息
21             OutputStream os = socket.getOutputStream();// 獲取字節輸出流
22             // 將輸出流包裝爲打印流
23             PrintWriter pw = new PrintWriter(os);
24             pw.write("用戶名:admin 密碼:123");
25             pw.flush();
26             socket.shutdownOutput();//關閉輸出流
27             
28             // 三、獲取輸入流,並讀取服務器端的響應信息
29             InputStream is = socket.getInputStream();
30             BufferedReader br = new BufferedReader(new InputStreamReader(is));
31             String info = null;
32             while((info = br.readLine())!=null){
33                 System.out.println("我是客戶端,服務器跟我說:"+info);
34             }
35             
36             // 四、關閉資源
37             br.close();
38             is.close();
39             pw.close();
40             os.close();
41             socket.close();
42             
43         } catch (UnknownHostException e) {
44             // TODO Auto-generated catch block
45             e.printStackTrace();
46         } catch (IOException e) {
47             // TODO Auto-generated catch block
48             e.printStackTrace();
49         }
50     }
51 }

具體運行結果就不展現了。

 

4-四、使用多線程實現多客戶端的通訊

 事實上,以上只實現了單個客戶端和單個服務器端進行socket通訊。那麼問題來了,實際應用程序是由一個服務器持續不斷的運行中,而後由多個客戶端異步經過服務器進行客戶端之間的收發信息,這該如何實現呢?

 基本步驟:

  一、服務器端建立ServerSocket,循環調用accept()等待客戶端鏈接。

  二、客戶端建立一個socket並請求和服務器端鏈接。

  三、服務器端接受客戶端請求,建立socket與該客戶端創建專線鏈接。

  四、創建鏈接的兩個socket在一個單獨的線程上對話。

  五、服務器端繼續等待新的鏈接。

服務器端的代碼:

 1 package com.heyang;
 2 
 3 
 4 import java.io.IOException;
 5 import java.net.InetAddress;
 6 import java.net.ServerSocket;
 7 import java.net.Socket;
 8 import com.heyang.ServerThread;;
 9 /*
10  * 基於TCP協議的Socket通訊,實現用戶登陸
11  * 服務器端
12  */
13 public class Server {
14     public static void main(String[] args) {
15         try {
16             // 一、建立一個服務器Socket,即ServerSocket,指定綁定的端口,並監聽此端口
17             ServerSocket serverSocket = new ServerSocket(8888);
18             // 二、調用()方法開始監聽,等待客戶端的鏈接
19             
20             // 記錄客戶端的數量
21             int count = 0;
22             System.out.println("***服務器即將啓動,等待客戶端的鏈接***");
23             
24             while(true){
25                 // 調用accept()方法開始監聽,等待客戶端的連接
26                 Socket socket = serverSocket.accept();
27                 // 建立一個新的線程
28                 ServerThread serverThread = new ServerThread(socket);
29                 // 啓動線程·
30                 serverThread.start();
31                 
32                 count++;
33                 System.out.println("客戶端鏈接的數量:"+count+"個");
34                 
35                 // 獲取客戶端的IP地址等信息
36                 InetAddress address = socket.getInetAddress();
37                 System.out.println("當前客戶端的IP:"+address.getHostAddress());
38                 
39             }
40             
41             // 須要死循環持續監聽客戶端的信息發送
42 //            serverSocket.close();
43             
44         } catch (IOException e) {
45             // TODO Auto-generated catch block
46             e.printStackTrace();
47         }
48     }
49 }

服務器線程代碼:

 1 package com.heyang;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 import java.io.InputStreamReader;
 7 import java.io.OutputStream;
 8 import java.io.PrintWriter;
 9 import java.net.Socket;
10 
11 /*
12  * 服務器端 線程處理類 
13  */
14 public class ServerThread extends Thread {
15     // 建立和本線程相關的socket
16     Socket socket = null;
17     
18     public ServerThread(Socket socket){
19         this.socket = socket;
20     }
21     
22     // 指向線程的操做,響應服務器端的請求
23     public void run(){
24         
25         InputStream is = null;
26         InputStreamReader isr = null;
27         BufferedReader br = null;
28         OutputStream os = null;
29         PrintWriter pw = null;
30         try {
31             // 三、獲取輸入流,病讀取客戶端信息
32             is = socket.getInputStream();// 字節輸入流
33             // 將字節流轉換爲字符流
34             isr = new InputStreamReader(is);
35             // 爲輸入流添加緩衝
36             br = new BufferedReader(isr);
37             String info = null;
38             while ((info = br.readLine()) != null) {
39                 System.out.println("我是服務器,讀取客戶端發過來的信息:" + info);
40             }
41             socket.shutdownInput();//關閉輸入流
42 
43             // 獲取輸出流
44             os = socket.getOutputStream();
45             pw = new PrintWriter(os);
46             pw.write("歡迎您!");
47             pw.flush();//調用flush()方法將緩衝輸出
48         } catch (Exception e) {
49             // TODO: handle exception
50         }finally{
51             try {
52                 // 五、關閉資源
53                 if (pw != null) {
54                     pw.close();
55                 }
56                 if (os != null) {
57                     os.close();
58                 }
59                 if (br != null) {
60                     br.close();
61                 }
62                 if (isr != null) {
63                     isr.close();
64                 }
65                 if (is != null) {
66                     is.close();
67                 }
68                 if (socket != null) {
69                     socket.close();
70                 } 
71             } catch (IOException e2) {
72                 // TODO: handle exception
73             }
74         }
75         
76         
77     }
78     
79 }

客戶端代碼:

 1 package com.heyang;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.IOException;
 5 import java.io.InputStream;
 6 import java.io.InputStreamReader;
 7 import java.io.OutputStream;
 8 import java.io.PrintWriter;
 9 import java.net.Socket;
10 import java.net.UnknownHostException;
11 
12 /*
13  * 客戶端
14  */
15 public class Client {
16     public static void main(String[] args) {
17         // 一、建立客戶端Socket,指定服務器地址和端口
18         try {
19             Socket  socket = new Socket("localhost", 8888);
20             // 二、獲取輸出流,向服務器端發送信息
21             OutputStream os = socket.getOutputStream();// 獲取字節輸出流
22             // 將輸出流包裝爲打印流
23             PrintWriter pw = new PrintWriter(os);
24             pw.write("用戶名:admin 密碼:123");
25             pw.flush();
26             socket.shutdownOutput();//關閉輸出流
27             
28             // 三、獲取輸入流,並讀取服務器端的響應信息
29             InputStream is = socket.getInputStream();
30             BufferedReader br = new BufferedReader(new InputStreamReader(is));
31             String info = null;
32             while((info = br.readLine())!=null){
33                 System.out.println("我是客戶端,服務器跟我說:"+info);
34             }
35             
36             // 四、關閉資源
37             br.close();
38             is.close();
39             pw.close();
40             os.close();
41             socket.close();
42             
43         } catch (UnknownHostException e) {
44             // TODO Auto-generated catch block
45             e.printStackTrace();
46         } catch (IOException e) {
47             // TODO Auto-generated catch block
48             e.printStackTrace();
49         }
50     }
51 }

運行結果不展現。


五、UDP編程

UDP協議(用戶數據報協議)是無鏈接、不可靠的、無序的。

特色:傳輸速度相對比較快

UDP協議以數據報做爲數據傳輸的載體

進行數據傳輸時,首先須要將要傳輸的數據定義成數據報(Datagram),在數據報中指明數據所要達到的Socket(主機地址和端口號),而後在將數據報發送出去。

 

相關操做的Java類

DatagramPacket:表示數據報包

DatagramSocket:進行端到端通訊的類

 

 

 

5-一、編程實現基於UDP的Socket通訊之服務器端

 

5-二、編程實現基於UDP的Socket通訊之客戶端

相關文章
相關標籤/搜索