1. 程式人生 > >等待喚醒機制,UDP通信和TCP通信

等待喚醒機制,UDP通信和TCP通信

其它 www. cal 輸入 width tro interrupt 客戶端 ann

等待喚醒機制

  通過等待喚醒機制使各個線程能有效的利用資源。

等待喚醒機制所涉及到的方法:

   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=new
Resourse(); 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通信