【Java SE】网络编程
阅读原文时间:2023年08月09日阅读:3

1. 网络编程概述

网络编程的目的:直接或者间接地通过网络协议与其他计算机实现数据交换,进行通讯。

网络编程两个主要的问题:

①如何精准地定位网络上的一台或多台主机,并定位主机上的特定应用

②找到主机后如何进行可靠高效的数据传输

1.1 网络通信要素

通信双方地址:

①IP地址(InetAddress):查找主机,对应问题一

唯一标识Internet上的计算机(通信实体)

本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost

127.0.0.1,通常被称为本地回环地址(Loopback Address),不属于任何一个有类别地址类。它代表设备的本地虚拟接口,所以默认被看作是永远不会宕掉的接口。在Windows操作系统中也有相似的定义,所以通常在安装网卡前就可以ping通这个本地回环地址。一般都会用来检查本地网络协议、基本数据接口等是否正常的。

IP地址分类方式一:

IPv4:4个字节,4个0-255,以点分十进制表示,如192.168.1.1

IPv6:16个字节128位,写成8个无符号整数,每个整数用4个十六进制位表示,数之间用:分开

IP地址分类方式二:

公网地址(万维网使用)和私有地址(局域网使用)

InetAddress

实例化方法

getByName("")

形参地址为IP或者域名

getLoopbackAddress()

本地回环地址的域名IP

getLocalHost()

主机网络域名IP

            InetAddress inetAddress = InetAddress.getByName("192.168.10.14");
            System.out.println(inetAddress);///192.168.10.14

            InetAddress inetAddress1 = InetAddress.getByName("www.bilibili.com");
            System.out.println(inetAddress1);//www.bilibili.com/120.92.78.97

            InetAddress inetAddress2 = InetAddress.getLocalHost();
            System.out.println(inetAddress2);//LAPTOP-B4BJHS06/192.168.137.1

            InetAddress inetAddress3 = InetAddress.getLoopbackAddress();
            System.out.println(inetAddress3);//localhost/127.0.0.1

实例方法

getHostName()

getHostAddress()

一定的规则,即网络通信协议:

①OSI参考模型(过于理想化)

②TCP/IP参考模型(或TCP/IP协议)

②端口号:标识主机上的应用程序,对应问题二

被规定为不同的16位整数0-65535

端口分类:

①公认端口:0-1023,被预定义的服务通信占用。如Http:80,FTP:21,Telnet:23

②注册端口:1024-49151,分配给用户进程和应用程序,如Tomcat:8080,MySQL:3306,Oracle:1521

③动态、私有端口:49152-65535

端口号与IP的组合为网络套接字:Socket

1.2 网络协议

也即是通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。同层之间可以通信、上一层可以调用下一层,而与再下一层不再发生关系。

1.2.1 TCP/IP协议簇

传输层包括TCP(传输层控制协议)和UDP(用户数据报协议)。

TCP协议:

使用TCP协议前,先建立TCP连接,形成数据传输通道;传输前,采用“三次握手”的方式,进行可靠的点对点通信;客户端和服务端两个应用进程在连接中进行大数据量的传输,传输结束需要释放已经建立的连接,效率低

TCP网络编程示例:Socket和ServerSocket

    @Test
    public void client() {
        InetAddress inet = null;
        Socket socket = null;
        OutputStream os = null;
        try {
            inet = InetAddress.getByName("10.42.138.5");
            socket = new Socket(inet, 8899);

            os = socket.getOutputStream();
            os.write("人类荣光永存!".getBytes(StandardCharsets.UTF_8));

        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(socket != null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(os != null)
                    os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println(inet);
    }


    @Test
    public void server() {
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        while(true) {
            try {
                serverSocket = new ServerSocket(8899);
                socket = serverSocket.accept();

                is = socket.getInputStream();
//            方式一,数组长度不足会乱码,UTF汉字占两个字节
//            int len;
//            byte[] buffer = new byte[5];
//            while((len = is.read(buffer)) != -1) {
//                String str = new String(buffer);
//                System.out.print(str);
//                System.out.println();
//            }
            /*
                人�
                �荣�
                ��永
                存�
                ����
             */

//            方式二:使用转换流将字节流转换为字符流
                InputStreamReader isr = new InputStreamReader(is);
                int len;
                char[] chs = new char[1];
                while ((len = isr.read(chs)) != -1) {
                    System.out.print(new String(chs, 0, len));
                }
                System.out.println();

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (serverSocket != null) {
                        serverSocket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (socket != null) {
                        socket.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    if (is != null) {
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

通过Socket传输图片

    @Test
    public void nib() {
        FileInputStream fis = null;
        Socket socket = null;
        OutputStream outputStream = null;

        try {
            File file = new File(".\\data\\2B\\2b.jpg");
//            System.out.println(file.getAbsolutePath());
            fis = new FileInputStream(file);

            InetAddress inetAddress = InetAddress.getByName("10.42.138.5");
            socket = new Socket(inetAddress, 8899);
            outputStream = socket.getOutputStream();

            int len;
            byte[] bytes = new byte[1024];
            while((len=fis.read(bytes))!=-1) {
                outputStream.write(bytes, 0, len);
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @Test
    public void kyuus() {
        FileOutputStream fs = null;
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        while(true) {
            try {
                serverSocket = new ServerSocket(8899);
                socket = serverSocket.accept();

                File file = new File(".\\data\\9S\\2b.jpg");
                fs = new FileOutputStream(file);

                is = socket.getInputStream();
                int len;
                byte[] bytes = new byte[1024];
                while ((len = is.read(bytes)) != -1) {
                    fs.write(bytes, 0, len);
                }

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fs.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

需要特别关注的是这一部分代码,file需要放在获取socket之后,否则会导致流无法写入

                serverSocket = new ServerSocket(8899);
                socket = serverSocket.accept();

                File file = new File(".\\data\\9S\\2b.jpg");
                fs = new FileOutputStream(file);

传输完成并回复消息:socket.shutdownOutput()

    @Test
    public void nib() {
        FileInputStream fis = null;
        Socket socket = null;
        OutputStream outputStream = null;

        try {
            File file = new File(".\\data\\2B\\2b.jpg");
//            System.out.println(file.getAbsolutePath());
            fis = new FileInputStream(file);

            InetAddress inetAddress = InetAddress.getByName("10.42.138.5");
            socket = new Socket(inetAddress, 8899);
            outputStream = socket.getOutputStream();

            int len;
            byte[] bytes = new byte[1024];
            while((len=fis.read(bytes))!=-1) {
                outputStream.write(bytes, 0, len);
            }

            socket.shutdownOutput();

            InputStream is = socket.getInputStream();
            byte[] bytes1 = new byte[1024];
            while((len=is.read(bytes1))!=-1) {
                System.out.println(new String(bytes1));
            }

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    @Test
    public void kyuus() {
        FileOutputStream fs = null;
        ServerSocket serverSocket = null;
        Socket socket = null;
        InputStream is = null;
        while(true) {
            try {

                File file = new File(".\\data\\9S\\2b.jpg");
                fs = new FileOutputStream(file);

                serverSocket = new ServerSocket(8899);
                socket = serverSocket.accept();

                is = socket.getInputStream();
                int len;
                byte[] bytes = new byte[1024];
                while ((len = is.read(bytes)) != -1) {
                    fs.write(bytes, 0, len);
                }

                is = socket.getInputStream();
                InputStreamReader isr = new InputStreamReader(is);
                char[] chs = new char[100];
                while((len=isr.read(chs))!=-1) {
                    System.out.println(new String(chs,0,len));
                }

                OutputStream os = socket.getOutputStream();
                os.write("传输完成".getBytes(StandardCharsets.UTF_8));

            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    fs.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

UDP协议:DatagramSocket和DatagramPacket

将数据、源、目的封装成数据报,不需要建立连接。每个数据包限制大小为64kb。由于不管对方是否准备好,接收方收到也不确认,因此是不可靠的。可以广播发送,发送数据结束时无序释放资源,开销小,速度快。适合传输一些视频。

UDP网络编程示例:

    @Test
    public void sender() {
        DatagramSocket socket = null;
        try {
            InetAddress inet = InetAddress.getByName("localhost");
            socket = new DatagramSocket();

            byte[] data = "我是UDP发送方".getBytes(StandardCharsets.UTF_8);
            DatagramPacket datagramPacket = new DatagramPacket(data, 0, data.length, inet, 8090);
            socket.send(datagramPacket);

        } catch (SocketException | UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

        }

    }

    @Test
    public void receiver() {
        try {
            InetAddress inet = InetAddress.getLocalHost();
            DatagramSocket socket = new DatagramSocket(8090);

            byte[] data = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(data, 0, data.length);

            socket.receive(datagramPacket);
            System.out.println(new String(datagramPacket.getData(), 0, datagramPacket.getLength()));

        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }

UDP与TCP的不同:

TCP必须是服务器端先启动,否则客户端先发送握手请求,没有响应便会断开TCP连接,无法再进行数据传送。而UDP是无连接协议,发送方发送数据不需要判断接收方是否接收,接收方如果后启动就会接收不到数据,但仍然能继续传送数据

IP协议是网络层的主要协议,支持网间互连的数据通信。

1.3 URL编程:统一资源定位符

常用方法

getProtocol()

协议名称

getPath()

资源路径

getHost()

主机域名

getPort()

端口号

getFile()

资源文件

getQuery()

关键字序列

URL url = new URL("http://localhost:8080/examples/beauty.jpg?username='admin'&pwd='123");
            System.out.println(url.getProtocol());
            System.out.println(url.getPort());
            System.out.println(url.getPath());
            System.out.println(url.getHost());
            System.out.println(url.getFile());
            System.out.println(url.getQuery());