UDP通信(二)

勿以惡小而爲之,勿以善小而不爲--------------------------劉備

勸諸君,多行善事積福報,莫作惡

上一章簡單介紹了 InetAddress和爬蟲簡單應用(一),如果沒有看過,請觀看上一章

網絡通信,是 C/S 模式,需要編寫 客戶端和服務器端的代碼, 服務器端需要指定端口號, 客戶端需要指定 服務器的ip地址和端口號,這樣才能保持連接通信, 網絡通信,需要先開啓服務端,才能啓動客戶端。

還需要了解一點, 客戶端的輸入,連接的是服務器端的輸出, 客戶端的輸出,連接的是服務器端的輸入。

一. UDP 通信所需要用到的類

一.一 DatagramSocket 類

這是服務器端的類, 需要指定接口。

一.一.一 構造方法

一.一.一.一 方法

方法 作用
DatagramSocket​(int port) 本機的某個端口
DatagramSocket​(int port, InetAddress laddr) 某個指定主機的某個端口

一.一.一.二 演示

   @Test
    public void conTest(){

        try {
            //綁定的是本機
            DatagramSocket datagramSocket=new DatagramSocket(9999);
        } catch (SocketException e) {
            e.printStackTrace();
        }

        try {
            //綁定到指定的服務器
            InetAddress inetAddress=InetAddress.getByName("localhost");

            DatagramSocket datagramSocket=new DatagramSocket(9999,inetAddress);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }catch (SocketException e) {
            e.printStackTrace();
        }
    }

一.一.二 其他常用方法

方法 作用
void close​() 關閉
void send​(DatagramPacket p) 發送數據
void receive​(DatagramPacket p) 接收數據

發送時和接收數據時,發送和接收的是一個對象, DatagramPacket, 這個對象裏面封裝了真正的通信數據。

一.二 DatagramPacket

數據封裝包。

一.二.一 構造方法

一.二.一.一 方法

方法 作用
DatagramPacket​(byte[] buf, int offset, int length) 從客戶端接收數據,將offset到offset+len的數據,放置到 字節數組裏面
DatagramPacket​(byte[] buf, int offset, int length, InetAddress address, int port) 將字節數組數據發送到服務器
DatagramPacket​(byte[] buf, int offset, int length, SocketAddress address) 將字節數組數據發送到服務器

一.二.一.二 演示

 @Test
    public void packetConTest(){

        //定義的是byte 字節

        byte[] bytes=new byte[1024];


        //第一種,從客戶端接收數據
        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,1024);


        InetAddress inetAddress= null;
        try {
            inetAddress = InetAddress.getByName("localhost");
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

        //第二種,接收固定 主機
        DatagramPacket datagramPacket1=new DatagramPacket(bytes,0,1024,inetAddress,6666);

        //第三種, 在客戶端發送數據到服務器端

        DatagramPacket datagramPacket2=new DatagramPacket(bytes,0,1024,new InetSocketAddress("localhost",6666));


    }

一.二.二 其他方法

方法 作用
byte[] getData​() 獲取客戶端發送過來的真實數據
int getLength​() 獲取客戶端發送過來的數據的長度
int getOffset​() 獲取偏移量
getPort​() 獲取端口號
InetAddress getAddress​() 獲取裏面的 inetAddress對象

二. 演示各種 UDP 通信

二.一 單條字符串數據

二.一.一 客戶端

  @Test
    public void send1Test() throws Exception{
        //1. 定義客戶端
        System.out.println("------------發送方開始發送單條數據--------------");

        //客戶端端口
        DatagramSocket datagramSocket=new DatagramSocket(6666);

        //2. 準備數據
        String content="你好啊,我是兩個蝴蝶飛";

        //3. 將數據轉換成字節數據

        byte[] bytes=content.getBytes("UTF-8");

        //4. 封裝進包
        InetAddress inetAddress=InetAddress.getByName("localhost");

        //服務器爲 localhost,端口爲 9999
        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length,inetAddress,9999);


        //發送數據
        datagramSocket.send(datagramPacket);

        //關閉流
        datagramSocket.close();


    }

二.一.二 服務器端

@Test
    public void send1Test() throws Exception{

        //1. 定義服務器
        System.out.println("---------------服務器啓動成功,接收單條數據----------------");

        DatagramSocket datagramSocket=new DatagramSocket(9999);

        //2. 設置數據,封裝到 bytes字節數據, 這個數組可能是不夠用的。
        byte[] bytes=new byte[1024];

        //能接收的大小,並不是實際接收的大小
        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length);


        //3. 阻塞式接收傳遞過來的數據
        datagramSocket.receive(datagramPacket);


        //4. 獲取接收的數據, 是真實的數據。 

        byte[] content=datagramPacket.getData();

        int len=datagramPacket.getLength();

        System.out.println("接收的長度是:"+len+",接收的內容是:"+new String(content,0,len));

        //5 關閉流

        datagramSocket.close();


    }

二.一.三 運行程序

先打開服務器端,

有圖片

一直在阻塞式接收客戶端

再打開客戶端

有圖片

這個時候,再查看一下服務器端

有圖片

二.二 發送多條不同類型的數據

二.二.一 客戶端

@Test
    public void send2Test() throws Exception{
        //1. 定義客戶端
        System.out.println("------------發送方開始發送多條數據--------------");
        DatagramSocket datagramSocket=new DatagramSocket(6666);

        //2. 準備數據


        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();

        //封裝數據
        DataOutputStream dataOutputStream=new DataOutputStream(new BufferedOutputStream(byteArrayOutputStream));

        dataOutputStream.writeUTF("兩個蝴蝶飛");

        dataOutputStream.writeInt(24);

        dataOutputStream.writeDouble(95.5);

        dataOutputStream.writeChar('男');

        dataOutputStream.writeUTF("一個快樂的程序員");

        //一定不要忘記刷新
        dataOutputStream.flush();


        //3. 將數據轉換成字節數據

        byte[] bytes=byteArrayOutputStream.toByteArray();

        //   System.out.println("發送數據爲:"+new String(bytes));

        //4. 封裝進包
        InetSocketAddress inetSocketAddress=new InetSocketAddress("localhost",9999);

        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length,inetSocketAddress);


        datagramSocket.send(datagramPacket);

        //關閉流
        datagramSocket.close();


    }

二.二.二 服務端

@Test
    public void send2Test() throws Exception{

        //1. 定義服務器
        System.out.println("---------------服務器啓動成功,接收多條數據----------------");

        DatagramSocket datagramSocket=new DatagramSocket(9999);

        //2. 設置數據

        byte[] bytes=new byte[1024];

        //能接收的大小,並不是實際接收的大小
        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length);


        //3. 阻塞式接收傳遞過來的數據
        datagramSocket.receive(datagramPacket);


        //4. 獲取接收的數據

        byte[] content=datagramPacket.getData();

        int len=datagramPacket.getLength();

       // System.out.println("數據是:"+ new String(content,0,content.length)+",長度是:"+len);

       //將數據放置到內存裏面

        ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(content);

        DataInputStream dataInputStream=new DataInputStream(new BufferedInputStream(byteArrayInputStream));

        //讀取數據

        String name=dataInputStream.readUTF();

        int age=dataInputStream.readInt();

        double score=dataInputStream.readDouble();

        char sex=dataInputStream.readChar();

        String desc=dataInputStream.readUTF();

        System.out.printf("姓名是:%s,年齡是:%d,成績是:%f,性別是:%c,描述是:%s",name,age,score,sex,desc);

        //5 關閉流

        datagramSocket.close();


    }

二.二.三 運行程序

運行服務器端, 後運行客戶端, 查看服務器端數據輸出:

有圖片

二.三 發送對象數據

有一個 Person 類, 還是具有 id,name,sex,age,desc 等屬性

二.三.一 客戶端


    @Test
    public void send3Test() throws Exception{
        //1. 定義客戶端
        System.out.println("------------發送方開始發送對象數據--------------");
        DatagramSocket datagramSocket=new DatagramSocket(6666);

        //2. 準備數據


        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();

        ObjectOutputStream objectOutputStream=new ObjectOutputStream(new BufferedOutputStream(byteArrayOutputStream));

       //放入對象
        Person person=new Person();
        person.setId(1);
        person.setName("兩個蝴蝶飛");
        person.setSex('男');
        person.setAge(24);
        person.setDesc("一個快樂的程序員");

        objectOutputStream.writeObject(person);
        //放置日期

        objectOutputStream.writeObject(new Date());

        //一定不要忘記刷新
        objectOutputStream.flush();


        //3. 將數據轉換成字節數據

        byte[] bytes=byteArrayOutputStream.toByteArray();

        //   System.out.println("發送數據爲:"+new String(bytes));

        //4. 封裝進包
        InetSocketAddress inetSocketAddress=new InetSocketAddress("localhost",9999);

        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length,inetSocketAddress);


        datagramSocket.send(datagramPacket);

        //關閉流
        datagramSocket.close();


    }

二.三.二 服務器端

  @Test
    public void send3Test() throws Exception{

        //1. 定義服務器
        System.out.println("---------------服務器啓動成功,接收對象數據----------------");

        DatagramSocket datagramSocket=new DatagramSocket(9999);

        //2. 設置數據

        byte[] bytes=new byte[1024];

        //能接收的大小,並不是實際接收的大小
        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length);


        //3. 阻塞式接收傳遞過來的數據
        datagramSocket.receive(datagramPacket);


        //4. 獲取接收的數據

        byte[] content=datagramPacket.getData();

        int len=datagramPacket.getLength();



        //將數據放置到內存裏面

        ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(content);

        ObjectInputStream objectInputStream=new ObjectInputStream(new BufferedInputStream(byteArrayInputStream));

        //讀取數據

        Object obj1=objectInputStream.readObject();

        if(obj1 instanceof Person){

            Person person=(Person)obj1;

            System.out.println(person.toString());

        }else{
            System.out.println("接收格式有誤");
        }

        Object obj2=objectInputStream.readObject();

        if(obj2 instanceof Date){

            SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");

            System.out.println("日期:"+sdf.format((Date)obj2));


        }else{
            System.out.println("接收格式有誤");
        }
        //5 關閉流

        datagramSocket.close();


    }

二.三.三 運行程序

運行服務器端, 後運行客戶端, 查看服務器端數據輸出:

有圖片

二.四 發送圖片文件

### 二.四.一 文件工具類 IOUtils

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 *1、 圖片讀取到字節數組
 *2、 字節數組寫出到文件
 *  @author 兩個蝴蝶飛
 *
 */
public class IOUtils {
	/**
	 * 1、文件,一般爲圖片 讀取到字節數組
	 * 1)、圖片到程序  FileInputStream
	 * 2)、程序到字節數組	ByteArrayOutputStream
	 */
	public static byte[] fileToByteArray(String filePath) {
		//1、創建源與目的地
		File src = new File(filePath);
		byte[] dest =null;
		//2、選擇流
		InputStream  is =null;
		ByteArrayOutputStream baos =null;
		try {
			is =new FileInputStream(src);
			baos = new ByteArrayOutputStream();
			//3、操作 (分段讀取)
			byte[] flush = new byte[1024*10]; //緩衝容器
			int len = -1; //接收長度
			while((len=is.read(flush))!=-1) {
				baos.write(flush,0,len);		 //寫出到字節數組中			
			}		
			baos.flush();
			return baos.toByteArray();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//4、釋放資源
			try {
				if(null!=is) {
					is.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;		
	}
	/**
	 * 2、字節數組寫入到文件
	 * 1)、字節數組到程序 ByteArrayInputStream
	 * 2)、程序到文件 FileOutputStream
	 */
	public static void byteArrayToFile(byte[] src,String filePath) {
		//1、創建源
		File dest = new File(filePath);
		//2、選擇流
		InputStream  is =null;
		OutputStream os =null;
		try {
			is =new ByteArrayInputStream(src);
			os = new FileOutputStream(dest);
			//3、操作 (分段讀取)
			byte[] flush = new byte[5]; //緩衝容器
			int len = -1; //接收長度
			while((len=is.read(flush))!=-1) {
				os.write(flush,0,len);			//寫出到文件	
			}		
			os.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//4、釋放資源
			try {
				if (null != os) {
					os.close();
				} 
			} catch (Exception e) {
			}
		}
	}
}

二.四.二 客戶端


    @Test
    public void send4Test() throws Exception{
        //1. 定義客戶端
        System.out.println("------------發送方開始發送圖片文件數據--------------");
        DatagramSocket datagramSocket=new DatagramSocket(6666);

        //2. 準備數據


        //3. 將數據轉換成字節數據

        String path="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
                +File.separator+"129.png";

        byte[] bytes= IOUtils.fileToByteArray(path);

        //   System.out.println("發送數據爲:"+new String(bytes));

        //4. 封裝進包
        InetSocketAddress inetSocketAddress=new InetSocketAddress("localhost",9999);

        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length,inetSocketAddress);


        datagramSocket.send(datagramPacket);

        //關閉流
        datagramSocket.close();

    }

二.四.三 服務器端

@Test
    public void send4Test() throws Exception{

        //1. 定義服務器
        System.out.println("---------------服務器啓動成功,接收圖片文件數據----------------");

        DatagramSocket datagramSocket=new DatagramSocket(9999);

        //2. 設置數據

        byte[] bytes=new byte[1024*20];

        //能接收的大小,並不是實際接收的大小
        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length);


        //3. 阻塞式接收傳遞過來的數據
        datagramSocket.receive(datagramPacket);


        //4. 獲取接收的數據

        byte[] content=datagramPacket.getData();

        int len=datagramPacket.getLength();



       String path="E:"+ File.separator+"ideaWork"+File.separator+"Java2"+File.separator+"fileSrc"
               +File.separator+"129Udp.png";

        IOUtils.byteArrayToFile(content,path);
        //5 關閉流

        datagramSocket.close();


    }

二.四.四 運行程序

運行服務器端, 後運行客戶端, 查看文件系統

有圖片

二.五 控制檯輸入單條數據

二.五.一 客戶端

 public static void main(String[] args) {
        try {
           send5Test();
            //send6Test();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void send5Test() throws Exception{
        //1. 定義客戶端
        System.out.println("------------發送方開始發送控制檯打印數據--------------");
        DatagramSocket datagramSocket=new DatagramSocket(6666);

        //2. 準備數據

        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));

        String content=bufferedReader.readLine();

        byte[] bytes=content.getBytes("UTF-8");
        //   System.out.println("發送數據爲:"+new String(bytes));

        //4. 封裝進包
        InetSocketAddress inetSocketAddress=new InetSocketAddress("localhost",9999);

        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length,inetSocketAddress);


        datagramSocket.send(datagramPacket);

        //關閉流
        datagramSocket.close();

    }

二.五.二 服務器端

@Test
    public void send5Test() throws Exception{

        //1. 定義服務器
        System.out.println("---------------服務器啓動成功,接收控制檯數據----------------");

        DatagramSocket datagramSocket=new DatagramSocket(9999);

        //2. 設置數據

        byte[] bytes=new byte[1024];

        //能接收的大小,並不是實際接收的大小
        DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length);


        //3. 阻塞式接收傳遞過來的數據
        datagramSocket.receive(datagramPacket);


        //4. 獲取接收的數據

        byte[] content=datagramPacket.getData();

        int len=datagramPacket.getLength();


        System.out.println("接收的內容是:"+new String(content,0,len));

        datagramSocket.close();


    }

二.五.三 運行程序

先運行服務器端,

有圖片

運行客戶端,輸入要打印的數據

有圖片

再次查看服務器端

有圖片

二.六 控制檯輸入多條數據

需要用 循環進行創建。

二.六.一 客戶端

public static void send6Test() throws Exception{
        //1. 定義客戶端
        System.out.println("------------發送方開始發送控制檯多條數據--------------");
        DatagramSocket datagramSocket=new DatagramSocket(6666);

        //2. 準備數據

        BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(System.in));

        while(true){
            String content=bufferedReader.readLine();

            byte[] bytes=content.getBytes("UTF-8");
            //   System.out.println("發送數據爲:"+new String(bytes));

            //4. 封裝進包
            InetSocketAddress inetSocketAddress=new InetSocketAddress("localhost",9999);

            DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length,inetSocketAddress);


            datagramSocket.send(datagramPacket);

            if("bye".equalsIgnoreCase(content)||"quit".equalsIgnoreCase(content)){

                System.out.println("退出程序");
                break;
            }

        }

        //關閉流
        datagramSocket.close();

    }

二.六.二 服務器端

 @Test
    public void send6Test() throws Exception{

        //1. 定義服務器
        System.out.println("---------------服務器啓動成功,接收控制檯多條數據----------------");

        DatagramSocket datagramSocket=new DatagramSocket(9999);

        //2. 設置數據

        byte[] bytes=new byte[1024];


        while(true){
            //能接收的大小,並不是實際接收的大小
            DatagramPacket datagramPacket=new DatagramPacket(bytes,0,bytes.length);


            //3. 阻塞式接收傳遞過來的數據
            datagramSocket.receive(datagramPacket);


            //4. 獲取接收的數據

            byte[] content=datagramPacket.getData();

            int len=datagramPacket.getLength();


            String temp=new String(content,0,len);

            if("bye".equalsIgnoreCase(temp)||"quit".equalsIgnoreCase(temp)){

                break;
            }
            System.out.println("echo:"+temp);

        }
        datagramSocket.close();


    }

二.六.三 運行程序

運行服務器,再運行客戶端

有圖片

服務器端

有圖片


謝謝您的觀看,如果喜歡,請關注我,再次感謝 !!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章