socket編程:
1、網絡基礎知識
兩臺計算機通過網絡進行通信,首先兩臺計算機要有唯一的標識,即唯一的IP地址。其次他們要有共同的語言用來交流即協議。再者,每套主機要有相應的端口號。
TCP/IP協議:
--TCP/IP協議是目前世界上應用最為廣泛的協議,是以TCP和IP為基礎的不同層次上多個協議的集合,也稱:TCP/IP協議族 或 TCP/IP協議棧
--TCP的全稱:Transmission Control Protocol 傳輸控制協議
--IP的全稱:Internet Protocol 互聯網協議
TCP/IP模型
一般分為五層:
5-應用層:HTTP 超文件傳輸協議 FTP 文件傳輸協議 SMTP 簡單郵件傳送協議 Telnet遠程登錄服務
4-傳輸層:TCP/IP協議
3-網絡層
2-數據鏈路層
1-物理層:網線,雙絞線,網卡
IP地址---《探索Linux的網絡世界》為實現網絡中不同計算機之間的通信,每臺機器都必須有一個唯一的標識---IP地址
IP地址格式:數字型,如:192.168.0.1
IP地址的長度:32位的二進制
端口
端口號
1、用于區分不同應用程序
2、端口號范圍為0~65535,其中0~1023為系統所保留
3、IP地址和端口號就組成了所謂的Socket,Socket是網絡上運行的程序之間雙向通信鏈路的終結點,是TCP和UDP的基礎。
4、常見的一些協議使用的端口號
http:80 ftp:21 telnet:23
Java中的網絡支持
針對網絡通信的不同層次,Java提供的網絡功能有四大類:
1、InetAddress類:用于標識網絡上的硬件資源。
2、URL:統一資源定位符 通過URL可以直接讀取或寫入網絡上的數據。
3、Sockets(TCP編程):使用TCP協議實現網絡通信的Socket相關的類。
4、Datagram(UDP編程):使用UDP協議,將數據保存在數據報中,通過網絡進行通信。
2、InetAdress類
java.net.InetAddress
所有已實現的接口:Serializable
直接已知子類:Inet4Address,Inet6Address
沒有構造方法。
(1)InetAddress類用于標識網絡上的硬件資源,表示互聯網協議(IP)地址。
栗子又來啦!!!
1 import java.net.InetAddress; 2 import java.net.UnknownHostException; 3 import java.util.Arrays; 4 5 public class Inet{ 6 public static void main(String[] args)throws UnknownHostException{ 7 //獲取本機的InetAddress實例 8 InetAddress address=InetAddress.getLocalHost(); 9 System.out.println("計算機名:"+address.getHostName()); 10 System.out.println("IP地址:"+address.getHostAddress()); 11 byte[] bytes=address.getAddress();//獲取字節數組形式的IP地址 12 System.out.println("字節數組形式的IP:"+Arrays.toString(bytes)); 13 System.out.println(address);//直接輸出InetAdress對象 14 15 //根據機器名獲取InetAddress實例 16 //InetAddress address2=InetAddress.getByName("MyTeam"); 17 //根據IP地址獲取相應的實例信息 18 InetAddress address2=InetAddress.getByName("192.16.54.161"); 19 System.out.println("計算機名:"+address2.getHostName()); 20 System.out.println("IP地址:"+address2.getHostAddress()); 21 } 22 }
會輸出計算機名,IP地址,以及字節數組形式的IP。
輸出:(計算機名純屬自己杜撰,如有雷同,純屬巧合,IP地址也是)
計算機名:MyTeam
IP地址:192.16.54.161
字節數組形式的IP:[-80,16,54,-70]
MyTeam/192.16.54.161
計算機名:MyTeam.it2102.fhjd.com.cn
IP地址:192.16.54.161
3、URL
3-1、URL(Uniform Resource Locator)統一資源定位符,表示Internet上某一資源的地址
小栗子來理解url的常用方法:
1 import java.net.URL; 2 import java.net.MalformedURLException; 3 //URL的常用方法 4 public class UURL{ 5 public static void main(String[] args){ 6 try{ 7 //創建一個URL實例 8 URL study=new URL("http://www.study.com"); 9 //?后面表示參數,#后面表示錨點 10 URL url=new URL(study,"/index.html?username=tom#test"); 11 System.out.println("協議:"+url.getProtocol());//輸出:協議:http 12 System.out.println("主機:"+url.getHost()); 13 //如果未指定端口號,則根據協議的不同,使用默認的端口號,此時getPort()方法的返回值為-1. 14 System.out.println("端口:"+url.getPort()); 15 System.out.println("文件路徑:"+url.getPath());//輸出:文件路徑:/index.html 16 System.out.println("文件名:"+url.getFile());//輸出:/index.html?username=tom 17 System.out.println("相對路徑:"+url.getRef());//輸出:test 18 System.out.println("查詢字符串:"+url.getQuery());//輸出:username=tom 19 }catch(MalformedURLException e){ 20 e.printStackTrace(); 21 } 22 23 } 24 }
3-2、使用URL讀取網頁內容
1、通過URL對象的openStream()方法可以得到指定資源的輸入流。
2、通過流來讀取、訪問網絡上的數據。
!!!看這里
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 public class Test{ 8 public static void main(String[] args){ 9 try{ 10 //創建一個URL實例 11 URL url=new URL("http://www.baidu.com"); 12 //通過URL的openStream方法獲取URL對象所表示的資源的字節輸入 13 InputStream is=url.openStream(); 14 //將字節輸入流轉換為字符輸入流 15 InputStreamReader isr=new InputStreamReader(is,"utf-8"); 16 //為流添加緩沖,提高讀取的效率 17 BufferedReader br =new BufferedReader(isr); 18 String data=br.readLine();//讀取數據 19 while(data!=null){//循環讀取數據 20 System.out.println(data);//輸出數據 21 data=br.readLine(); 22 } 23 br.close(); 24 isr.close(); 25 is.close(); 26 }catch(MalformedUrlException e){ 27 e.printStackTrace(); 28 }catch(IOException e){ 29 e.printStackTrace(); 30 } 31 } 32 }
4、Socket通信
TCP協議是面向連接、可靠的、有序的、以字節流的方式發送數據。
基于TCP協議實現網絡通信的類:客戶端的Socket類,服務器端的ServerSocket類
Socket通信模型
Server
建立服務器傾聽socket ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Client
等待并接收連接請求------------建立連接---------創建連接socket向服務端發送請求
接收請求后創建連接socket
--------------------------------------------------------------------------------------------------------------------
InputStream----------------------------------------------OutputStream
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 開始通信
OutputStream-------------------------------------------InputStream
-------------------------------------------------------------------------------------------------------------------
關閉socket及相關資源--------結束通信-------------關閉socket及相關資源
Socket通信實現步驟
1、分別在服務器端和客戶端創建ServerSocket和Socket
2、打開連接到Socket的輸入/輸出流,進行數據通信
3、按照協議對Socket進行讀/寫操作
4、關閉輸入輸出流,關閉Socket
ServerSocket:
ServerSocket類位于java.net包中,直接已知子類是SQLServerSocket,此類實現服務器套接字。
服務器套接字等待請求通過網絡傳入。它基于該請求執行某些操作,然后可能向請求者返回結果。
服務器套接字的實際工作由SocketImpl類的實例執行。應用程序可以更改創建套接字實現的套接字工廠來配置它自身,從而創建適合本地防火墻的套接字。
構造方法:
ServerSocket(int port)----創建綁定到特定端口的服務器套接字。
方法摘要:
accept()-----偵聽并接受到此套接字的連接。
bind()-------將ServerSocket綁定到特定地址(IP地址和端口號)
close()------關閉此套接字。
getChannel()------返回與此套接字關聯的唯一ServerSocketChannel對象(如果有)
getInetAddress()------返回此服務器套接字的本地地址。
getLocalPort()--------返回此套接字在其上偵聽的端口。
getLocalSocketAddress()----返回此套接字綁定的端點的地址,如果尚未綁定則返回null。
getReceiveBufferSize()---獲取此ServerSocket的SO_RCVBUF選項的值,該值是將用于從此ServerSocket接受的套接字的建議緩沖區大小。
Socket:
位于java.net包中,直接已知子類是:SSLSocket,此類實現客戶端套接字(也可以就叫“套接字”)。套接字
是兩臺機器間通信的端點。
套接字的實際工作由SocketImpl類的實例執行。應用程序通過更改創建套接字實現的套接字工廠可以配置它自身,創建適合本地防火墻的套接字。
構造方法:
Socket(InetAddress address,int port)----創建一個流套接字并將其連接到指定IP地址的指定端口號。
Socket(String host,int port)-----創建一個流套接字并將其連接到指定主機上的指定端口號。
方法摘要:
bind(SocketAddress bindpoint)---將套接字綁定到本地地址。
close()-----關閉此套接字。
connect(SocketAddress endpoint,int timeout)------將此套接字連接至服務器,并制定一個超時值。
getChannel()--------返回與此數據報套接字關聯的唯一的SocketChannel對象(如果有)。
getInetAddress()-----返回套接字連接的地址。
getInputStream()-----返回此套接字的輸入流。
getKeepAlive()-------測試是否啟用SO_KEEPALIVE.
shutdownInput()------此套接字的輸入流置于“流的末尾”。
shutdownOutput()------禁用此套接字的輸出流。
實現用戶登錄的步驟:
基于TCP協議的Socket通信,實現用戶登錄
服務器端:
1 import java.net.ServerSocket; 2 import java.net.Socket; 3 import java.io.InputStream; 4 import java.io.InputStreamReader; 5 import java.io.IOException; 6 import java.io.OutputStream; 7 import java.io.PrintWriter; 8 public class Server{ 9 public static void main(String[] args){ 10 try{ 11 //1、創建一個服務器端Socket,即ServerSocket,指定綁定的端口,并監聽此端口 12 ServerSocket serverSocket=new ServerSocket(8888); 13 //2、調用accept()方法開始監聽,等待客戶端的連接 14 System.out.println("***服務器即將啟動,等待客戶端的連接***"); 15 serverSocket.accept(); 16 Socket socket=serverSocket.accept(); 17 //3、獲取輸入流,并讀取客戶端信息 18 InputStream is=socket.getInputStream();//字節輸入流 19 InputStreamReader isr=new InputStreamReader(is);//將字節流轉換為字符流 20 BufferedReader br=new BufferedReader(isr);//為輸入流添加緩沖 21 String info=null; 22 while((info=br.readLine())!=null){//循環讀取客戶端的信息 23 System.out.println("我是服務器,客戶端說:"+info); 24 } 25 socket.shutdownInput();//關閉輸入流 26 //4、獲取輸出流,響應客戶端的請求 27 OutputStream os=socket.getOutputStream(); 28 PrintWriter pw=new PrintWriter(os);//包裝為打印流 29 pw.write("歡迎您!"); 30 pw.flush();//調用flush()方法將緩沖輸出 31 //5、關閉相關資源 32 br.close(); 33 isr.close(); 34 is.close(); 35 pw.close(); 36 os.close(); 37 socket.close(); 38 serverSocket.close(); 39 }catch(IOException e){ 40 e.printStackTrace(); 41 } 42 } 43 }
客戶端:
1 import java.net.Socket; 2 import java.net.IOException; 3 import java.io.BufferedReader; 4 import java.io.InputStreamReader; 5 import java.io.InputStream; 6 import java.io.OutputStream; 7 import java.io.PrintWriter; 8 import java.net.UnknownHostException; 9 public class Client{ 10 public static void main(String[] args){ 11 try{ 12 //1、創建客戶端Socket,指定服務器地址和端口 13 Socket socket=new Socket("localhost",8888); 14 //2、獲取輸出流,向服務器端發送信息 15 OutputStream os=socket.getOutputStream();//字節輸出流 16 PrintWriter pw=new PrintWriter(os);//將輸出流包裝為打印流 17 pw.write("用戶名:admin;密碼:123"); 18 pw.flush(); 19 socket.shutdownOutput();//關閉輸出流 20 //3、獲取輸入流,并讀取服務器端的響應信息 21 InputStream is=socket.getInputStream(); 22 BufferedReader br=new BufferedReader(new InputStreamReader(is));//將字節流包裝為字符流 23 String info=null; 24 while((info=br.readLine())!=null){ 25 System.out.println("我是客戶端,服務器說:"+info); 26 } 27 //4、關閉資源 28 br.close(); 29 is.close(); 30 pw.close(); 31 os.close(); 32 socket.close(); 33 }catch(UnknownHostException e){ 34 e.printStackTrace(); 35 }catch(IOException e){ 36 e.printlnStackTrace(); 37 } 38 } 39 }
注:服務器必須早于客戶端啟動
5、使用多線程實現多客戶端的通信
基本步驟:
1、服務器端創建ServerSocket,循環調用accept()等待客戶端連接
2、客戶端創建一個socket并請求和服務器端連接
3、服務器端接受客戶端請求,創建socket與該客戶建立專線連接
4、創建連接的兩個socket在一個單獨的線程上對話
5、服務器端繼續等待新的連接
服務器線程處理類:
1 import java.net.Socket; 2 import java.io.InputStream; 3 import java.io.IOException; 4 import java.io.BufferedReader; 5 import java.io.InputStreamReader; 6 import java.io.OutputStream; 7 import java.io.PrintWriter; 8 9 public class ServerThread extends Thread{ 10 //與本線程相關的Socket 11 Socket socket=null; 12 13 public ServerThread(Socket socket){ 14 this.socket=socket; 15 } 16 17 //線程執行的操作,響應客戶端的請求 18 public void run(){ 19 InputStream is=null; 20 InputStreamReader isr=null; 21 BufferedReader br=null; 22 OutputStream os=null; 23 PrintWriter pw=null; 24 try{ 25 //獲取輸入流,讀取客戶端的信息 26 is=socket.getInputStream();//字節輸入流 27 isr=new InputStreamReader(is);//將字節流轉換為字符流 28 br=new BufferedReader(isr);//為輸入流添加緩沖 29 String info=null; 30 while((info=br.readLine())!=null){//循環讀取客戶端的信息 31 System.out.println("我是服務器,客戶端說:"+info); 32 } 33 socket.shutdownInput();//關閉輸入流 34 //獲取輸出流,響應客戶端的請求 35 os=socket.getOutputStream(); 36 pw=new PrintWriter(os);//包裝為打印流 37 pw.write("歡迎您!"); 38 pw.flush();//調用flush()方法將緩沖輸出 39 }catch(IOException e){ 40 e.printStrackTrace(); 41 }finally{ 42 //關閉相關資源 43 try{ 44 if(pw!=null) pw.close(); 45 if(os!=null) os.close(); 46 if(br!=null) br.close(); 47 if(isr!=null) isr.close(); 48 if(is!=null) is.close(); 49 if(socket!=null) socket.close(); 50 }catch(IOException e){ 51 e.printStrackTrace(); 52 } 53 } 54 } 55 }
服務器端:
1 import java.net.ServerSocket; 2 import java.net.Socket; 3 import java.io.InputStream; 4 import java.io.InputStreamReader; 5 import java.io.IOException; 6 import java.io.OutputStream; 7 import java.io.PrintWriter; 8 public class Server{ 9 public static void main(String[] args){ 10 try{ 11 //1、創建一個服務器端Socket,即ServerSocket,指定綁定的端口,并監聽此端口 12 ServerSocket serverSocket=new ServerSocket(8888); 13 Socket socket=null; 14 //記錄客戶端的數量 15 int count=0; 16 System.out.println("***服務器即將啟動,等待客戶端的連接***"); 17 //循環監聽等待客戶端的連接 18 while(true){ 19 //調用accept()方法開始監聽,等待客戶端的連接 20 socket=serverSocket.accept(); 21 //啟動一個線程,通過當前線程與客戶端進行通信 22 ServerThread serverThread=new ServerThread(socket); 23 //啟動線程 24 serverThread.start(); 25 26 count++;//統計客戶端的數量 27 System.out.println("客戶端的數量:"+count); 28 InetAddress address=socket.getInetAddress(); 29 System.out.println("當前客戶端的IP:")+address.getHostAddress()); 30 } 31 }catch(IOException e){ 32 e.printStackTrace(); 33 } 34 } 35 }
?
客戶端:
1 import java.net.Socket; 2 import java.net.IOException; 3 import java.io.BufferedReader; 4 import java.io.InputStreamReader; 5 import java.io.InputStream; 6 import java.io.OutputStream; 7 import java.io.PrintWriter; 8 import java.net.UnknownHostException; 9 public class Client{ 10 public static void main(String[] args){ 11 try{ 12 //1、創建客戶端Socket,指定服務器地址和端口 13 Socket socket=new Socket("localhost",8888); 14 //2、獲取輸出流,向服務器端發送信息 15 OutputStream os=socket.getOutputStream();//字節輸出流 16 PrintWriter pw=new PrintWriter(os);//將輸出流包裝為打印流 17 //模擬多客戶端同時登錄 18 pw.write("用戶名:admin;密碼:123"); 19 //pw.write("用戶名:er;密碼:456"); 20 pw.flush(); 21 socket.shutdownOutput();//關閉輸出流 22 //3、獲取輸入流,并讀取服務器端的響應信息 23 InputStream is=socket.getInputStream(); 24 BufferedReader br=new BufferedReader(new InputStreamReader(is));//將字節流包裝為字符流 25 String info=null; 26 while((info=br.readLine())!=null){ 27 System.out.println("我是客戶端,服務器說:"+info); 28 } 29 //4、關閉資源 30 br.close(); 31 is.close(); 32 pw.close(); 33 os.close(); 34 socket.close(); 35 }catch(UnknownHostException e){ 36 e.printStackTrace(); 37 }catch(IOException e){ 38 e.printlnStackTrace(); 39 } 40 } 41 }
6、基于UDP的socket編程
UDP協議(用戶數據報協議)是無連接的、不可靠的、無序的。
UDP協議以數據報作為數據傳輸的載體
使用UDP進行數據傳輸時,首先需要將要傳輸的數據定義成數據報(Datagram),在數據報中指明數據所要達到的Socket(主機地址和端口號),然后再將數據報發送出去。
相關操作類:
DatagramPacket:表示數據報包,數據報包用來實現無連接包投遞服務。每條報文僅根據該包中包含的信息從一臺機器路由器。從一臺機器發送到另一臺機器的多個包可能選擇不同的路由,也可能按不同的順序傳遞做出保證。
構造方法摘要:
DatagramPacket(byte[] buf, int length):構造DatagramPacket,用來接收長度為length的數據包。
DatagramPacket(byte[] buf,int length,InetAddress address,int port):構造數據報包,用來將長度為length的包發送至指定主機上的指定端口號。
DatagramPacket(byte[] buf,int offset,int length):構造DatagramPacket,用來接收長度為length的包,在緩沖區指定了偏移量為offset
DatagramSocket:進行端到端通信的類,表示用來發送和接收數據報包的套接字。
數據報套接字是包投遞服務的發送或接收點。每個在數據報套接字上發送或接收的包都是自由的。從一臺機器發送到另一臺機器的多個包可能選擇不同的路由,也可能按不同的順序。
構造方法摘要:
DatagramSocket():構造數據報套接字并將其綁定到本地主機上任何可用的端口。
DatagramSocket(DatagramSocketImpl impl):創建帶有指定DatagramSocketImpl的未綁定數據報套接字。
DatagramSocket(int port,InetAddress laddr):創建數據報套接字,將其綁定到指定的本地地址。
方法摘要:
close():關閉此數據報套接字。
bind():將此DatagramSocket綁定到特定的地址和端口。
getLocalAddress():返回此套接字連接的地址
getpPort():返回此套接字的端口
send():從此套接字發送數據報包
receive():從此套接字接收數據報包
基于UDP的用戶登錄:
服務器端實現步驟:
1 import java.io.IOException; 2 import java.net.DatagramPacket; 3 import java.net.DatagramSocket; 4 import java.net.InetAddress; 5 6 public class UDPServer{ 7 public static void main(String[] args) throws IOException{ 8 /* 9 *接受客戶端發送的數據 10 */ 11 //1、創建服務器端DatagramSocket,指定端口 12 DatagramSocket socket=new DatagramSocket(8800); 13 //2、創建數據報,用于接收客戶端發送的數據 14 byte[] data=new byte[1024];//創建字節數組,指定接收的數據包的大小 15 DatagramPacket packet=new DatagramPacket(data,data.length); 16 //3、接收客戶端發送的數據 17 System.out.println("****服務器端已經啟動,等待客戶端發送數據"); 18 socket.receive(packet);//此方法在接收到數據報之前會一直阻塞 19 //4、讀取數據 20 String info=new String(data,0,packet.getLength()); 21 System.out.println("我是服務器,客戶端說:"+info); 22 /* 23 *向客戶端響應數據 24 */ 25 //1、定義客戶端的地址、端口號、數據 26 InetAddress address=packet.getAddress(); 27 int port=packet.getPort(); 28 byte[] data2="歡迎您!".getBytes(); 29 //2、創建數據報、包含響應的數據信息 30 DatagramPacket packet=new DatagramPacket(data2,data2.length,address,port) 31 //3、響應客戶端 32 socket.send(packet2); 33 //4、關閉資源 34 socket.close(); 35 } 36 }
客戶端實現步驟:
1 import java.net.DatagramSocket; 2 import java.net.DatagramPacket; 3 import java.net.InetAddress; 4 import java.io.IOException; 5 6 public class UDPClient{ 7 /* 8 *向服務器端發送請求 9 */ 10 public static void main(String[] args) throws IOException{ 11 //1、定義服務器的地址、端口號、數據 12 InetAddress address=InetAddress.getByName("localhost"); 13 int port=8800; 14 byte[] data="用戶名:admin;密碼:123".getBytes(); 15 //2、創建數據報,包含發送的數據信息 16 DatagramPacket packet packet=new DatagramPacket(data,data.length,address,port) 17 //3、創建DatagramSocket對象 18 DatagramSocket socket=new DatagramSocket(); 19 //4、向服務器端發送數據報 20 socket.send(packect); 21 22 /* 23 *接收服務器端響應的數據 24 */ 25 //1、創建數據報,用于接收服務器端響應的數據 26 byte[] data2=new byte[1024]; 27 DatagramPacket packet2=new DatagramPacket(data2,data.length); 28 //2、接受服務器響應的數據 29 socket.receive(packet2); 30 //3、讀取數據 31 String reply=new String(data2,0,packet2.getLength()); 32 System.out.println("我是客戶端,服務器說:"+reply); 33 //4、關閉資源 34 socket.close(); 35 } 36 }
重點:Socket通信,基于TCP的Socket通信
經驗和技巧:
多線程的優先級,是否關閉輸入流和輸出流,使用TCP通信傳輸對象,socket編程傳遞文件
?
?