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();


    }

二.六.三 运行程序

运行服务器,再运行客户端

有图片

服务器端

有图片


谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!

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