Java 网络
網(wǎng)絡(luò)基礎(chǔ)知識
1、計算機(jī)網(wǎng)絡(luò)
計算機(jī)網(wǎng)絡(luò)的概念:把分布在不同地理區(qū)域的計算機(jī)通過專門的通信線路連接在一起形成的規(guī)模龐大的、功能強(qiáng)大的網(wǎng)絡(luò)系統(tǒng),一個非常著名的網(wǎng)絡(luò)萬維網(wǎng)( World Wide Web , 即www )。
計算機(jī)網(wǎng)絡(luò)的作用:
- 資源共享
- 信息傳輸和集中處理
- 均衡負(fù)荷與分布處理
- 綜合信息服務(wù)
計算機(jī)網(wǎng)絡(luò)的分類:
- 按照網(wǎng)絡(luò)結(jié)構(gòu)劃分
星型網(wǎng)絡(luò)、總線型網(wǎng)絡(luò)、環(huán)線型網(wǎng)絡(luò)、樹形網(wǎng)絡(luò)、星型環(huán)線網(wǎng)絡(luò)
- 按照介質(zhì)來分
雙絞線網(wǎng)絡(luò)、同軸電纜網(wǎng)絡(luò)、光纖網(wǎng)、無線網(wǎng)、電力線網(wǎng)
- 按照規(guī)模來劃分
局域網(wǎng)( LAN )、城域網(wǎng)( MAN )、廣域網(wǎng)( WAN )
2、通信網(wǎng)絡(luò)協(xié)議
通信網(wǎng)絡(luò)協(xié)議的概念:網(wǎng)絡(luò)通信協(xié)議是一種網(wǎng)絡(luò)通用語言,為連接不同操作系統(tǒng)和不同硬件體系結(jié)構(gòu)的互聯(lián)網(wǎng)絡(luò)引提供通信支持,是一種網(wǎng)絡(luò)通用語言。
常見協(xié)議:
- OSI
OSI是Open System Interconnect的縮寫,意為開放式系統(tǒng)互聯(lián)。
國際標(biāo)準(zhǔn)組織(國際標(biāo)準(zhǔn)化組織)制定了OSI模型,這個模型把網(wǎng)絡(luò)通信的工作分為7層,分別是物理層、數(shù)據(jù)鏈路層、網(wǎng)絡(luò)層、傳輸層、會話層、表示層和應(yīng)用層。
- TCP/IP
IP是英文Internet Protocol(網(wǎng)絡(luò)之間互連的協(xié)議)的縮寫中,文簡稱為“網(wǎng)協(xié)”,是為計算機(jī)網(wǎng)絡(luò)相互連接進(jìn)行通信而設(shè)計的協(xié)議。
TCP:Transmission Control Protocol 傳輸控制協(xié)議,是一種面向連接(連接導(dǎo)向)的、可靠的、基于字節(jié)流的運(yùn)輸層(Transport layer)通信協(xié)議,由IETF的RFC 793說明。 ? 在簡化的計算機(jī)網(wǎng)絡(luò)OSI模型中,它完成第四層傳輸層所指定的功能。
UDP是同一層內(nèi)另一個重要的傳輸協(xié)議。
3、IP地址
IP 地址是根據(jù)IP 協(xié)議為某個通信實(shí)體分配的地址
- 這個通信實(shí)體,有可能是計算機(jī)、打印機(jī)、路由器的某個端口。
IP地址是數(shù)字型的,是個32 位( 32bit ) 的整數(shù)
- 通常為了方便記憶,把這32 位分成4 個部分,每個部分存放8 位。
- 因此我們實(shí)際上看到的ip 地址形式為:192.168.95.27。
NIC 負(fù)責(zé)全球的IP 地址的規(guī)劃、管理
- NIC : Internet Network Information Center。
- NIC 下屬Inter NIC 、APNIC、PIPE 機(jī)構(gòu)負(fù)責(zé)美國及其他地區(qū)的IP 地址分配。
- APNIC 總部在日本東京大學(xué),負(fù)責(zé)亞太地區(qū)的IP地址分配。
IP地址被分成A、B、C、D、E 五類
- A類: 10.0.0.0 ~ 10.255.255.255
- B類: 172.16.0.0 ~ 172.31.255.255
- C類: 192.168.0.0 ~ 192.168.255.255
一個特殊的IP 地址
- 127.0.0.1 : 代表當(dāng)前計算機(jī)本身。
查詢IP地址的命令:
- ?Windows :? ipconfig? /all
- ?Unix / Linux / Mac OS :? ifconfig? -a
?IP v4 :
- 采用 32 bit 的數(shù)字表示一個地址??? ------->? java.net.Inet4Address
- 通??吹降?192.168.100.100 形式是將 32bit 拆分成 4 各部分,每個部分 8bit
- 不同的部分之間 用 圓點(diǎn) 進(jìn)行分隔
IP v6 :
- 采用 128bit 的數(shù)字表示一個地址??? ------->? java.net.Inet6Address
- 通??吹降?FEC0:0:0:ffff::1
- 每個部分由 4 位 十六進(jìn)制 表示的 整數(shù) 組成,比如 FEC0 , 每部分占 16 bit
- 不同的部分之間 用 冒號 進(jìn)行分隔 ,( 分成 8 個部分 )
- ?如果 某個部分 的 4 個十六進(jìn)制數(shù)字都是 0 ,可以只寫一個 ,比如? 0000 可以寫成 0
- ?FEC0:0:0:ffff::1 ------>? FEC0:0000:0000:FFFF:0000:0000:0000:0001
4、端口
端口的作用:一個IP 地址可以唯一地確定一個通信實(shí)體,一個通信實(shí)體上可以有多個程序提供服務(wù)比如一臺服務(wù)器上可以有多個DBMS,如MySQL 、DB2、Oracle,為了區(qū)別同一個通信實(shí)體上的不同服務(wù)(程序),還要使用端口。
端口的概念:
- 端口是一個16 bit的整數(shù),用于表示數(shù)據(jù)交給哪個通信程序處理。
- 端口是應(yīng)用程序與外界交流的出入口,是種抽象的軟件結(jié)構(gòu)。
端口的分類:
- 不同的應(yīng)用程序處理不同端口上的數(shù)據(jù)同一個計算機(jī)上,不允許有兩個以上程序使用同一個端口。
- 我們自己在使用端口時,盡量使用1024 以上的端口,同時還要注意避開已經(jīng)被已有的服務(wù)占用的端口。
- 端口的范圍從0 到65535 ,被分成三類
公認(rèn)端口: 從0 到1023
他們緊密綁定一些特定服務(wù),如80 端口、23端口、21端口等等
注冊端口: 從1024 到49151
松散地綁定一些服務(wù),比如
? Oracle 數(shù)據(jù)庫默認(rèn)的端口是1521
? MySQL 數(shù)據(jù)庫默認(rèn)的端口是3306
? Tomcat 默認(rèn)的端口是8080
動態(tài)或私有端口: 從49152 到65535
應(yīng)用程序使用的動態(tài)端口,應(yīng)用程序一般不會主動去使用這些端口
InetAddress
?1、InetAddress 用來代表IP 地址
它有兩個子類
- Inet4Address : 對應(yīng)IPv4 地址
- Inet6Address : 對應(yīng)IPv6 地址
沒有構(gòu)造,通過以下靜態(tài)方法獲取該類的實(shí)例
- getByName( String hostName ) :根據(jù)指定主機(jī)名稱得到對應(yīng)的InetAddress 實(shí)例
- getByAddress( byte[] address ) :根據(jù)指定的IP 地址獲取對應(yīng)的InetAddress 實(shí)例
常用方法
- String getCanonicalHostName()獲取此IP 地址的完全限定域名
- String getHostAddress()返回IP 地址字符串(以文本表現(xiàn)形式)
- String getHostName()獲取此IP 地址的主機(jī)名
- static InetAddress getLocalHost()返回本地主機(jī)對應(yīng)的InetAddress 實(shí)例
- boolean isReachable(int timeout)測試是否可以達(dá)到該地址
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 { // 構(gòu)造方法私有,通過靜態(tài)方法獲得 當(dāng)前主機(jī)的 一個 IP 地址 對應(yīng)的 InetAddress 實(shí)例 InetAddress local = InetAddress.getLocalHost() ; System.out.println( local ); System.out.println( "主機(jī)名稱: " + local.getHostName() ); System.out.println( "主機(jī)地址: " + local.getHostAddress() ); byte[] address = local.getAddress(); // 以 byte 數(shù)組形式表示的 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
1、java.net.InetSocketAddress 類型的實(shí)例表示 ( IP + Port )
構(gòu)造方法
- ?InetSocketAddress(InetAddress addr, int port)根據(jù) IP 地址和端口號創(chuàng)建套接字地址。
- InetSocketAddress(String hostname, int port)根據(jù)主機(jī)名和端口號創(chuàng)建套接字地址。
?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地址和主機(jī)名都可以 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地址和主機(jī)名都可以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
1、URL 對象代表Uniform Resource Locator 統(tǒng)一資源定位符(協(xié)議://主機(jī):端口/資源路徑和名稱)
它是指向互聯(lián)網(wǎng)"資源"的指針
- 這里的資源可以是簡單的文件或目錄。
- 也可以是更為復(fù)雜的對象(比如數(shù)據(jù)庫對象)。
URL 通常由協(xié)議名、主機(jī)名、端口和資源組成,格式如下:
- protocol : // host : port / resourceName。
- 常見的URL 如:http://www.baidu.com:80/index.html。
- ftp://ftp.baidu.com:xx/金剛.mkv 、 jdbc:mysql://localhost:3306/ecut?useUnicode=true&characterEncoding=utf8、jdbc:oracle:thin:@localhost:1521:ecut
2、URL 類的構(gòu)造
該類有很多重載的構(gòu)造
- 但不外乎就是指定協(xié)議名、主機(jī)名、端口、資源名等參數(shù)
可以根據(jù)已有的URL 創(chuàng)建全新的URL 對象
- URL(URL context, String spec)
- URL(URL context, String spec, URLStreamHandler handler)
3、URL 類中常用方法
String getFile() 獲取此URL 的文件名。
String getHost() 獲取此URL 的主機(jī)名(如果適用)。
String getPath() 獲取此URL 的路徑部分。
int getPort() 獲取此URL 的端口號。
String getProtocol() 獲取此URL 的協(xié)議名稱。
String getQuery() 獲取此URL 的查詢部分。
URLConnection openConnection()返回一個URLConnection 對象,它表示到URL 所引用的遠(yuǎn)程對象的連接。
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 如果指定了未知協(xié)議。URL u = new URL( "http", "www.baidu.com", 80, "/customer/account/change/password.do?date=20170506" );System.out.println( u );System.out.println( "協(xié)議: " + u.getProtocol() );System.out.println( "主機(jī): " + 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 );}}運(yùn)行結(jié)果如下:
http://www.baidu.com:80/customer/account/change/password.do?date=20170506 協(xié)議: http 主機(jī): 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@3e3abc88TCP 協(xié)議
1、TCP 協(xié)議
使用IP 協(xié)議可以將一個消息從一個通信主體發(fā)送到另一個通信主體,消息在傳輸過程中被分割成一個一個的小數(shù)據(jù)包。
雖然解決了數(shù)據(jù)發(fā)送和接受問題,但是不能解決數(shù)據(jù)分組在傳輸過程中可能出現(xiàn)的問題。
為了解決以上問題,需要提供一整套保障無差錯通信的措施或協(xié)議,這就是目前使用廣泛的TCP 協(xié)議。
TCP 協(xié)議被稱作端對端協(xié)議,通過該協(xié)議建立了兩個通信實(shí)體之間用于發(fā)送和收取數(shù)據(jù)的虛擬鏈路。
2、TCP 協(xié)議的可靠性
TCP 協(xié)議負(fù)責(zé)收集被分割的數(shù)據(jù)分組,并將其按照適當(dāng)?shù)拇涡虬l(fā)送;對方在接受到數(shù)據(jù)分組后再將其正確還原。
為了保證數(shù)據(jù)包傳送中準(zhǔn)確無誤,TCP 協(xié)議使用重發(fā)機(jī)制。
- 通信實(shí)體A發(fā)送一個消息給通信實(shí)體B后,會等待通信實(shí)體B返回的確認(rèn)信息,如果A沒有收到B的確認(rèn)信息,則A會再次發(fā)送該消息
- 這種重發(fā)機(jī)制,保證了數(shù)據(jù)傳輸?shù)目煽啃院屯暾浴?/li>
ServerSocket
1、使用ServerSocket 創(chuàng)建TCP 服務(wù)器端
ServerSocket 類的對象用于監(jiān)聽來自其它通信實(shí)體的連接
- 該類的accept() 方法用于監(jiān)聽來自外部的連接。
- 如果沒有任何通信實(shí)體連接該ServerSocket 對象,它將永遠(yuǎn)等待下去。
獲得ServerSocket 對象
- 該類中提供了許多重載的構(gòu)造方法,可以用于實(shí)例化ServerSocket。
- 一般而言需要指定IP 和port。
Socket
1、獲得Socket 進(jìn)行通信
ServerSocket 實(shí)例只負(fù)責(zé)監(jiān)聽來自其它通信實(shí)體的連接
- 如果accept() 方法監(jiān)聽到來自外部的通信實(shí)體的連接,它將返回一個Socket。
- Socket accept() : 返回監(jiān)聽到的連接對應(yīng)的Socket 的實(shí)例。
使用Socket 類的實(shí)例才能實(shí)現(xiàn)通信,該類中有兩個非常重要的方法
- InputStream getInputStream()返回當(dāng)前的Socket 對應(yīng)的輸入流,讓程序通過該流從Socket 中讀取數(shù)據(jù)。
- OutputStream getOutputStream()返回當(dāng)前的Socket 對應(yīng)的輸出流,讓程序通過該流向Socket 中寫入數(shù)據(jù)。
使用Socket 創(chuàng)建另外一個通信實(shí)體
- 構(gòu)造一個Socket 實(shí)例,讓它去連接前面建好的ServerSocket
- Socket 類構(gòu)造有多種重載形式,但一般需要指定連接哪個主機(jī)或哪個ip,另外還要指定端口號,比如:Socket s = new Socket("127.0.0.1" , 8888 );
服務(wù)端監(jiān)聽一次測試案例:
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 {// 創(chuàng)建一個 ServerSocket 對象,用來對外提供服務(wù)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() );// 監(jiān)聽來自客戶端的連接 ( 會阻塞當(dāng)前線程 )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 {// 創(chuàng)建一個 Socket 對象,充當(dāng)客戶端程序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 );// 連接 "遠(yuǎn)程" 服務(wù) client.connect( remote );System.out.println( client );}}運(yùn)行結(jié)果如下:
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]服務(wù)端持續(xù)監(jiān)聽測試案例:
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 {// 創(chuàng)建一個 ServerSocket 對象ServerSocket server = new ServerSocket();SocketAddress endpoint = new InetSocketAddress( "10.10.12.72", 6666 );server.bind( endpoint );while( true ) {// 監(jiān)聽來自客戶端的連接 ( 會阻塞當(dāng)前線程 )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 {// 創(chuàng)建一個 Socket 對象,充當(dāng)客戶端程序Socket client = new Socket();//默認(rèn)指定為當(dāng)?shù)氐腎P地址,將一個空閑的端口號用于使用 SocketAddress remote = new InetSocketAddress( "10.10.12.72", 6666 );// 連接 "遠(yuǎn)程" 服務(wù) client.connect( remote );System.out.println( client );}}運(yùn)行結(jié)果如下:
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?服務(wù)端持續(xù)監(jiān)聽測試案例:
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 {// 創(chuàng)建一個 ServerSocket 對象System.out.println("服務(wù)器啟動中...");ServerSocket server = new ServerSocket();SocketAddress endpoint = new InetSocketAddress("10.10.12.72", 6666);System.out.println("服務(wù)器正在初始化...");server.bind(endpoint);System.out.println("服務(wù)器初始化完成,準(zhǔn)備對外提供服務(wù)");while (true) {System.out.println("服務(wù)器正在監(jiān)聽來自客戶端的連接..."); // ( 會阻塞當(dāng)前線程 )Socket socket = server.accept();try {// 獲得 可以從 當(dāng)前監(jiān)聽到的 客戶端連接 中 讀取數(shù)據(jù)的 字節(jié)輸入流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 );// 1、創(chuàng)建一個可以連接遠(yuǎn)程服務(wù)器的一個 Socket 對象 ( 充當(dāng)客戶端 )Socket client = new Socket();SocketAddress remote = new InetSocketAddress( "10.10.12.72", 6666 );// 2、連接 "遠(yuǎn)程" 服務(wù)器上的指定程序 client.connect( remote );// 3、獲得可以向服務(wù)器輸出數(shù)據(jù)的 字節(jié)輸出流OutputStream out = client.getOutputStream();PrintStream ps = new PrintStream( out );while( true ){System.out.println( "請輸入你要向服務(wù)器發(fā)送的信息:" );String s = scanner.nextLine();ps.println( s );if( "byebyebye".equalsIgnoreCase( s ) ){break ;}}// end client.close();scanner.close();}}?實(shí)現(xiàn)群聊測試案例:
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 {// 創(chuàng)建一個 ServerSocket 對象System.out.println( "服務(wù)器啟動中..." );ServerSocket server = new ServerSocket();SocketAddress endpoint = new InetSocketAddress( "192.168.0.106", 6666 );System.out.println( "服務(wù)器正在初始化..." );server.bind( endpoint );System.out.println( "服務(wù)器初始化完成,準(zhǔn)備對外提供服務(wù)" );while( true ) {System.out.println( "服務(wù)器正在監(jiān)聽來自客戶端的連接..."); // ( 會阻塞當(dāng)前線程 )Socket socket = server.accept();InetSocketAddress remote = (InetSocketAddress) socket.getRemoteSocketAddress();String name = remote.getHostString() + ":" + remote.getPort() ;// 將當(dāng)前監(jiān)聽到的 客戶端 name ( ip:port ) 跟 響應(yīng)的 Socket 對象建立 "映射" SOCKETS.put( name , socket ) ;System.out.println( "創(chuàng)建為[ " + name + " ]提供服務(wù)的線程" );ServerThread t = new ServerThread( socket , name ) ;System.out.println( "啟動為[ " + name + " ]提供服務(wù)的線程" );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;}@Overridepublic void run() {try {// 獲得 可以從 當(dāng)前監(jiān)聽到的 客戶端連接 中 讀取數(shù)據(jù)的 字節(jié)輸入流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( "客戶端斷開" );// 將已經(jīng)廢棄的 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 );// 1、創(chuàng)建一個可以連接遠(yuǎn)程服務(wù)器的一個 Socket 對象 ( 充當(dāng)客戶端 )Socket client = new Socket();SocketAddress remote = new InetSocketAddress( "192.168.0.106", 6666 );// 2、連接 "遠(yuǎn)程" 服務(wù)器上的指定程序 client.connect( remote );// 3、創(chuàng)建一個獨(dú)立的線程,專門用來讀取服務(wù)器發(fā)送的數(shù)據(jù)ClientThread t = new ClientThread( client );t.start();// 4、獲得可以向服務(wù)器輸出數(shù)據(jù)的 字節(jié)輸出流OutputStream out = client.getOutputStream();PrintStream ps = new PrintStream( out );while( true ){System.out.println( "請輸入你要向服務(wù)器發(fā)送的信息:" );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 );//應(yīng)該設(shè)置為精靈線程this.socket = socket;}@Overridepublic void run() {try {// 獲得 可以從 當(dāng)前監(jiān)聽到的 客戶端連接 中 讀取數(shù)據(jù)的 字節(jié)輸入流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( "服務(wù)器已關(guān)閉" );} else {se.printStackTrace();}} else {e.printStackTrace();}}}}}ServerSocket / Socket
?? ??? ?Socket 插板
?? ??? ?服務(wù)器:被動地、對外提供某種服務(wù)或多種服務(wù)
?? ??? ?
?? ??? ?ServerV4 監(jiān)聽客戶端連接、將監(jiān)聽到的客戶端加入集合、啟動服務(wù)線程
?? ??? ?
?? ??? ?ClientV4 連接遠(yuǎn)程服務(wù)器、啟動接受數(shù)據(jù)的線程、向服務(wù)器發(fā)送數(shù)據(jù)
運(yùn)行結(jié)果如下:
服務(wù)器啟動中... 服務(wù)器正在初始化... 服務(wù)器初始化完成,準(zhǔn)備對外提供服務(wù) 服務(wù)器正在監(jiān)聽來自客戶端的連接... 創(chuàng)建為[ 192.168.0.106:52916 ]提供服務(wù)的線程 啟動為[ 192.168.0.106:52916 ]提供服務(wù)的線程 服務(wù)器正在監(jiān)聽來自客戶端的連接... 創(chuàng)建為[ 192.168.0.106:52917 ]提供服務(wù)的線程 啟動為[ 192.168.0.106:52917 ]提供服務(wù)的線程 服務(wù)器正在監(jiān)聽來自客戶端的連接... [ 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 ] 請輸入你要向服務(wù)器發(fā)送的信息: hello 請輸入你要向服務(wù)器發(fā)送的信息: [ 192.168.0.106:52916 ] 說 : [ hello ] [ 192.168.0.106:52917 ] 說 : [ hi ] nice to meet you 請輸入你要向服務(wù)器發(fā)送的信息: [ 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 請輸入你要向服務(wù)器發(fā)送的信息: [ 192.168.0.106:52916 ] 說 : [ i have someting to do ,so see you later ] [ 192.168.0.106:52917 ] 說 : [ ok, ] byebyebye 請輸入你要向服務(wù)器發(fā)送的信息: [ 192.168.0.106:52916 ] 說 : [ hello ] hi 請輸入你要向服務(wù)器發(fā)送的信息: [ 192.168.0.106:52917 ] 說 : [ hi ] [ 192.168.0.106:52916 ] 說 : [ nice to meet you ] nice to meet you too 請輸入你要向服務(wù)器發(fā)送的信息: [ 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, 請輸入你要向服務(wù)器發(fā)送的信息: [ 192.168.0.106:52917 ] 說 : [ ok, ] [ 192.168.0.106:52916 ] 說 : [ byebyebye ] byebyebye?基于ServerSocket / Socket 的聊天室圖解:
?UDP
1、UDP : 解決數(shù)據(jù)傳輸問題 ,非面向連接、不可靠的數(shù)據(jù)傳輸協(xié)議? ( 代價較小 ),突發(fā)性的對數(shù)據(jù)傳輸要求較低的。
2、數(shù)據(jù)報(Datagrama):
通過網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)的基本單元
- 包含一個報頭(header)和數(shù)據(jù)本身。
- 其中報頭描述了數(shù)據(jù)的目的地以及和其它數(shù)據(jù)之間的關(guān)系。
數(shù)據(jù)報工作方式的特點(diǎn)
- 同一報文的不同分組可以由不同的傳輸路徑通過通信子網(wǎng)。
- 同一報文的不同分組到達(dá)目的結(jié)點(diǎn)時可能出現(xiàn)亂序、重復(fù)與丟失現(xiàn)象。
- 每一個分組在傳輸過程中都必須帶有目的地址與源地址。
- 數(shù)據(jù)報方式報文傳輸延遲較大,適用于突發(fā)性通信,不適用于長報文、會話式通信。
3、IP數(shù)據(jù)報( IP Datagram )
TCP/IP 協(xié)議定義的在因特網(wǎng)上傳輸?shù)陌?/p>
- 這是一個與硬件無關(guān)的虛擬包, 由首部和數(shù)據(jù)兩部分組成。
- 首部的前一部分是固定長度,共20字節(jié),是所有IP數(shù)據(jù)報必須具有的。
- 在首部的固定部分的后面是一些可選字段,其長度是可變的。
- 首部中的源地址和目的地址都是IP協(xié)議地址。
4、用戶數(shù)據(jù)報( User Datagram )
基于UDP 在因特網(wǎng)上傳輸?shù)臄?shù)據(jù)包
- UDP在IP數(shù)據(jù)報的頭部僅僅加入了復(fù)用和數(shù)據(jù)校驗(yàn)。
- UDP首部字段由4個部分組成,其中兩個是可選的。
來源端口和目的端口用來標(biāo)記發(fā)送和接受的應(yīng)用進(jìn)程
? 因UDP不需要應(yīng)答,故來源端口是可選的,若來源端口不用,那么置為零
在目的端口后面是長度固定的以字節(jié)為單位的長度域
? 用來指定UDP數(shù)據(jù)報包括數(shù)據(jù)部分的長度,長度最小值為8byte。
首部剩下的部分是用來對首部和數(shù)據(jù)部分一起做校驗(yàn)和(Checksum)的
? 這部分是可選的,但在實(shí)際應(yīng)用中一般都使用這一功能
用戶數(shù)據(jù)報協(xié)議在網(wǎng)絡(luò)中的地位
數(shù)據(jù)報:報頭(header)+報文。
IP數(shù)據(jù)報:首部(做出了明確劃分和規(guī)定)+數(shù)據(jù)。
用戶數(shù)據(jù)報:首部+數(shù)據(jù)(首部+數(shù)據(jù))。
5、TCP 與UDP 的對比
面向連接的TCP :可靠,傳輸大小無限制,但是需要連接建立時間,差錯控制開銷大。
面向非連接的UDP :不可靠,差錯控制開銷較小,傳輸大小限制在64K以下,不需要建立連接(虛擬數(shù)
據(jù)鏈路)。
6、Java 中的UDP 通信技術(shù)核心api
DatagramPacket ( 數(shù)據(jù)報包)
- 該類的構(gòu)造都需要接受一個字節(jié)數(shù)組做參數(shù): byte[] buff
- 該字節(jié)數(shù)組即為需要傳送的數(shù)據(jù)
- 該類中有很多重載的構(gòu)造,可以根據(jù)實(shí)際情況來調(diào)用
DatagramSocket
- 重載的構(gòu)造比較多,根據(jù)實(shí)際情況來調(diào)用。
- 常用方法:
bind(SocketAddress addr) 將此DatagramSocket綁定到特定的地址和端口。
close() 關(guān)閉此DatagramSocket。
InetAddress getLocalAddress() 獲取DatagramSocket 綁定的本地地址。
int getLocalPort() 返回此DatagramSocket 綁定的本地主機(jī)上的端口號。
void receive(DatagramPacket p) 從此DatagramSocket 接收數(shù)據(jù)報包。
void send(DatagramPacket p) 從此DatagramSocket 發(fā)送數(shù)據(jù)報包。
DatagramChannel : UDP 的NIO 支持
- 該類是抽象類,因此構(gòu)造不能被用來創(chuàng)建對象。
- 常用方法:
open() : 靜態(tài)方法,用于打開一個數(shù)據(jù)報通道。
receive(ByteBuffer dst) 通過此通道接收數(shù)據(jù)報。
send(ByteBuffer src, SocketAddress target) 通過此通道發(fā)送數(shù)據(jù)報。
三個重載的write() 用于將數(shù)據(jù)報寫入此通道。
socket() 獲取與此通道關(guān)聯(lián)的數(shù)據(jù)報套接字。
- 繼承的方法:
該類繼承了java.nio.channels.spi.AbstractSelectableChannel 和java.nio.channels.SelectableChannel ,這兩個類中的方法均可使用。
- DatagramChannel 也可以注冊于Selector 實(shí)例。
基于UDP協(xié)議的收發(fā)測試案例:
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 ) ;// 創(chuàng)建一個可以基于UDP 協(xié)議進(jìn)行 收、發(fā) 數(shù)據(jù)的 DatagramSocket 對象DatagramSocket receiver = new DatagramSocket( local );byte[] buffer = new byte[ 1024 ];// 創(chuàng)建一個用來接受數(shù)據(jù)的數(shù)據(jù)報包 對象DatagramPacket dp = new DatagramPacket( buffer , buffer.length );// 接受數(shù)據(jù) ( 接受時,依然是以數(shù)據(jù)報包形式接受 ) receiver.receive( dp );byte[] data =dp.getData() ; // 從數(shù)據(jù)報包 中獲取接受到的數(shù)據(jù) ( 長度可能超出實(shí)際數(shù)據(jù)的長度 ) System.out.println( buffer == data ); // true 說明 buffer 和 data 是同一個數(shù)組對象int n = dp.getLength() ; // 接受到的字節(jié)數(shù)組的實(shí)際長度 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 ) ;// 創(chuàng)建一個可以基于UDP 協(xié)議進(jìn)行 收、發(fā) 數(shù)據(jù)的 DatagramSocket 對象DatagramSocket sender = new DatagramSocket( local );// 指定遠(yuǎn)程IP地址和端口 ( 就是 數(shù)據(jù)報包 的目的地址 )SocketAddress remote = new InetSocketAddress( "192.168.0.106" , 5555 );// 確定要發(fā)送的數(shù)據(jù)byte[] buffer = "今天天氣不好?好?".getBytes() ;// 構(gòu)造數(shù)據(jù)報包 ( 并指定發(fā)送的數(shù)據(jù) )DatagramPacket dp = new DatagramPacket( buffer, buffer.length );// 為數(shù)據(jù)報包 指定目的地址 dp.setSocketAddress( remote );// 發(fā)送數(shù)據(jù)報包 sender.send( dp );sender.close();}}運(yùn)行結(jié)果如下:
true 今天天氣不好?好?基于UDP協(xié)議的多次收發(fā)測試案例一:
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 ) ;// 創(chuàng)建一個可以基于UDP 協(xié)議進(jìn)行 收、發(fā) 數(shù)據(jù)的 DatagramSocket 對象DatagramSocket receiver = new DatagramSocket( local );final byte[] buffer = new byte[ 1024 ];// 創(chuàng)建一個用來接受數(shù)據(jù)的數(shù)據(jù)報包 對象DatagramPacket dp = new DatagramPacket( buffer , buffer.length );while( true ) {// 接受數(shù)據(jù) ( 接受時,依然是以數(shù)據(jù)報包形式接受 ) receiver.receive( dp );//byte[] data =dp.getData() ; // 從數(shù)據(jù)報包 中獲取接受到的數(shù)據(jù) ( 長度可能超出實(shí)際數(shù)據(jù)的長度 )int n = dp.getLength() ; // 接受到的字節(jié)數(shù)組的實(shí)際長度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 ) ;// 創(chuàng)建一個可以基于UDP 協(xié)議進(jìn)行 收、發(fā) 數(shù)據(jù)的 DatagramSocket 對象DatagramSocket sender = new DatagramSocket( local );// 指定遠(yuǎn)程IP地址和端口 ( 就是 數(shù)據(jù)報包 的目的地址 )SocketAddress remote = new InetSocketAddress( "192.168.0.106" , 5555 );final byte[] buffer = { };DatagramPacket dp = new DatagramPacket( buffer, buffer.length );// 為數(shù)據(jù)報包 指定目的地址 dp.setSocketAddress( remote );String s ;System.out.println( "請輸入你要發(fā)送的數(shù)據(jù):" );while( ( s = scan.nextLine() ) != null ){byte[] data = s.getBytes() ;// 每次發(fā)送之前都將要發(fā)送的數(shù)據(jù)設(shè)置到 數(shù)據(jù)報包 中 dp.setData( data );// 發(fā)送數(shù)據(jù)報包 sender.send( dp );if( "byebyebye".equalsIgnoreCase( s ) ){break ;}System.out.println( "請輸入你要發(fā)送的數(shù)據(jù):" );}sender.close();scan.close();}}運(yùn)行結(jié)果如下:
world hello hi hello byebyebye 請輸入你要發(fā)送的數(shù)據(jù): world 請輸入你要發(fā)送的數(shù)據(jù): hello 請輸入你要發(fā)送的數(shù)據(jù): byebyebye 請輸入你要發(fā)送的數(shù)據(jù): hi 請輸入你要發(fā)送的數(shù)據(jù): hello 請輸入你要發(fā)送的數(shù)據(jù): byebyebye基于UDP協(xié)議的多次收發(fā)測試案例二:
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 ) ;// 創(chuàng)建一個可以基于UDP 協(xié)議進(jìn)行 收、發(fā) 數(shù)據(jù)的 DatagramSocket 對象DatagramSocket receiver = new DatagramSocket( local );final byte[] buffer = new byte[ 1024 ];// 創(chuàng)建一個用來接受數(shù)據(jù)的數(shù)據(jù)報包 對象DatagramPacket dp = new DatagramPacket( buffer , buffer.length );while( true ) {// 接受數(shù)據(jù) ( 接受時,依然是以數(shù)據(jù)報包形式接受 ) receiver.receive( dp );InetSocketAddress remote = (InetSocketAddress)dp.getSocketAddress(); // 作為接收者,獲得發(fā)送者對應(yīng)的 IP 地址 和 端口String sender = remote.getHostString() + ":" + remote.getPort() ;int n = dp.getLength() ; // 接受到的字節(jié)數(shù)組的實(shí)際長度String s = new String( buffer , 0 , n );System.out.println( sender + "說: " + s );if( "byebyebye".equalsIgnoreCase( s ) ){break ;}}receiver.close();}}運(yùn)行結(jié)果如下:
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 請輸入你要發(fā)送的數(shù)據(jù): byebyebye 請輸入你要發(fā)送的數(shù)據(jù): hi 請輸入你要發(fā)送的數(shù)據(jù): byebye 請輸入你要發(fā)送的數(shù)據(jù): byebyebye?MulticastSocket
1、多播數(shù)據(jù)報套接字類用于發(fā)送和接收 IP 多播包
MulticastSocket 是一種(UDP) DatagramSocket。
它具有加入Internet 上其他多播主機(jī)的“組”的附加功能。
多播組通過D 類IP 地址和標(biāo)準(zhǔn)UDP 端口號指定。
- D 類IP 地址在224.0.0.0 和239.255.255.255 的范圍內(nèi)(包括兩者)。
- 地址224.0.0.0 被保留,不應(yīng)使用。
構(gòu)造方法:
- MulticastSocket()使用本機(jī)默認(rèn)地址、隨機(jī)端口來創(chuàng)建MulticastSocket 對象。
- MulticastSocket(int port)使用本機(jī)默認(rèn)地址和指定端口號來創(chuàng)建MulticastSocket 對象。
- MulticastSocket( SocketAddress bindaddr )創(chuàng)建綁定到指SocketAddress 的MulticastSocket 對象該SocketAddress 中包含了IP 地址和端口號。
常用方法:
- void joinGroup(InetAddress mcastaddr)加入多播組。
- void joinGroup( SocketAddress sa, NetworkInterface ni )加入指定接口上的指定多播組。
- void leaveGroup( InetAddress ia )離開多播組。
- void leaveGroup( SocketAddress sa, NetworkInterface ni )離開指定本地接口上的多播組。
- int getTimeToLive()獲取在套接字上發(fā)出的多播數(shù)據(jù)包的默認(rèn)生存時間。
- void setTimeToLive(int ttl)設(shè)置所發(fā)出的多播數(shù)據(jù)包的默認(rèn)生存時間(用以控制多播的范圍)。
ttl = 0 , 表示指定數(shù)據(jù)報應(yīng)停留在本地主機(jī)。
ttl = 1 , 表示指定數(shù)據(jù)報發(fā)送到本地局域網(wǎng)(這是默認(rèn)值)。
ttl = 32 , 表示指定數(shù)據(jù)報只能發(fā)送到本站的的網(wǎng)絡(luò)上。
ttl = 64 , 表示指定數(shù)據(jù)報應(yīng)保留在本地區(qū)。
ttl = 128 , 表示指定數(shù)據(jù)報應(yīng)保留在本大洲。
ttl = 255 , 表示指定數(shù)據(jù)報可以發(fā)送到Internet 的所有地方。 - boolean getLoopbackMode()獲取多播數(shù)據(jù)報的本地回送的設(shè)置。
- void setLoopbackMode(boolean disable)啟用/禁用多播數(shù)據(jù)報的本地回送。
- NetworkInterface getNetworkInterface()獲取多播網(wǎng)絡(luò)接口集合。
- void setNetworkInterface(NetworkInterface netIf)指定在此套接字上發(fā)送的輸出多播數(shù)據(jù)報的網(wǎng)絡(luò)接口。
- InetAddress getInterface()獲取用于多播數(shù)據(jù)包的網(wǎng)絡(luò)接口的地址。
- void setInterface(InetAddress inf)設(shè)置多播網(wǎng)絡(luò)接口,供其行為將受網(wǎng)絡(luò)接口值影響的方法使用。
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 };// 創(chuàng)建 多播組 對應(yīng)的 IP 地址 ( 224.0.0.0 ~ 239.255.255.255 )final InetAddress group = InetAddress.getByAddress( addr );final int port = 445 ;// 創(chuàng)建可以實(shí)現(xiàn)多點(diǎn)廣播的 MulticastSocket 對象MulticastSocket node = new MulticastSocket( port );// 讓當(dāng)前的 "節(jié)點(diǎn)" 加入指定的 多播組 node.joinGroup( group );// 創(chuàng)建一個獨(dú)立的接受數(shù)據(jù)的線程ReceiveThread t = new ReceiveThread( node );t.start(); // 啟動線程// 創(chuàng)建一個數(shù)據(jù)報包對象 ( 其中 "沒有" 要發(fā)送的數(shù)據(jù) )DatagramPacket dp = new DatagramPacket( new byte[0] , 0 );// 指定數(shù)據(jù)報包的目的地址 dp.setAddress( group );// 指定數(shù)據(jù)報包的目的端口 dp.setPort( port );Scanner scanner = new Scanner( System.in );while( true ){System.out.println( "請輸入你要發(fā)送的信息: " );String s = scanner.nextLine();byte[] data = s.getBytes() ; // 將 字符串 根據(jù)當(dāng)前默認(rèn)平臺編碼編碼為字節(jié)數(shù)組 ( String ---> byte[] )dp.setData( data ); // 設(shè)置將要發(fā)送的數(shù)據(jù)node.send( dp ); // 發(fā)送數(shù)據(jù)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 ); // 守護(hù)線程 }@Overridepublic void run() {final byte[] buffer = new byte[ 1 << 16 ] ;DatagramPacket dp = new DatagramPacket( buffer , buffer.length );while( true ) {try {System.out.println( "接受數(shù)據(jù)" );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僅僅同意數(shù)據(jù)報發(fā)送給指定的目標(biāo)地址,而MulticastSocket能夠?qū)?shù)據(jù)報以廣播的方式發(fā)送到多個client。
若要使用多點(diǎn)廣播,則須要讓一個數(shù)據(jù)報標(biāo)有一組目標(biāo)主機(jī)地址,當(dāng)數(shù)據(jù)報發(fā)出后,整個組的全部主機(jī)都能收到該數(shù)據(jù)報。IP多點(diǎn)廣播(或多點(diǎn)發(fā)送)實(shí)現(xiàn)了將單一信息發(fā)送到多個接受者的廣播,其思想是設(shè)置一組特殊網(wǎng)絡(luò)地址作為多點(diǎn)廣播地址,每個多點(diǎn)廣播地址都被看做一個組,當(dāng)client須要發(fā)送、接收廣播信息時,增加到改組就可以。
MulticastSocket既能夠?qū)?shù)據(jù)報發(fā)送到多點(diǎn)廣播地址,也能夠接收其它主機(jī)的廣播信息。
待解決問題
MulticastSocket
轉(zhuǎn)載請于明顯處標(biāo)明出處
http://www.cnblogs.com/AmyZheng/p/8616135.html
轉(zhuǎn)載于:https://www.cnblogs.com/AmyZheng/p/8616135.html
總結(jié)
- 上一篇: Python攻克之路-random模块
- 下一篇: 静态页面中导航切换时的当前状态(四中方法