網絡基礎知識html
一、計算機網絡java
計算機網絡的概念:把分佈在不一樣地理區域的計算機經過專門的通訊線路鏈接在一塊兒造成的規模龐大的、功能強大的網絡系統,一個很是著名的網絡萬維網( World Wide Web , 即www )。node
計算機網絡的做用:mysql
計算機網絡的分類:sql
星型網絡、總線型網絡、環線型網絡、樹形網絡、星型環線網絡數據庫
雙絞線網絡、同軸電纜網絡、光纖網、無線網、電力線網api
局域網( LAN )、城域網( MAN )、廣域網( WAN )數組
二、通訊網絡協議服務器
通訊網絡協議的概念:網絡通訊協議是一種網絡通用語言,爲鏈接不一樣操做系統和不一樣硬件體系結構的互聯網絡引提供通訊支持,是一種網絡通用語言。網絡
常見協議:
OSI是Open System Interconnect的縮寫,意爲開放式系統互聯。
國際標準組織(國際標準化組織)制定了OSI模型,這個模型把網絡通訊的工做分爲7層,分別是物理層、數據鏈路層、網絡層、傳輸層、會話層、表示層和應用層。
IP是英文Internet Protocol(網絡之間互連的協議)的縮寫中,文簡稱爲「網協」,是爲計算機網絡相互鏈接進行通訊而設計的協議。
TCP:Transmission Control Protocol 傳輸控制協議,是一種面向鏈接(鏈接導向)的、可靠的、基於字節流的運輸層(Transport layer)通訊協議,由IETF的RFC 793說明。 在簡化的計算機網絡OSI模型中,它完成第四層傳輸層所指定的功能。
UDP是同一層內另外一個重要的傳輸協議。
三、IP地址
IP 地址是根據IP 協議爲某個通訊實體分配的地址
IP地址是數字型的,是個32 位( 32bit ) 的整數
NIC 負責全球的IP 地址的規劃、管理
IP地址被分紅A、B、C、D、E 五類
一個特殊的IP 地址
查詢IP地址的命令:
IP v4 :
IP v6 :
四、端口
端口的做用:一個IP 地址能夠惟一地肯定一個通訊實體,一個通訊實體上能夠有多個程序提供服務好比一臺服務器上能夠有多個DBMS,如MySQL 、DB二、Oracle,爲了區別同一個通訊實體上的不一樣服務(程序),還要使用端口。
端口的概念:
端口的分類:
公認端口: 從0 到1023
他們緊密綁定一些特定服務,如80 端口、23端口、21端口等等
註冊端口: 從1024 到49151
鬆散地綁定一些服務,好比
» Oracle 數據庫默認的端口是1521
» MySQL 數據庫默認的端口是3306
» Tomcat 默認的端口是8080
動態或私有端口: 從49152 到65535
應用程序使用的動態端口,應用程序通常不會主動去使用這些端口
InetAddress
一、InetAddress 用來表明IP 地址
它有兩個子類
沒有構造,經過如下靜態方法獲取該類的實例
經常使用方法
InetAddress測試案例一:
package ecut.network; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; public class InetAddressTest1 { public static void main(String[] args) throws UnknownHostException { // 構造方法私有,經過靜態方法得到 當前主機的 一個 IP 地址 對應的 InetAddress 實例 InetAddress local = InetAddress.getLocalHost() ; System.out.println( local ); System.out.println( "主機名稱: " + local.getHostName() ); System.out.println( "主機地址: " + local.getHostAddress() ); byte[] address = local.getAddress(); // 以 byte 數組形式表示的 IP 地址 System.out.println( address.length ); System.out.println( Arrays.toString( address ) ); } }
InetAddress測試案例二:
package ecut.network; import java.net.InetAddress; import java.net.UnknownHostException; public class InetAddressTest2 { public static void main(String[] args) throws UnknownHostException { System.out.println( (byte)172 ); // byte[] address = { 1 , 0 , 0 , 100 }; byte[] address = { (byte)172 , 26 , 28 , 55 }; InetAddress remote = InetAddress.getByAddress( address ); System.out.println( remote.getHostAddress() ); } }
InetAddress測試案例三:
package ecut.network; import java.net.InetAddress; import java.net.UnknownHostException; public class InetAddressTest3 { public static void main(String[] args) throws UnknownHostException { InetAddress ia = InetAddress.getByName( "V-AIMEIZHENGX" ); System.out.println( ia.getHostAddress() ); System.out.println( "~~~~~~~~~~~~~~~~~~" ); InetAddress[] addresses = InetAddress.getAllByName( "V-AIMEIZHENGX" ); for( int i = 0 , n = addresses.length ; i < n ;i++){ InetAddress a = addresses[ i ] ; System.out.println( a.getHostAddress() ); } } }
InetSocketAddress
一、java.net.InetSocketAddress 類型的實例表示 ( IP + Port )
構造方法
InetSocketAddress 測試案例一:
package ecut.network; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; public class InetSocketAddressTest1 { public static void main(String[] args) throws UnknownHostException { InetAddress address = InetAddress.getByName( "1.0.0.2" );//傳ip地址和主機名均可以 System.out.println( address.getHostName() ); int port = 9527 ; InetSocketAddress isa = new InetSocketAddress( address , port ); System.out.println( isa ); System.out.println( isa.getHostName() ); System.out.println( isa.getAddress().getHostAddress() ); System.out.println( isa.getPort() ); } }
InetSocketAddress 測試案例二:
package ecut.network; import java.net.InetSocketAddress; import java.net.UnknownHostException; public class InetSocketAddressTest2 { public static void main(String[] args) throws UnknownHostException { String hostname = "1.0.0.2";//傳ip地址和主機名均可以 int port = 9527 ; InetSocketAddress isa = new InetSocketAddress( hostname , port ); System.out.println( isa ); System.out.println( isa.getHostName() ); System.out.println( isa.getAddress().getHostAddress() ); System.out.println( isa.getPort() ); } }
URL
一、URL 對象表明Uniform Resource Locator 統一資源定位符(協議://主機:端口/資源路徑和名稱)
它是指向互聯網"資源"的指針
URL 一般由協議名、主機名、端口和資源組成,格式以下:
二、URL 類的構造
該類有不少重載的構造
能夠根據已有的URL 建立全新的URL 對象
三、URL 類中經常使用方法
String getFile() 獲取此URL 的文件名。
String getHost() 獲取此URL 的主機名(若是適用)。
String getPath() 獲取此URL 的路徑部分。
int getPort() 獲取此URL 的端口號。
String getProtocol() 獲取此URL 的協議名稱。
String getQuery() 獲取此URL 的查詢部分。
URLConnection openConnection()返回一個URLConnection 對象,它表示到URL 所引用的遠程對象的鏈接。
InputStream openStream()打開到此URL 的鏈接並返回一個用於從該鏈接讀入的InputStream。
URL 測試案例:
package ecut.network; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; public class URLTest { public static void main(String[] args) throws IOException { // http://www.baidu.com:80/index.html //MalformedURLException 若是指定了未知協議。 URL u = new URL( "http", "www.baidu.com", 80, "/customer/account/change/password.do?date=20170506" ); System.out.println( u ); System.out.println( "協議: " + u.getProtocol() ); System.out.println( "主機: " + u.getHost() ); System.out.println( "端口: " + u.getPort() ); System.out.println( "File :" + u.getFile() ); System.out.println( "Path : " + u.getPath() ); System.out.println( "Query : " + u.getQuery() ); System.out.println( "~~~~~~~~~~~~~~~~~~~~~~~~~" ); InputStream in = u.openStream() ; System.out.println( in ); } }
運行結果以下:
http://www.baidu.com:80/customer/account/change/password.do?date=20170506 協議: http 主機: www.baidu.com 端口: 80 File :/customer/account/change/password.do?date=20170506 Path : /customer/account/change/password.do Query : date=20170506 ~~~~~~~~~~~~~~~~~~~~~~~~~ sun.net.www.protocol.http.HttpURLConnection$HttpInputStream@3e3abc88
TCP 協議
一、TCP 協議
使用IP 協議能夠將一個消息從一個通訊主體發送到另外一個通訊主體,消息在傳輸過程當中被分割成一個一個的小數據包。
雖然解決了數據發送和接受問題,可是不能解決數據分組在傳輸過程當中可能出現的問題。
爲了解決以上問題,須要提供一整套保障無差錯通訊的措施或協議,這就是目前使用普遍的TCP 協議。
TCP 協議被稱做端對端協議,經過該協議創建了兩個通訊實體之間用於發送和收取數據的虛擬鏈路。
二、TCP 協議的可靠性
TCP 協議負責收集被分割的數據分組,並將其按照適當的次序發送;對方在接受到數據分組後再將其正確還原。
爲了保證數據包傳送中準確無誤,TCP 協議使用重發機制。
ServerSocket
一、使用ServerSocket 建立TCP 服務器端
ServerSocket 類的對象用於監聽來自其它通訊實體的鏈接
得到ServerSocket 對象
Socket
一、得到Socket 進行通訊
ServerSocket 實例只負責監聽來自其它通訊實體的鏈接
使用Socket 類的實例才能實現通訊,該類中有兩個很是重要的方法
使用Socket 建立另一個通訊實體
服務端監聽一次測試案例:
package ecut.network; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; public class ServerV1 { public static void main(String[] args) throws Exception { // 建立一個 ServerSocket 對象,用來對外提供服務 ServerSocket server = new ServerSocket(); System.out.println( server ); System.out.println( server.isBound() ); SocketAddress endpoint = new InetSocketAddress( "10.10.12.72", 5555 ); server.bind( endpoint ); System.out.println( "server ip : " + server.getInetAddress().getHostAddress()); System.out.println( "server port : " +server.getLocalPort() ); // 監聽來自客戶端的鏈接 ( 會阻塞當前線程 ) Socket socketFromClient = server.accept(); System.out.println( "socket : " + socketFromClient ); System.out.println( "server ip ( local ) : " + socketFromClient.getLocalAddress().getHostAddress() ); System.out.println( "server port ( local ) : " + socketFromClient.getLocalPort() ); InetSocketAddress sa = (InetSocketAddress) socketFromClient.getRemoteSocketAddress(); System.out.println( "client ip : " + sa.getHostString() ); System.out.println( "client port : " + sa.getPort() ); server.close(); } }
package ecut.network; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; public class ClientV1 { public static void main(String[] args) throws IOException { // 建立一個 Socket 對象,充當客戶端程序 Socket client = new Socket(); SocketAddress bindpoint = new InetSocketAddress( "10.10.12.72", 3333 ); // 爲客戶端綁定本地的IP地址和端口 client.bind( bindpoint ); System.out.println( client ); System.out.println( client.getLocalAddress().getHostAddress() ); System.out.println( client.getLocalPort() ); SocketAddress remote = new InetSocketAddress( "10.10.12.72", 5555 ); // 鏈接 "遠程" 服務 client.connect( remote ); System.out.println( client ); } }
運行結果以下:
ServerSocket[unbound] false server ip : 10.10.12.72 server port : 5555 socket : Socket[addr=/10.10.12.72,port=3333,localport=5555] server ip ( local ) : 10.10.12.72 server port ( local ) : 5555 client ip : 10.10.12.72 client port : 3333
Socket[unconnected] 10.10.12.72 3333 Socket[addr=/10.10.12.72,port=5555,localport=3333]
服務端持續監聽測試案例:
package ecut.network; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; public class ServerV2 { @SuppressWarnings("resource") public static void main(String[] args) throws Exception { // 建立一個 ServerSocket 對象 ServerSocket server = new ServerSocket(); SocketAddress endpoint = new InetSocketAddress( "10.10.12.72", 6666 ); server.bind( endpoint ); while( true ) { // 監聽來自客戶端的鏈接 ( 會阻塞當前線程 ) Socket socketFromClient = server.accept(); InetSocketAddress sa = (InetSocketAddress) socketFromClient.getRemoteSocketAddress(); System.out.print( "client ip : " + sa.getHostString() ); System.out.println( " , client port : " + sa.getPort() ); } } }
package ecut.network; import java.io.IOException; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; public class ClientV2 { public static void main(String[] args) throws IOException { // 建立一個 Socket 對象,充當客戶端程序 Socket client = new Socket();//默認指定爲當地的IP地址,將一個空閒的端口號用於使用 SocketAddress remote = new InetSocketAddress( "10.10.12.72", 6666 ); // 鏈接 "遠程" 服務 client.connect( remote ); System.out.println( client ); } }
運行結果以下:
Socket[addr=/10.10.12.72,port=6666,localport=50688]
Socket[addr=/10.10.12.72,port=6666,localport=50699]
client ip : 10.10.12.72 , client port : 50688
client ip : 10.10.12.72 , client port : 50699
服務端持續監聽測試案例:
package ecut.network; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; public class ServerV3 { @SuppressWarnings("resource") public static void main(String[] args) throws IOException { // 建立一個 ServerSocket 對象 System.out.println("服務器啓動中..."); ServerSocket server = new ServerSocket(); SocketAddress endpoint = new InetSocketAddress("10.10.12.72", 6666); System.out.println("服務器正在初始化..."); server.bind(endpoint); System.out.println("服務器初始化完成,準備對外提供服務"); while (true) { System.out.println("服務器正在監聽來自客戶端的鏈接..."); // ( 會阻塞當前線程 ) Socket socket = server.accept(); try { // 得到 能夠從 當前監聽到的 客戶端鏈接 中 讀取數據的 字節輸入流 InputStream in = socket.getInputStream(); Reader reader = new InputStreamReader(in); BufferedReader br = new BufferedReader(reader); while (true) { String s = br.readLine(); System.out.println("來自客戶端的信息: [ " + s + " ]"); if ("byebyebye".equalsIgnoreCase(s)) { break; } } } catch (IOException e) { if (e instanceof SocketException) { SocketException se = (SocketException) e; String message = se.getMessage(); System.out.print("message"+message); if ("Connection reset".equalsIgnoreCase(message)) { System.out.println("客戶端斷開"); } else { se.printStackTrace(); } } else { e.printStackTrace(); } } } } }
package ecut.network; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.util.Scanner; public class ClientV3 { public static void main(String[] args) throws IOException { final Scanner scanner = new Scanner( System.in ); // 一、建立一個能夠鏈接遠程服務器的一個 Socket 對象 ( 充當客戶端 ) Socket client = new Socket(); SocketAddress remote = new InetSocketAddress( "10.10.12.72", 6666 ); // 二、鏈接 "遠程" 服務器上的指定程序 client.connect( remote ); // 三、得到能夠向服務器輸出數據的 字節輸出流 OutputStream out = client.getOutputStream(); PrintStream ps = new PrintStream( out ); while( true ){ System.out.println( "請輸入你要向服務器發送的信息:" ); String s = scanner.nextLine(); ps.println( s ); if( "byebyebye".equalsIgnoreCase( s ) ){ break ; } } // end client.close(); scanner.close(); } }
實現羣聊測試案例:
package ecut.network; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.Reader; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.util.HashMap; import java.util.Map; public class ServerV4 { private static final Map<String,Socket> SOCKETS = new HashMap<>() ; @SuppressWarnings("resource") public static void main(String[] args) throws IOException { // 建立一個 ServerSocket 對象 System.out.println( "服務器啓動中..." ); ServerSocket server = new ServerSocket(); SocketAddress endpoint = new InetSocketAddress( "192.168.0.106", 6666 ); System.out.println( "服務器正在初始化..." ); server.bind( endpoint ); System.out.println( "服務器初始化完成,準備對外提供服務" ); while( true ) { System.out.println( "服務器正在監聽來自客戶端的鏈接..."); // ( 會阻塞當前線程 ) Socket socket = server.accept(); InetSocketAddress remote = (InetSocketAddress) socket.getRemoteSocketAddress(); String name = remote.getHostString() + ":" + remote.getPort() ; // 將當前監聽到的 客戶端 name ( ip:port ) 跟 響應的 Socket 對象創建 "映射" SOCKETS.put( name , socket ) ; System.out.println( "建立爲[ " + name + " ]提供服務的線程" ); ServerThread t = new ServerThread( socket , name ) ; System.out.println( "啓動爲[ " + name + " ]提供服務的線程" ); t.start(); } } public static class ServerThread extends Thread { private String name ; private Socket socket ; public ServerThread(Socket socket , String name ) { super( name ); this.name = name ; if( socket == null ) { throw new RuntimeException( "Socket不能是null" ); } this.socket = socket; } @Override public void run() { try { // 得到 能夠從 當前監聽到的 客戶端鏈接 中 讀取數據的 字節輸入流 InputStream in = socket.getInputStream(); Reader reader = new InputStreamReader( in ) ; BufferedReader br = new BufferedReader( reader ); while( true ) { String message = br.readLine(); String msg = message ; message = "[ " + name +" ] 說 : [ " + message + " ]"; System.out.println( message ); for( Socket s :SOCKETS.values() ){ OutputStream out = s.getOutputStream(); PrintStream ps = new PrintStream( out ); ps.println( message ) ; } if( "byebyebye".equalsIgnoreCase( msg ) ){ SOCKETS.remove( name ); break ; } } } catch (IOException e) { if( e instanceof SocketException ){ SocketException se = (SocketException)e ; String message = se.getMessage() ; if( "Connection reset".equalsIgnoreCase( message ) ) { System.out.println( "客戶端斷開" ); // 將已經廢棄的 Socket 對象從 Map 集合中移除 SOCKETS.remove( name ); } else { se.printStackTrace(); } } else { e.printStackTrace(); } } } } }
package ecut.network; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.Reader; import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.net.SocketException; import java.util.Scanner; public class ClientV4 { public static void main(String[] args) throws IOException { final Scanner scanner = new Scanner( System.in ); // 一、建立一個能夠鏈接遠程服務器的一個 Socket 對象 ( 充當客戶端 ) Socket client = new Socket(); SocketAddress remote = new InetSocketAddress( "192.168.0.106", 6666 ); // 二、鏈接 "遠程" 服務器上的指定程序 client.connect( remote ); // 三、建立一個獨立的線程,專門用來讀取服務器發送的數據 ClientThread t = new ClientThread( client ); t.start(); // 四、得到能夠向服務器輸出數據的 字節輸出流 OutputStream out = client.getOutputStream(); PrintStream ps = new PrintStream( out ); while( true ){ System.out.println( "請輸入你要向服務器發送的信息:" ); String s = scanner.nextLine(); ps.println( s ); if( "byebyebye".equalsIgnoreCase( s ) ){ break ; } } // end client.close(); scanner.close(); } public static class ClientThread extends Thread { private Socket socket ; public ClientThread(Socket socket) { super(); this.setDaemon( true );//應該設置爲精靈線程 this.socket = socket; } @Override public void run() { try { // 得到 能夠從 當前監聽到的 客戶端鏈接 中 讀取數據的 字節輸入流 InputStream in = socket.getInputStream(); Reader reader = new InputStreamReader( in ) ; BufferedReader br = new BufferedReader( reader ); while( true ) { String s = br.readLine(); System.out.println( s ); } } catch (IOException e) { if( e instanceof SocketException ){ SocketException se = (SocketException)e ; String message = se.getMessage() ; if( "Connection reset".equalsIgnoreCase( message ) ) { System.out.println( "服務器已關閉" ); } else { se.printStackTrace(); } } else { e.printStackTrace(); } } } } }
ServerSocket / Socket
Socket 插板
服務器:被動地、對外提供某種服務或多種服務
ServerV4 監聽客戶端鏈接、將監聽到的客戶端加入集合、啓動服務線程
ClientV4 鏈接遠程服務器、啓動接受數據的線程、向服務器發送數據
運行結果以下:
服務器啓動中... 服務器正在初始化... 服務器初始化完成,準備對外提供服務 服務器正在監聽來自客戶端的鏈接... 建立爲[ 192.168.0.106:52916 ]提供服務的線程 啓動爲[ 192.168.0.106:52916 ]提供服務的線程 服務器正在監聽來自客戶端的鏈接... 建立爲[ 192.168.0.106:52917 ]提供服務的線程 啓動爲[ 192.168.0.106:52917 ]提供服務的線程 服務器正在監聽來自客戶端的鏈接... [ 192.168.0.106:52916 ] 說 : [ hello ] [ 192.168.0.106:52917 ] 說 : [ hi ] [ 192.168.0.106:52916 ] 說 : [ nice to meet you ] [ 192.168.0.106:52917 ] 說 : [ nice to meet you too ] [ 192.168.0.106:52916 ] 說 : [ i have someting to do ,so see you later ] [ 192.168.0.106:52917 ] 說 : [ ok, ] [ 192.168.0.106:52916 ] 說 : [ byebyebye ] [ 192.168.0.106:52917 ] 說 : [ byebyebye ]
請輸入你要向服務器發送的信息: hello 請輸入你要向服務器發送的信息: [ 192.168.0.106:52916 ] 說 : [ hello ] [ 192.168.0.106:52917 ] 說 : [ hi ] nice to meet you 請輸入你要向服務器發送的信息: [ 192.168.0.106:52916 ] 說 : [ nice to meet you ] [ 192.168.0.106:52917 ] 說 : [ nice to meet you too ] i have someting to do ,so see you later 請輸入你要向服務器發送的信息: [ 192.168.0.106:52916 ] 說 : [ i have someting to do ,so see you later ] [ 192.168.0.106:52917 ] 說 : [ ok, ] byebyebye
請輸入你要向服務器發送的信息: [ 192.168.0.106:52916 ] 說 : [ hello ] hi 請輸入你要向服務器發送的信息: [ 192.168.0.106:52917 ] 說 : [ hi ] [ 192.168.0.106:52916 ] 說 : [ nice to meet you ] nice to meet you too 請輸入你要向服務器發送的信息: [ 192.168.0.106:52917 ] 說 : [ nice to meet you too ] [ 192.168.0.106:52916 ] 說 : [ i have someting to do ,so see you later ] ok, 請輸入你要向服務器發送的信息: [ 192.168.0.106:52917 ] 說 : [ ok, ] [ 192.168.0.106:52916 ] 說 : [ byebyebye ] byebyebye
基於ServerSocket / Socket 的聊天室圖解:
UDP
一、UDP : 解決數據傳輸問題 ,非面向鏈接、不可靠的數據傳輸協議 ( 代價較小 ),突發性的對數據傳輸要求較低的。
二、數據報(Datagrama):
經過網絡傳輸的數據的基本單元
數據報工做方式的特色
三、IP數據報( IP Datagram )
TCP/IP 協議定義的在因特網上傳輸的包
四、用戶數據報( User Datagram )
基於UDP 在因特網上傳輸的數據包
來源端口和目的端口用來標記發送和接受的應用進程
» 因UDP不須要應答,故來源端口是可選的,若來源端口不用,那麼置爲零
在目的端口後面是長度固定的以字節爲單位的長度域
» 用來指定UDP數據報包括數據部分的長度,長度最小值爲8byte。
首部剩下的部分是用來對首部和數據部分一塊兒作校驗和(Checksum)的
» 這部分是可選的,但在實際應用中通常都使用這一功能
用戶數據報協議在網絡中的地位
數據報:報頭(header)+報文。
IP數據報:首部(作出了明確劃分和規定)+數據。
用戶數據報:首部+數據(首部+數據)。
五、TCP 與UDP 的對比
面向鏈接的TCP :可靠,傳輸大小無限制,可是須要鏈接創建時間,差錯控制開銷大。
面向非鏈接的UDP :不可靠,差錯控制開銷較小,傳輸大小限制在64K如下,不須要創建鏈接(虛擬數
據鏈路)。
六、Java 中的UDP 通訊技術核心api
DatagramPacket ( 數據報包)
DatagramSocket
bind(SocketAddress addr) 將此DatagramSocket綁定到特定的地址和端口。
close() 關閉此DatagramSocket。
InetAddress getLocalAddress() 獲取DatagramSocket 綁定的本地地址。
int getLocalPort() 返回此DatagramSocket 綁定的本地主機上的端口號。
void receive(DatagramPacket p) 今後DatagramSocket 接收數據報包。
void send(DatagramPacket p) 今後DatagramSocket 發送數據報包。
DatagramChannel : UDP 的NIO 支持
open() : 靜態方法,用於打開一個數據報通道。
receive(ByteBuffer dst) 經過此通道接收數據報。
send(ByteBuffer src, SocketAddress target) 經過此通道發送數據報。
三個重載的write() 用於將數據報寫入此通道。
socket() 獲取與此通道關聯的數據報套接字。
該類繼承了java.nio.channels.spi.AbstractSelectableChannel 和java.nio.channels.SelectableChannel ,這兩個類中的方法都可使用。
基於UDP協議的收發測試案例:
package ecut.network; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketAddress; public class Receiver1 { public static void main(String[] args) throws IOException { // 指定本地的 IP 地址和 端口 SocketAddress local = new InetSocketAddress( "192.168.0.106" , 5555 ) ; // 建立一個能夠基於UDP 協議進行 收、發 數據的 DatagramSocket 對象 DatagramSocket receiver = new DatagramSocket( local ); byte[] buffer = new byte[ 1024 ]; // 建立一個用來接受數據的數據報包 對象 DatagramPacket dp = new DatagramPacket( buffer , buffer.length ); // 接受數據 ( 接受時,依然是以數據報包形式接受 ) receiver.receive( dp ); byte[] data =dp.getData() ; // 從數據報包 中獲取接受到的數據 ( 長度可能超出實際數據的長度 ) System.out.println( buffer == data ); // true 說明 buffer 和 data 是同一個數組對象 int n = dp.getLength() ; // 接受到的字節數組的實際長度 System.out.println( new String( data , 0 , n ) ); receiver.close(); } }
package ecut.network; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketAddress; public class Sender1 { public static void main(String[] args) throws IOException { // 指定本地的 IP 地址和 端口 SocketAddress local = new InetSocketAddress( "192.168.0.106" , 2222 ) ; // 建立一個能夠基於UDP 協議進行 收、發 數據的 DatagramSocket 對象 DatagramSocket sender = new DatagramSocket( local ); // 指定遠程IP地址和端口 ( 就是 數據報包 的目的地址 ) SocketAddress remote = new InetSocketAddress( "192.168.0.106" , 5555 ); // 肯定要發送的數據 byte[] buffer = "今每天氣很差?好?".getBytes() ; // 構造數據報包 ( 並指定發送的數據 ) DatagramPacket dp = new DatagramPacket( buffer, buffer.length ); // 爲數據報包 指定目的地址 dp.setSocketAddress( remote ); // 發送數據報包 sender.send( dp ); sender.close(); } }
運行結果以下:
true 今每天氣很差?好?
基於UDP協議的屢次收發測試案例一:
package ecut.network; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketAddress; public class Receiver2 { public static void main(String[] args) throws IOException { // 指定本地的 IP 地址和 端口 SocketAddress local = new InetSocketAddress( "192.168.0.106" , 5555 ) ; // 建立一個能夠基於UDP 協議進行 收、發 數據的 DatagramSocket 對象 DatagramSocket receiver = new DatagramSocket( local ); final byte[] buffer = new byte[ 1024 ]; // 建立一個用來接受數據的數據報包 對象 DatagramPacket dp = new DatagramPacket( buffer , buffer.length ); while( true ) { // 接受數據 ( 接受時,依然是以數據報包形式接受 ) receiver.receive( dp ); //byte[] data =dp.getData() ; // 從數據報包 中獲取接受到的數據 ( 長度可能超出實際數據的長度 ) int n = dp.getLength() ; // 接受到的字節數組的實際長度 String s = new String( buffer , 0 , n ); System.out.println( s ); if( "byebyebye".equalsIgnoreCase( s ) ){ break ; } } receiver.close(); } }
package ecut.network; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Scanner; public class Sender2 { public static void main(String[] args) throws IOException { Scanner scan = new Scanner( System.in ); // 指定本地的 IP 地址和 端口 SocketAddress local = new InetSocketAddress( "192.168.0.106" , 2222 ) ; // 建立一個能夠基於UDP 協議進行 收、發 數據的 DatagramSocket 對象 DatagramSocket sender = new DatagramSocket( local ); // 指定遠程IP地址和端口 ( 就是 數據報包 的目的地址 ) SocketAddress remote = new InetSocketAddress( "192.168.0.106" , 5555 ); final byte[] buffer = { }; DatagramPacket dp = new DatagramPacket( buffer, buffer.length ); // 爲數據報包 指定目的地址 dp.setSocketAddress( remote ); String s ; System.out.println( "請輸入你要發送的數據:" ); while( ( s = scan.nextLine() ) != null ){ byte[] data = s.getBytes() ; // 每次發送以前都將要發送的數據設置到 數據報包 中 dp.setData( data ); // 發送數據報包 sender.send( dp ); if( "byebyebye".equalsIgnoreCase( s ) ){ break ; } System.out.println( "請輸入你要發送的數據:" ); } sender.close(); scan.close(); } }
運行結果以下:
world
hello
hi
hello
byebyebye
請輸入你要發送的數據:
world
請輸入你要發送的數據:
hello
請輸入你要發送的數據:
byebyebye
請輸入你要發送的數據:
hi
請輸入你要發送的數據:
hello
請輸入你要發送的數據:
byebyebye
基於UDP協議的屢次收發測試案例二:
package ecut.network; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketAddress; public class Receiver3 { public static void main(String[] args) throws IOException { // 指定本地的 IP 地址和 端口 SocketAddress local = new InetSocketAddress( "192.168.0.106" , 5555 ) ; // 建立一個能夠基於UDP 協議進行 收、發 數據的 DatagramSocket 對象 DatagramSocket receiver = new DatagramSocket( local ); final byte[] buffer = new byte[ 1024 ]; // 建立一個用來接受數據的數據報包 對象 DatagramPacket dp = new DatagramPacket( buffer , buffer.length ); while( true ) { // 接受數據 ( 接受時,依然是以數據報包形式接受 ) receiver.receive( dp ); InetSocketAddress remote = (InetSocketAddress)dp.getSocketAddress(); // 做爲接收者,得到發送者對應的 IP 地址 和 端口 String sender = remote.getHostString() + ":" + remote.getPort() ; int n = dp.getLength() ; // 接受到的字節數組的實際長度 String s = new String( buffer , 0 , n ); System.out.println( sender + "說: " + s ); if( "byebyebye".equalsIgnoreCase( s ) ){ break ; } } receiver.close(); } }
運行結果以下:
192.168.0.106:2222說: hello 192.168.0.106:6666說: hi 192.168.0.106:6666說: byebye 192.168.0.106:2222說: byebyebye
hello
請輸入你要發送的數據:
byebyebye
請輸入你要發送的數據:
hi
請輸入你要發送的數據:
byebye
請輸入你要發送的數據:
byebyebye
MulticastSocket
一、多播數據報套接字類用於發送和接收 IP 多播包
MulticastSocket 是一種(UDP) DatagramSocket。
它具備加入Internet 上其餘多播主機的「組」的附加功能。
多播組經過D 類IP 地址和標準UDP 端口號指定。
構造方法:
經常使用方法:
MulticastSocket測試案例:
package ecut.network; import java.io.IOException; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; import java.util.Scanner; public class MulticastSocketTest { public static void main(String[] args) throws IOException { byte[] addr = { (byte)224, 0, 0 , 1 }; // 建立 多播組 對應的 IP 地址 ( 224.0.0.0 ~ 239.255.255.255 ) final InetAddress group = InetAddress.getByAddress( addr ); final int port = 445 ; // 建立能夠實現多點廣播的 MulticastSocket 對象 MulticastSocket node = new MulticastSocket( port ); // 讓當前的 "節點" 加入指定的 多播組 node.joinGroup( group ); // 建立一個獨立的接受數據的線程 ReceiveThread t = new ReceiveThread( node ); t.start(); // 啓動線程 // 建立一個數據報包對象 ( 其中 "沒有" 要發送的數據 ) DatagramPacket dp = new DatagramPacket( new byte[0] , 0 ); // 指定數據報包的目的地址 dp.setAddress( group ); // 指定數據報包的目的端口 dp.setPort( port ); Scanner scanner = new Scanner( System.in ); while( true ){ System.out.println( "請輸入你要發送的信息: " ); String s = scanner.nextLine(); byte[] data = s.getBytes() ; // 將 字符串 根據當前默認平臺編碼編碼爲字節數組 ( String ---> byte[] ) dp.setData( data ); // 設置將要發送的數據 node.send( dp ); // 發送數據 if( "byebyebye".equalsIgnoreCase( s ) ){ break; } } scanner.close(); node.leaveGroup( group ); node.close(); } public static class ReceiveThread extends Thread { private MulticastSocket currentNode ; public ReceiveThread(MulticastSocket currentNode) { super(); this.currentNode = currentNode; this.setDaemon( true ); // 守護線程 } @Override public void run() { final byte[] buffer = new byte[ 1 << 16 ] ; DatagramPacket dp = new DatagramPacket( buffer , buffer.length ); while( true ) { try { System.out.println( "接受數據" ); currentNode.receive( dp ); String sender = dp.getAddress().getHostAddress() + ":" + dp.getPort() ; int len = dp.getLength(); String message = new String( buffer , 0 , len ) ; System.out.println( "[" + sender +"] 說 :[ " + message + " ]" ); } catch (IOException e) { System.err.println( e.getMessage() ); } } } } }
DatagramSocket僅僅贊成數據報發送給指定的目標地址,而MulticastSocket可以將數據報以廣播的方式發送到多個client。
若要使用多點廣播,則需要讓一個數據報標有一組目標主機地址,當數據報發出後,整個組的所有主機都能收到該數據報。IP多點廣播(或多點發送)實現了將單一信息發送到多個接受者的廣播,其思想是設置一組特殊網絡地址做爲多點廣播地址,每一個多點廣播地址都被看作一個組,當client需要發送、接收廣播信息時,增長到改組就能夠。
MulticastSocket既可以將數據報發送到多點廣播地址,也可以接收其它主機的廣播信息。
待解決問題
MulticastSocket
轉載請於明顯處標明出處