上帝视角学JAVA- 基础18-网络编程【2021-09-07】

1、网络编程

1、如何定位网络上的主机?

IP 地址: 定位主机位置

端口号: 主机上的应用程序。电脑上很多程序,比如微信,qq,浏览器等。这个用来定位到底是那一个程序来通信。

2、定位之后如何进行数据传输?

一定的规则。OSI 参考模型或者TCP/IP 协议

TCP/IP 协议 是事实上的协议,OSI 没有在英特网上广泛应用。

协议是对 速度、传输代码、代码结构、传输控制步骤、出错控制等制定标准

制定协议时,采用协议分层思想,把复杂的问题划分为简单的多个成分,最后在组合起来。

最常用组合方式是层次方式,即同层之间可以通信、上一层可以调用下一层,不能调用下一层的下一层。各层之间互不影响。

 

1.1 IP与端口号

IP地址:java 有一个 InetAddress 类 来表示。

唯一标识Internet 上的计算机。

本地回环地址 hostAddress:127.0.0.1

本地主机名:localhost

IP地址分类: IPV4、IPV6

IPV4:4个字节组成, 4个 0-255 大概42亿。 30亿在北美,亚洲4亿 2011年初用尽。以点隔开,十进制表示

,如:192.168.0.1

IPV6:16个字节表示,共128位,写成8个无符号整数,每个整数用4个十六进制位表示,用冒号分开。

如:3ff3:3201:1401:1280:c8ff:fe4d:db39:1984

分类方式2:公网地址、私有地址。

192.168.xxx.xxx 是私有地址。范围 192.168.0.0 - 192.168.255.255 为机构内部使用。

公网地址就是英特网使用。

InetAddress 类是 JDK4 开始引入。这个类构造器是私有的,只能通过静态方法获取对象。

try {
    InetAddress byName = InetAddress.getByName("127.0.0.1");
    System.out.println(byName); // /127.0.0.1
} catch (UnknownHostException e) {
    e.printStackTrace();
}

上面是使用 getByName 得到 InetAddress 对象。传入的一个ip地址。还可以传入 域名

try {
    InetAddress byName = InetAddress.getByName("localhost");
    System.out.println(byName); // localhost/127.0.0.1
} catch (UnknownHostException e) {
    e.printStackTrace();
}

可以使用 getLocalHost 方法获取本机的ip地址。注意:一个主机可以有多个不同的ip。

try {
    InetAddress byName = InetAddress.getLocalHost();
    System.out.println(byName); // xgz/192.168.56.1
} catch (UnknownHostException e) {
    e.printStackTrace();
}

我的电脑在局域网的ip是 192.168.56.1 同时,127.0.0.1 也是指我的电脑ip。

端口号:用来标识正在计算机上运行的进程(程序)

不同的进程有不同的端口号。端口号是一个16位的整数,范围为0-65535

端口号分类:

0-1023 被预先定义的服务占用。比如 http 占用80端口,ftp占用21端口,teinet占用23

1024-49151: 分配给用户进程或程序。就是我们自己可以自定义占用的端口。

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

端口号与IP的组合得出一个网络套接字:socket

1.2 网络协议

ip和端口号 的作用是定位需要传输的对象。找到了需要传输的对象还需要知道怎么传输。

传输就需要用到协议,TCP/IP协议簇。

TCP (transmission Control Protocol)传输控制协议

IP(Internet Protocol) 网络互联协议,是网络层的主要协议。支持网间互联数据通信。

TCP/IP 协议模型形成了四层体系结构,物理链路层、IP层、传输层、应用层。

传输过程有2个非常重要的协议:TCP、UDP协议

  • TCP 协议:

使用TCP协议前,需要建立TCP连接,形成传输数据通道。

传输前,采用三次握手,点对点通信

TCP协议通信的两个进程 客户端、服务端

连接成功后可以进行大数据量传输

传输完毕后,需要释放已建立的连接。

 

1、3是客户端,需要进行通信的人,2是服务端,接收信息的人。

 

  • UDP 协议:

    将数据、源、目的封装成数据包,不需要建立连接

    每个数据包的大小限制在64K内

    发送不管对方是否准备好,接收方收到也不确认。是不可靠的

    可以进行广播发送

    发送结束后无需释放资源,开销小,速度快。

TCP协议类似于打电话,必须先拨号,当对方收到铃声并接通电话,说一声 喂喂喂,才确认通信成功。可以交流。

如果网络有问题,会说,喂喂喂你还在吗?收不到回应就会挂电话,收到了才会继续通信。

UDP就像是 村里面的广播,不管你有没有准备好,也不管你有没有听到。播完就算了。

1.3 TCP 网络编程

1.3.1 客户端发送消息给服务端

以简单的例子看看是怎么用的。

1、先写服务端。

   @Test
    public void test2() {
        // 服务端 接收数据并打印
        ServerSocket serverSocket = null;
        InputStream ips = null;
        ByteArrayOutputStream baos = null;
        Socket client = null;
        try {
            serverSocket= new ServerSocket(20000);
            // 1、 接收客户端 套接字。 accept是阻塞方法。
            client = serverSocket.accept();
            // 2、客户端套接字获取 输入流
            ips = client.getInputStream();
            // 3、使用 ByteArrayOutputStream 流,来写数据,写到ByteArrayOutputStream内部的字节数组。不够会自动扩容。
            // 可以全部接收完客户端发送的数据。最后再转字符串,避免出现乱码问题
            baos = new ByteArrayOutputStream();
            int len;
            byte[] buf = new byte[1024];
            while ((len=ips.read(buf)) != -1){
                baos.write(buf, 0 , len);
            }
            System.out.println(baos);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (baos != null){
                    baos.close();
                }
                if (client!= null){
                    client.close();
                }
                if (ips != null){
                    ips.close();
                }
                if (serverSocket != null){
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
    }

2、再写客户端

    
@Test
    public void test1() {
        // 客户端 发送数据给服务端
        OutputStream ops = null;
        Socket socket = null;
        try {
            // 1、ip信息
            InetAddress inet = InetAddress.getByName("127.0.0.1");
            // 2、创建 包含 ip和端口的套接字对象
            socket = new Socket(inet, 20000);
            // 3、获取输出流
            ops = socket.getOutputStream();
            // 4、写数据
            ops.write("你好,我是客户端".getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5、关闭资源
            try {
                if (ops != null){
                    ops.close();
                }
                if (socket != null){
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

先启动服务端,再启动客户端即可测试。

1.3.2 客户端发送文件给服务端

  • 以上只是客户端发送消息给服务端。再来看看客户端发送文件给服务端。服务端进行保存

  
 @Test
    public void test2() {
        // 服务端 接收数据并打印
        ServerSocket serverSocket = null;
        InputStream ips = null;
        FileOutputStream fos = null;
        Socket client = null;
        try {
            serverSocket= new ServerSocket(20000);
            // 1、 接收客户端 套接字
            client = serverSocket.accept();
            // 2、客户端套接字获取 输入流
            ips = client.getInputStream();
            // 3、使用 ByteArrayOutputStream 流,来写数据,写到ByteArrayOutputStream内部的字节数组。不够会自动扩容。
            // 可以全部接收完客户端发送的数据。最后再转字符串,避免出现乱码问题
            fos = new FileOutputStream("server_java.jpg");
            int len;
            byte[] buf = new byte[1024];
            while ((len=ips.read(buf)) != -1){
                fos.write(buf, 0 , len);
            }
​
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null){
                    fos.close();
                }
                if (client!= null){
                    client.close();
                }
                if (ips != null){
                    ips.close();
                }
                if (serverSocket != null){
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
    }

再写客户端:

   
 @Test
    public void test1() {
        // 客户端 发送数据给服务端
        OutputStream ops = null;
        Socket socket = null;
        FileInputStream fi = null;
        try {
            // 1、创建 包含 ip和端口的套接字对象
            socket = new Socket(InetAddress.getByName("127.0.0.1"), 20000);
            // 2、获取输出流
            ops = socket.getOutputStream();
            // 3、写数据
            // 要发送文件,先要读进内存。
            fi = new FileInputStream("java.jpg");
            byte[] buf =  new byte[1024];
            int len;
            while ((len = fi.read(buf)) != -1){
                // 写出去
                ops.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5、关闭资源
            try {
                if (fi != null){
                    fi.close();
                }
                if (ops != null){
                    ops.close();
                }
                if (socket != null){
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

也是先运行服务端,再运行服务端。其实与消息的发送没有什么区别。

1.3.2 客户端发送文件给服务端,服务端发送收到了给客户端

前面都是例子都只是单向的通信,客户端发送给服务端。服务端没有发送给客户端。事实上是可以的

@Test
    public void test2() {
        // 服务端 接收数据并打印
        ServerSocket serverSocket = null;
        InputStream ips = null;
        FileOutputStream fos = null;
        Socket client = null;
        try {
            serverSocket= new ServerSocket(20000);
            // 1、 接收客户端 套接字
            client = serverSocket.accept();
            // 2、客户端套接字获取 输入流
            ips = client.getInputStream();
            // 3、使用 ByteArrayOutputStream 流,来写数据,写到ByteArrayOutputStream内部的字节数组。不够会自动扩容。
            // 可以全部接收完客户端发送的数据。最后再转字符串,避免出现乱码问题
            fos = new FileOutputStream("server_java.jpg");
            int len;
            byte[] buf = new byte[1024];
            while ((len=ips.read(buf)) != -1){
                fos.write(buf, 0 , len);
            }
            // 4、 反馈给客户端
            OutputStream os = client.getOutputStream();
            os.write("我收到了,非常好!".getBytes(StandardCharsets.UTF_8));
​
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (fos != null){
                    fos.close();
                }
                if (client!= null){
                    client.close();
                }
                if (ips != null){
                    ips.close();
                }
                if (serverSocket != null){
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
​
    }

再写客户端:

 @Test
    public void test1() {
        // 客户端 发送数据给服务端
        OutputStream ops = null;
        Socket socket = null;
        FileInputStream fi = null;
        ByteArrayOutputStream baos = null;
        try {
            // 1、创建 包含 ip和端口的套接字对象
            socket = new Socket(InetAddress.getByName("127.0.0.1"), 20000);
            // 2、获取输出流
            ops = socket.getOutputStream();
            // 3、写数据
            // 要发送文件,先要读进内存。
            fi = new FileInputStream("java.jpg");
            byte[] buf =  new byte[1024];
            int len;
            while ((len = fi.read(buf)) != -1){
                // 写出去
                ops.write(buf, 0, len);
            }
            // 关闭发送,表示这一次传输完成
            socket.shutdownOutput();
            // 4、 接收 客户端的反馈
            InputStream is = socket.getInputStream();
            baos = new ByteArrayOutputStream();
            byte[] buff =  new byte[1024];
            int len1;
            while ((len1 = is.read(buff)) != -1){
                baos.write(buff, 0, len1);
            }
            System.out.println(baos);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5、关闭资源
            try {
                if (baos != null){
                    baos.close();
                }
                if (fi != null){
                    fi.close();
                }
                if (ops != null){
                    ops.close();
                }
                if (socket != null){
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

注意,客户端在发送图片之后,需要调用一下socket.shutdownOutput(); 表示传输结束,不然服务端不知道是否已传输完成。会阻塞。

启动还是先服务端,再客户端。

1.4 UDP 网络编程

发送端:

 @Test
    public void test1() {
        // 发送端 发送数据给服务端
        DatagramSocket socket = null;
        try {
            // 1、创建 UDP 套接字
            socket = new DatagramSocket();
            // 2、UDP的数据是放在 数据包里面。新建数据包
            String data = "UDP数据";
            byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
            InetAddress ip = InetAddress.getLocalHost();
            // 数据包 指定 要发送的数据,目标的ip,目标的端口
            DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, ip, 9090);

            // 2、发送数据
           socket.send(packet);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5、关闭资源
            if (socket != null){
                socket.close();
            }
        }
    }

接收端:

 @Test
    public void test2() {
        // 接收端 接收数据并打印
        DatagramSocket socket = null;
        try {
            // 1、 接收端也是 DatagramSocket, 需要指定接收的端口号。
            socket = new DatagramSocket(9090);
            // 2、 用buf来存数据
            byte[] buf =  new byte[1024];
            DatagramPacket packet = new DatagramPacket(buf, 0, buf.length);
            socket.receive(packet);
            // 3、输出一下
            System.out.println(new String(packet.getData(),0, packet.getLength()));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (socket != null){
                socket.close();
            }
        }

    }

UDP 没有服务端、客户端的概念。只有发送端和接收端。同一台主机,既可以是发送端,也可以是接收端。

上面的例子是一个充当发送,一个充当接收。先运行发送端不会报错,只是不能接收到数据。

要想接收到数据,还是要先运行接收端,再运行发送端。

1.5 URL 网络编程

URL (Uniform Resource Locator )统一资源定位地址

它表示Internet上某一个资源的地址。

URL 基本结构由 5个部分组成

传输协议://主机名:端口号/文件名#片段名?参数列表

比如下面这个:使用的是https协议。https是http的升级版。

https://fanyi.baidu.com/#en/zh/URL

URL 类

try {
    URL url = new URL("https://baike.baidu.com/item/URL%E6%A0%BC%E5%BC%8F/10056474?fr=aladdin");
    // 获取协议名
    System.out.println(url.getProtocol());
    // 获取主机名
    System.out.println(url.getHost());
    // 获取端口名
    System.out.println(url.getPort());
    // 获取文件路径
    System.out.println(url.getPath());
    // 获取文件名
    System.out.println(url.getFile());
    // 获取查询名
    System.out.println(url.getQuery());
} catch (MalformedURLException e) {
    e.printStackTrace();
}

// 输出
// https
// baike.baidu.com
// -1
// /item/URL%E6%A0%BC%E5%BC%8F/10056474
// /item/URL%E6%A0%BC%E5%BC%8F/10056474?fr=aladdin
// fr=aladdin

使用 URL 下载服务器资源 这里找的是百度上面的一张图片。可以自己在浏览器中打开看看。

https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.uc.cn%2Fs%2Fwemedia%2Fs%2Fupload%2F2018%2F424199fb84eb0adf50163cdd7ea424d3x1600x1000x104.jpeg&refer=http%3A%2F%2Fimage.uc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633240502&t=9e446fc4aef58c515ae03fbebe350e6d
@Test
public void test1() {
    FileOutputStream fo = null;
    InputStream is = null;
    HttpURLConnection conn = null;
    try {
        String tar = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimage.uc.cn%2Fs%2Fwemedia%2Fs%2Fupload%2F2018%2F424199fb84eb0adf50163cdd7ea424d3x1600x1000x104.jpeg&refer=http%3A%2F%2Fimage.uc.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1633240502&t=9e446fc4aef58c515ae03fbebe350e6d";
        URL url = new URL(tar);
        // 打开链接
        conn = (HttpURLConnection) url.openConnection();
        // 进行链接
        conn.connect();
        // 获取输入流
        is = conn.getInputStream();
        // 写到本地
        fo = new FileOutputStream("lyf.jpg");
        byte[] buf = new byte[1024];
        int len;
        while ((len = is.read(buf))!= -1){
            fo.write(buf, 0, len);
        }

    } catch (Exception e) {
        e.printStackTrace();
    }finally {
        try {
            if ( is != null){
                is.close();
            }
            if ( fo != null){
                fo.close();
            }
            if (conn != null) {
                conn.disconnect();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值