• Java网络编程的使用


    InetAddress

    了解了IP地址的作用,我们看学习下JDK中提供了一个InetAdderss类,该类用于封装一个IP地址,并提供了一系列与IP地址相关的方法,下表中列出了InetAddress类的一些常用方法。

    方法名 说明
    static InetAddress getByName(String host) 在给定主机名的情况下确定主机的IP地址。
    static InetAddress getLocalHost() 返回本地主机。
    String getHostName() 获取此IP的主机名。
    String getHostAddress() 返回IP地址字符串(以文本的形式表现)。

    上图中,列举了InetAddress的四个常用方法。其中,前两个方法用于获得该类的实例对象,第一个方法用于获得表示指定主机的InetAddress对象,第二个方法用于获得表示本地的InetAddress对象。通过InetAddress对象便可获取指定主机名,IP地址等,接下来通过一个案例来演示InetAddress的常用方法,如下所示。

    package cn.last.demo;
    
    import java.net.InetAddress;
    public class SpringTestApplicationTests {
        public static void main(String[] args) throws Exception {
            InetAddress local = InetAddress.getLocalHost();
            InetAddress remote = InetAddress.getByName("mebius.online");
            System.out.println("本机的IP地址:" + local.getHostAddress());
            System.out.println("本机的主机名为:" + local.getHostName());
            System.out.println("mebius的IP地址:" + remote.getHostAddress());
            System.out.println("mebius的主机名为:" + remote.getHostName());
        }
    }
    

    UDP通信

    DatagramPacket

    前面介绍了UDP是一种面向无连接的协议,因此,在通信时发送端和接收端不用建立连接。UDP通信的过程就像是货运公司在两个码头间发送货物一样。在码头发送和接收货物时都需要使用集装箱来装载货物,UDP通信也是一样,发送和接收的数据也需要使用“集装箱”进行打包,为此JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据。

    想要创建一个DatagramPacket对象,首先需要了解一下它的构造方法。在创建发送端和接收端的DatagramPacket对象时,使用的构造方法有所不同,接收端的构造方法只需要接收一个字节数组来存放接收到的数据,而发送端的构造方法不但要接收存放了发送数据的字节数组,还需要指定发送端IP地址和端口号。

    构造方法:

    方法名 说明
    DatagramPacket(byte[] buf, int length) 构造DatagramPacket,用来接收长度为length的数据包。
    DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据包,用来将长度为length的包发送到指定主机的指定端口号。

    常用方法:

    方法名 说明
    InetAddress getAddress() 返回某台机器的IP地址,此数据报要发往该机器或是从该机器接收到的。
    int getPort() 返回某台远程主机的端口号,此数据将要发往该主机或是从该主机接受到的。
    byte[] getData() 返回数据缓冲区。
    int getLength() 返回将要发送或接收到的数据的长度。

    DatagramSocket

    DatagramPacket数据包的作用就如同是“集装箱”,可以将发送端或者接收端的数据封装起来。然而运输货物只有“集装箱”是不够的,还需要有码头。在程序中需要实现通信只有DatagramPacket数据包也同样不行,为此JDK中提供的一个DatagramSocket类。DatagramSocket类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包,发送数据的过程如下图所示。

    构造方法:

    方法名 说明
    DatagramSocket() 构造数据报套接字并将其绑定到主机的任何可用的端口。
    DatagramSocket(int port) 创建数据报套接字并将其绑定到本地主机的指定端口。

    常用方法:

    方法名 说明
    void receive(DatagramPacket p) 从套接字接收数据报包。
    void send(DataGramPacket p) 从套接字发送数据报包。

    UDP网络程序

    UDP发送
    package cn.last.demo;
    
    /*
     * 发送端
     * 1,创建DatagramSocket对象
     * 2,创建DatagramPacket对象,并封装数据
     * 3,发送数据
     * 4,释放流资源
     */
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    
    public class SpringTestApplicationTests {
        public static void main(String[] args) throws IOException {
            //1,创建DatagramSocket对象
            DatagramSocket sendSocket = new DatagramSocket();
            //2,创建DatagramPacket对象,并封装数据
            //public DatagramPacket(byte[] buf, int length, InetAddress address, int port)
            //构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
            byte[] buffer = "hello,UDP".getBytes();
            DatagramPacket dp = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("192.168.3.40"), 12306);
            //3,发送数据
            //public void send(DatagramPacket p) 从此套接字发送数据报包
            sendSocket.send(dp);
            //4,释放流资源
            sendSocket.close();
        }
    }
    
    UDP接收
    package cn.last.demo;
    /*
     * UDP接收端
     *
     * 1,创建DatagramSocket对象
     * 2,创建DatagramPacket对象
     * 3,接收数据存储到DatagramPacket对象中
     * 4,获取DatagramPacket对象的内容
     * 5,释放流资源
     */
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    
    public class Test2 {
        public static void main(String[] args) throws IOException {
            //1,创建DatagramSocket对象,并指定端口号
            DatagramSocket receiveSocket = new DatagramSocket(12306);
            //2,创建DatagramPacket对象, 创建一个空的仓库
            byte[] buffer = new byte[1024];
            DatagramPacket dp = new DatagramPacket(buffer, 1024);
            //3,接收数据存储到DatagramPacket对象中
            receiveSocket.receive(dp);
            //4,获取DatagramPacket对象的内容
            //谁发来的数据 getAddress()
            InetAddress ipAddress = dp.getAddress();
            String ip = ipAddress.getHostAddress();//获取到了IP地址
            //发来了什么数据 getData()
            byte[] data = dp.getData();
            //发来了多少数据 getLength()
            int length = dp.getLength();
            //显示收到的数据
            String dataStr = new String(data, 0, length);
            System.out.println("IP地址:" + ip + "数据是" + dataStr);
            //5,释放流资源
            receiveSocket.close();
        }
    }
    

    TCP通信

    TCP通信同UDP通信一样,都能实现两台计算机之间的通信,通信的两端都需要创建socket对象。

    区别在于,UDP中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据。而TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接。

    在JDK中提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端,一个是Socket类,用于表示客户端。

    通信时,首先创建代表服务器端的ServerSocket对象,该对象相当于开启一个服务,并等待客户端的连接,然后创建代表客户端的Socket对象向服务器端发出连接请求,服务器端响应请求,两者建立连接开始通信。

    ServerSocket

    方法名 说明
    ServerSocket(int port) 创建绑定到特定端口的服务器套接字。

    常用方法:

    方法名 说明
    Scoket accept() 侦听并接受此套接字的连接。
    InetAdderss getAddress() 返回此服务器套接字的本地地址。

    ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行。

    Socket

    构造方法:

    方法名 说明
    Scoket() 通过系统默认类型的SocketImpl创建未连接套接字。
    Scoket(String host, int port) 创建一个流套接字并将其连接到指定主机的指定端口号。
    Scoket(InetAddress address, int port) 创建一个流套接字并将其连接到指定IP地址的指定端口号。

    常用方法:

    方法名 说明
    int getPort() 该方法返回一个int类型对象,该对象是Socket对象与服务器端连接的端口号。
    InetAddress getLocalAddress() 该方法用于获取Socket对象绑定的本地IP地址,并将IP地址封装成InetAddress类型的对象返回。
    void close() 该方法用于关闭Socket连接,结束本次通信。在关闭socket之前,应将与socket相关的所有的输入/输出流全部关闭,这是因为一个良好的程序应该在执行完毕时释放所有的资源。
    InputStream getInputStream() 该方法返回一个InputStream类型的输入流对象,如果该对象是由服务器端的Socket返回,就用于读取客户端发送的数据,反之,用于读取服务器端发送的数据。
    OutputStream getOutputStream() 该方法返回一个OutputStream类型的输出流对象,如果该对象是由服务器端的Socket返回,就用于向客户端发送数据,反之,用于向服务器端发送数据。

    简单的TCP网络程序

    服务端:
    package cn.last.demo;
    
    /*
     * TCP 服务器端
     *
     * 1,创建服务器ServerSocket对象(指定服务器端口号)
     * 2,开启服务器了,等待客户端的连接,当客户端连接后,可以获取到连接服务器的客户端Socket对象
     * 3,给客户端反馈信息
     * 4,关闭流资源
     */
    
    import java.io.IOException;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class SpringTestApplicationTests {
        public static void main(String[] args) throws IOException {
            //1,创建服务器ServerSocket对象(指定服务器端口号)
            ServerSocket ss = new ServerSocket(8888);
            //2,开启服务器了,等待客户端的连接,当客户端连接后,可以获取到连接服务器的客户端Socket对象
            Socket s = ss.accept();
            //3,给客户端反馈信息
            /*
             * a,获取客户端的输出流
             * b,在服务端端,通过客户端的输出流写数据给客户端
             */
            //a,获取客户端的输出流
            OutputStream out = s.getOutputStream();
            //b,在服务端端,通过客户端的输出流写数据给客户端
            out.write("你已经连接上了服务器".getBytes());
            //4,关闭流资源
            out.close();
            s.close();
            //ss.close(); 服务器流 通常都是不关闭的
        }
    }
    
    客户端
    package cn.last.demo;
    
    /*
     * TCP 客户端
     *
     * 1,创建客户端Socket对象,(指定要连接的服务器地址与端口号)
     * 2,获取服务器端的反馈回来的信息
     * 3,关闭流资源
     */
    
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.Socket;
    
    public class Test2 {
        public static void main(String[] args) throws IOException {
            //1,创建客户端Socket对象,(指定要连接的服务器地址与端口号)
            Socket s = new Socket("192.168.3.40", 8888);
            //2,获取服务器端的反馈回来的信息
            InputStream in = s.getInputStream();
            //获取获取流中的数据
            byte[] buffer = new byte[1024];
            //把流中的数据存储到数组中,并记录读取字节的个数
            int length = in.read(buffer);
            //显示数据
            System.out.println(new String(buffer, 0, length));
            //3,关闭流资源
            in.close();
            s.close();
        }
    }
    

    文件多线程上传案例

    服务端:
    package cn.last.demo;
    
    import java.io.BufferedOutputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /*
     * 文件上传多线程版本, 服务器端
     */
    
    public class SpringTestApplicationTests {
        public static void main(String[] args) throws IOException {
            //1,创建服务器,等待客户端连接
            ServerSocket serverSocket = new ServerSocket(8888);
            //实现多个客户端连接服务器的操作
            while (true) {
                final Socket clientSocket = serverSocket.accept();
                //启动线程,完成与当前客户端的数据交互过程
                new Thread(() -> {
                    try {
                        //显示哪个客户端Socket连接上了服务器
                        InetAddress ipObject = clientSocket.getInetAddress();//得到IP地址对象
                        String ip = ipObject.getHostAddress(); //得到IP地址字符串
                        System.out.println("小样,抓到你了,连接我!!" + "IP:" + ip);
                        //7,获取Socket的输入流
                        InputStream in = clientSocket.getInputStream();
                        //8,创建目的地的字节输出流 D:\upload\192.168.74.58(1).jpg
                        BufferedOutputStream fileOut = new BufferedOutputStream(new FileOutputStream(System.currentTimeMillis() + ".png"));
                        //9,把Socket输入流中的数据,写入目的地的字节输出流中
                        byte[] buffer = new byte[1024];
                        int len = -1;
                        while ((len = in.read(buffer)) != -1) {
                            //写入目的地的字节输出流中
                            fileOut.write(buffer, 0, len);
                        }
    
                        //-----------------反馈信息---------------------
                        //10,获取Socket的输出流, 作用:写反馈信息给客户端
                        OutputStream out = clientSocket.getOutputStream();
                        //11,写反馈信息给客户端
                        out.write("图片上传成功".getBytes());
                        out.close();
                        fileOut.close();
                        in.close();
                        clientSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }).start();
            }
            //serverSocket.close();
        }
    }
    
    客户端:
    package cn.last.demo;
    
    import java.io.BufferedInputStream;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;
    
    /*
     * 文件上传 客户端
     *
     * public void shutdownOutput() 禁用此Socket的输出流,间接的相当于告知了服务器数据写入完毕
     */
    public class SpringTestApplicationTests {
        public static void main(String[] args) throws IOException {
            //2,创建客户端Socket,连接服务器
            Socket socket = new Socket("192.168.1.67", 8888);
            //3,获取Socket流中的输出流,功能:用来把数据写到服务器
            OutputStream out = socket.getOutputStream();
            //4,创建字节输入流,功能:用来读取数据源(图片)的字节
            BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream("dowload.png"));
            //5,把图片数据写到Socket的输出流中(把数据传给服务器)
            byte[] buffer = new byte[1024];
            int len = -1;
            while ((len = fileIn.read(buffer)) != -1) {
                //把数据写到Socket的输出流中
                out.write(buffer, 0, len);
            }
            //6,客户端发送数据完毕,结束Socket输出流的写入操作,告知服务器端
            socket.shutdownOutput();
            //-----------------反馈信息---------------------
            //12,获取Socket的输入流 作用: 读反馈信息
            InputStream in = socket.getInputStream();
            //13,读反馈信息
            byte[] info = new byte[1024];
            //把反馈信息存储到info数组中,并记录字节个数
            int length = in.read(info);
            //显示反馈结果
            System.out.println(new String(info, 0, length));
            //关闭流
            in.close();
            fileIn.close();
            out.close();
            socket.close();
        }
    }
    
  • 相关阅读:
    pix-PID介绍
    我的四轴专用PID参数整定方法及原理---超长文慎入(转)
    卡尔曼滤波原理
    5种常用的四轴飞行器PID算法讲解集合
    深入浅出无人机姿态,欧拉角,四元数,指数表示及数据转换与程序实现
    理解四元数
    Normal Equation(正规方程)
    特征与多项式回归
    实际梯度下降中的两个重要调节方面
    BZOJ 3624 Apio2008 免费道路
  • 原文地址:https://www.cnblogs.com/SnowPrince/p/14387875.html
Copyright © 2020-2023  润新知