等待喚醒機制,UDP通信和TCP通信
等待喚醒機制
通過等待喚醒機制使各個線程能有效的利用資源。
等待喚醒機制所涉及到的方法:
wait() :等待,將正在執行的線程釋放其執行資格 和 執行權,並存儲到線程池中。
notify():喚醒,喚醒線程池中被wait()的線程,一次喚醒一個,而且是任意的。
notifyAll(): 喚醒全部:可以將線程池中的所有wait() 線程都喚醒。
所謂喚醒的意思就是讓 線程池中的線程具備執行資格。
必須註意的是:這些方法都是在 同步中才有效;
同時這些方法在使用時必須標明所屬鎖,這樣才可以明確出這些方法操作的到底是哪 個鎖上的線程。而鎖又可以是任意對象。能被任意對象調用的方法一定定義在Object類中。
例子:
代碼如下:
模擬資源類:
package com.oracle.Resourse; public class Resourse { public String name; public String sex; public boolean flag = false; }
輸入線程任務類:
package com.oracle.Resourse; public class InputR implements Runnable{ private int i=0; private Resourse r=newResourse(); public InputR(Resourse r){ this.r=r; } public void run() { while(true){ synchronized (r){ if(r.flag){ try { r.wait(); } catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace(); } } if(i%2==0){ r.name="張三"; r.sex="男"; }else{ r.name="LISI"; r.sex="NV"; } r.flag=true; r.notify(); } i++; } } }
輸出線程任務類:
package com.oracle.Resourse; public class OutputR implements Runnable{ private Resourse r=new Resourse(); public OutputR(Resourse r){ this.r=r; } public void run() { while(true){ synchronized (r){ if(r.flag==false){ try { r.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.println(r.name+"..."+r.sex); r.flag=false; r.notify(); } } } }
測試類:
package com.oracle.Resourse; public class TEST { public static void main(String[] args) { Resourse r=new Resourse(); InputR in=new InputR(r); OutputR out=new OutputR(r) ; Thread t1=new Thread(in); Thread t2=new Thread(out); t1.start(); t2.start(); } }
結果如下:
InetAddress類
InetAdderss類,該類用於封裝一個IP地址,並提供了一系列與IP地址相關的方法
例子:
public class Example01 { public static void main(String[] args) throws Exception { InetAddress local = InetAddress.getLocalHost(); InetAddress remote = InetAddress.getByName("www.oracle.cn"); System.out.println("本機的IP地址:" + local.getHostAddress()); System.out.println("oracle的IP地址:" + remote.getHostAddress()); System.out.println("oracle的主機名為:" + remote.getHostName()); } }
UDP是一種面向無連接的協議,因此,在通信時發送端和接收端不用建立連接。
DatagramPacket類
該類的實例對象就相當於一個集裝箱,用於封裝UDP通信中發送或者接收的數據。
構造方法
發送端一定要明確指出數據的目的地(ip地址和端口號),而接收端不需要明確知道數據的來源,只需要接收到數據即可。
DatagramPacket類中的常用方法
DatagramSocket類
DatagramSocket類的作用就類似於碼頭,使用這個類的實例對象就可以發送和接收DatagramPacket數據包
構造方法:
該構造方法用於創建發送端的DatagramSocket對象,在創建DatagramSocket對象時,並沒有指定端口號,此時,系統會分配一個沒有被其它網絡程序所使用的端口號。
該構造方法既可用於創建接收端的DatagramSocket對象,又可以創建發送端的DatagramSocket對象,在創建接收端的DatagramSocket對象時,必須要指定一個端口號,這樣就可以監聽指定的端口。
常用方法:
例子:
UDP完成數據的發送:
package com.oracle.UdpTcp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.util.Scanner; public class UDPSend { public static void main(String[] args) throws IOException { Scanner sc=new Scanner(System.in ); //InetAddress類調用getbyName方法返回一個本類對象 InetAddress i=InetAddress.getByName("192.168.1.171"); //創建DatagramSocket對象, DatagramSocket ds = new DatagramSocket(); while(true){ //構造數據報包,用來將長度為 length 的包發送到指定主機上的指定端口號。 //定義字節數組來接收發送端的內容 String mes=sc.next(); byte[] bytes=mes.getBytes(); //裝包 DatagramPacket dp = new DatagramPacket(bytes, bytes.length, i, 6666); ds.send(dp); } } }
UDP完成數據的接收:
package com.oracle.UdpTcp; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; public class UdpS { public static void main(String[] args) throws IOException { //1,創建DatagramSocket對象,並指定端口號 DatagramSocket ds = new DatagramSocket(8888); //創建接收數據的字節數組 byte[] bytes =new byte[1024]; //創建接收數據包 while(true){ DatagramPacket dp = new DatagramPacket(bytes, bytes.length); //接收 ds.receive(dp); //獲取數據 String ip=dp.getAddress().getHostAddress();//獲取IP地址 int port=dp.getPort();//獲取端口號 int length = dp.getLength();//獲取多少條數據 System.out.println(ip+"..."+port+"發送的內容為"+new String(bytes,0,length)); } // ds.close(); } }
TCP通信
在JDK中提供了兩個類用於實現TCP程序,一個是ServerSocket類,用於表示服務器端,一個是Socket類,用於表示客戶端。
通信時,首先創建代表服務器端的ServerSocket對象,該對象相當於開啟一個服務,並等待客戶端的連接,然後創建代表客戶端的Socket對象向服務器端發出連接請求,服務器端響應請求,兩者建立連接開始通信。
ServerSocket類
構造方法
使用該構造方法在創建ServerSocket對象時,就可以將其綁定到一個指定的端口號上(參數port就是端口號)。
ServerSocket的常用方法:
ServerSocket對象負責監聽某臺計算機的某個端口號,在創建ServerSocket對象後,需要繼續調用該對象的accept()方法,接收來自客戶端的請求。當執行了accept()方法之後,服務器端程序會發生阻塞,直到客戶端發出連接請求,accept()方法才會返回一個Scoket對象用於和客戶端實現通信,程序才能繼續向下執行。
Socket類
構造方法
常用方法;
方法聲明 |
功能描述 |
int getPort() |
該方法返回一個int類型對象,該對象是Socket對象與服務器端連接的端口號 |
InetAddress getLocalAddress() |
該方法用於獲取Socket對象綁定的本地IP地址,並將IP地址封裝成InetAddress類型的對象返回 |
void close() |
該方法用於關閉Socket連接,結束本次通信。在關閉socket之前,應將與socket相關的所有的輸入/輸出流全部關閉,這是因為一個良好的程序應該在執行完畢時釋放所有的資源 |
InputStream getInputStream() |
該方法返回一個InputStream類型的輸入流對象,如果該對象是由服務器端的Socket返回,就用於讀取客戶端發送的數據,反之,用於讀取服務器端發送的數據 |
OutputStream getOutputStream() |
該方法返回一個OutputStream類型的輸出流對象,如果該對象是由服務器端的Socket返回,就用於向客戶端發送數據,反之,用於向服務器端發送數據 |
例子:
客戶端程序:
package com.oracle.Tcp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; public class TCPK { public static void main(String[] args) throws IOException { //創建socket對象,連接服務器 Socket s=new Socket ("127.0.0.1",7777 ); //通過客戶端套接字對象Socket對象中的獲取字節輸出流的方法 OutputStream out=s.getOutputStream(); //將數據寫向服務器 out.write("服務器你好".getBytes()); //接收服務器端的回復 InputStream in=s.getInputStream(); byte[] bytes=new byte[1024]; int len=in.read(bytes); System.out.println(new String(bytes,0,len)); s.close(); } }
服務器端程序:
package com.oracle.Tcp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPS { public static void main(String[] args) throws IOException { //創建服務器套接字 ServerSocket ss=new ServerSocket(7777); //調用ACCEPT方法與客戶端創建鏈接 Socket s=ss.accept(); InputStream in=s.getInputStream(); byte[] bytes=new byte[1024]; int len=in.read(bytes); System.out.println(new String(bytes,0,len)); //服務器端給回復 OutputStream out=s.getOutputStream(); out.write("收到".getBytes()); } }
文件上傳案例
首先編寫服務器端程序,用來接收圖片。
package com.oracle.TCPS; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.Random; public class TCPS { public static void main(String[] args) throws IOException { ServerSocket server=new ServerSocket(7500);//明確端口號 Socket socket=server.accept(); //讀取文件,明確數據源 InputStream in=socket.getInputStream(); //明確目的地 File file=new File("e:\\JPG"); //判斷文件是否存在 if(!file.exists()){ file.mkdirs();//沒有則創建文件夾 } //域名+毫秒值+6位隨機數 // 隨機數 String num=""; for(int i=0;i<6;i++){ num+=new Random().nextInt(10); } String filename="oracle"+System.currentTimeMillis()+num+".jpg"; FileOutputStream fos=new FileOutputStream(file+File.separator+filename); //復制‘ int len=0; byte[] bytes=new byte[1024]; while((len=in.read(bytes))!=-1){ fos.write(bytes,0,len);//往服務器端寫 } //回復客戶端 OutputStream out=socket.getOutputStream(); out.write("上傳成功".getBytes()); fos.close(); server.close(); } }
客戶端程序:
package com.oracle.TCPS; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.Socket; import java.net.UnknownHostException; public class TCPK { public static void main(String[] args) throws UnknownHostException, IOException { //創建socket對象,連接服務器 Socket socket=new Socket ("127.0.0.1",7500 ); //獲取Socket流中的輸出流,功能:用來把數據寫到服務器 OutputStream out=socket.getOutputStream(); //從文件讀到客戶端 FileInputStream fis=new FileInputStream("E:\\QQwenjian\\1972680739\\FileRecv\\0601.jpg"); //定義字節數組接收文件 byte[] bytes=new byte[1024]; int len=0; while((len=fis.read(bytes))!=-1){ out.write(bytes,0,len); } //客戶端發送數據完畢,結束Socket輸出流的寫入操作,告知服務器端,不再讀了 socket.shutdownOutput(); //接收服務器端的回復 InputStream in=socket.getInputStream(); len=in.read(bytes); System.out.println(new String(bytes,0,len));//將字節數組內容轉成字符串打印出來 //釋放資源 fis.close(); socket.close(); } }
多文件上傳案例
只需將服務器端代碼封裝改一下:
package com.oracle.TCPS; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetAddress; import java.net.Socket; import java.util.Random; public class UpLoad implements Runnable{ private Socket socket; public UpLoad(Socket socket){ this.socket=socket; } public void run() { FileOutputStream fos=null; try{ //顯示哪個客戶端Socket連接上了服務器 InetAddress ipObject = socket.getInetAddress();//得到IP地址對象 String ip = ipObject.getHostAddress(); //得到IP地址字符串 System.out.println("小樣,抓到你了,連接我!!" + "IP:" + ip); //讀取文件,明確數據源 InputStream in=socket.getInputStream(); //明確目的地 File file=new File("e:\\JPG"); //判斷文件是否存在 if(!file.exists()){ file.mkdirs();//沒有則創建文件夾 } //域名+毫秒值+6位隨機數 String filename="oracle"+System.currentTimeMillis()+new Random().nextInt(6)+".jpg"; fos=new FileOutputStream(file+File.separator+filename); //復制,把Socket輸入流中的數據,寫入目的地的字節輸出流中‘ int len=0; byte[] bytes=new byte[1024]; while((len=in.read(bytes))!=-1){ fos.write(bytes,0,len);//往服務器端寫 } //回復客戶端 OutputStream out=socket.getOutputStream(); out.write("上傳成功".getBytes()); }catch (IOException ex){ ex.printStackTrace(); }finally{ try { fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
測試類:
package com.oracle.TCPS; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class Demo { public static void main(String[] args) throws IOException { ServerSocket server=new ServerSocket(7500);//明確端口號 while(true){ Socket socket=server.accept(); new Thread(new UpLoad(socket)).start();//匿名內部類 } } }
等待喚醒機制,UDP通信和TCP通信