Java笔记038-网络编程/网络的相关概念、InetAddress类、Socket、TCP网络通信编程、netstat指令、UDP网络通信编程、网络编程章节练习

目录

网络编程

网络的相关概念

网络通信

网络

IP地址

IPv4地址分类

域名

端口号 

网络通信协议

网络通信协议

 TCP和UDP

InetAddress类

相关方法

应用案例 

Socket

基本介绍

示意图

TCP网络通信编程

基本介绍

应用案例1

应用案例2(使用字节流)

应用案例3(使用字符流)

应用案例4

netstat指令

TCP网络通讯的特殊内容

UDP网络通信编程

基本介绍 

示意图

UDP说明:

基本流程

应用案例

网络编程章节练习

1.编程题

2.编程题

3.编程题


网络编程

网络的相关概念

  • 网络通信

  1. 概念:两台设备之间通过网络实现数据传输
  2. 网络通信:将数据通过网络从一台设备传输到另一台设备
  3. java.net包下提供了一系列的类或接口,供程序员使用,完成网络通信 

  • 网络

  1. 概念:两台或多台设备通过一定物理设备连接起来构成了网络
  2. 根据网络的覆盖范围不同,对网络进行分类:
  • 局域网:覆盖范围最小,仅仅覆盖一个教室或一个机房
  • 城域网:覆盖范围较大,可以覆盖一个城市
  • 广域网:覆盖范围最大,可以覆盖全国,甚至全球,万维网是广域网的代表

IP地址

  1. 概念:用于唯一标识网络中的每台计算机
  2. 查看IP地址:ipconfig
  3. IP地址的表示形式:点分十进制 xx.xx.xx.xx
  4. 每一个十进制数的范围:0~255
  5. IP地址的组成=网络地址+主机地址,比如:192.168.16.69
  6. IPv6是互联网工程任务组设计的用于替代IPv4的下一代IP协议,其数量号称可以为全世界的每一粒沙子编上一个地址
  7. 由于IPv4最大的问题在于网络地址资源有限,严重制约了互联网的应用和发展。IPv6的使用,不仅能解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍

IPv4地址分类

IPv4地址范围

类型

范围
A

0.0.0.0~127.255.255.255

B

128.0.0.0~191.255.255.255

C 192.0.0.0~223.255.255.255
D 224.0.0.0~239.255.255.255
E 240.0.0.0~255.255.255.255

注:只有A、B、C有网络号和主机号之分,D类地址和E类地址没有划分网络号和主机号。 

特殊的127.0.0.1表示本机地址

  • 域名

  1. www.baidu.com
  2. 好处:为了方便记忆,解决记IP的困难
  3. 概念:将IP地址映射成域名,HTTP协议
  • 端口号 

  1. 概念:用于标识计算机上某个特定的网络程序
  2. 表示形式:以整数形式,范围0~65535 [2个字节表示端口0~2^16-1]
  3. 0~1024已经被占用,比如:ssh 22,ftp 21,smtp 25,http 80
  4. 常见的网络程序端口号
    tomcat:8080
    mysql:3306
    oracle:1521
    sqlserver:1433

在网络开发中,不要使用0~1024的端口

  • 网络通信协议

  • 协议(TCP/IP)

TCP/IP(Transmission Control Protocol/Internet Protocol)的简写,中文译名为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是互联网网络的基础,简单的说,就是由网络层的IP协议和传输层的TCP协议组成的。

  • 网络通信协议

  •  TCP和UDP

  • TCP协议:传输控制协议
  1. 使用TCP协议前,须先建立TCP连接,形成传输数据通道
  2. 传输前,采用"三次握手"方式,是可靠的
  3. TCP协议进行通信的两个应用进程:客户端、服务端
  4. 在连接中可以进行大数据量的传输
  5. 传输完毕,需释放已建立的连接,效率低
  • UDP协议:
  1. 将数据、源、目的封装成数据包,不需要建立连接
  2. 每个数据报的大小限制在64K内
  3. 因无需连接,固是不可靠的
  4. 发送数据结束时无需释放资源(因为不是面向连接的),速度快

InetAddress类

  • 相关方法

  1. 获取本机InetAddress对象 getLocalHost
  2. 根据指定主机名/域名获取IP地址对象 getByName
  3. 获取InetAddress对象的主机名 getHostName
  4. 获取InetAddress对象的地址 getHostAddress
  • 应用案例 

package com21.api;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * @author 甲柒
 * @version 1.0
 * @title API_ 演示InetAddress类的使用
 * @package com21.api
 * @time 2023/4/6 19:57
 */
public class API_ {
    public static void main(String[] args) throws UnknownHostException {
        //1. 获取本机的InetAddress对象
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost);//DESKTOP-T6S0N27/192.168.56.1

        //2. 根据指定主机名 获取 InetAddress对象
        InetAddress host1 = InetAddress.getByName("DESKTOP-T6S0N27");
        System.out.println("host1 = " + host1);

        //3. 根据域名返回 InetAddress对象,比如 www.baidu.com
        InetAddress host2 = InetAddress.getByName("www.baidu.com");
        System.out.println("host2 = " + host2);//www.baidu.com/110.242.68.3

        //4. 通过InetAddress对象,获取对应的地址
        String hostAddress = host2.getHostAddress();// 110.242.68.3
        System.out.println("host2 对应的ip = " + hostAddress);// 110.242.68.3

        //5. 通过InetAddress对象,获取对应的主机名/或者域名
        String hostName = host2.getHostName();
        System.out.println("host2对应的主机名/域名 = " + hostName);
    }
}

Socket

  • 基本介绍

  1. 套接字(Socket)开发网络应用程序被广泛采用,以至于成为事实上的标准
  2. 通信的两端都要由Socket,是两台机器之间通信的端点
  3. 网络通信其实就是Socket间的通信
  4. Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输
  5. 一般主动发起通信的应用属于客户端,等待通信请求的为服务端
  • 示意图

TCP网络通信编程

  • 基本介绍

  1. 基于客户端-服务端的网络通信
  2. 底层使用的是TCP/IP协议
  3. 应用场景举例:客户端发送数据,服务器端接受并显示
  4. 基于Socket的TCP编程

  • 应用案例1

  1. 编写一个服务器端,和一个客户端
  2. 服务器端在9999端口监听
  3. 客户端连接到服务器端,发送"hello,server",然后退出
  4. 服务器端接收到客户端发送的信息,输出,并退出

思路分析:

        服务器端:

  1. 在本机的9999号端口监听,等待连接
  2. 当没有客户端连接9999号端口时,程序会阻塞,等待连接
  3. 通过socket.getInputStream()读取客户端写入到 数据通道 的数据,显示
  4. IO读取
  5. 关闭流和socket

        客户端:

  1. 连接服务器端(IP,端口)
  2. 连接上后,生成Socket,通过socket.getOutputStream()
  3. 通过输出流,写入到 数据通道
  4. 关闭流和socket
package com21.socket;


import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 甲柒
 * @version 1.0
 * @title SocketTCP01Server 服务端
 * @package com21.socket
 * @time 2023/4/6 21:16
 */
public class SocketTCP01Server {
    public static void main(String[] args) throws IOException {
        //服务端:
        //
        //1. 在本机的9999号端口监听,等待连接
        //   细节:要求在本机没有其他服务在监听9999
        //   细节:这个ServerSocket可以通过accept() 返回多个Socket[多个客户端连接服务器的并发]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端,在9999端口监听,等待连接~~~~~");

        //2. 当没有客户端连接9999号端口时,程序会阻塞,等待连接
        //   如果有客户端连接,则返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        System.out.println("socket = " + socket.getClass());
        //
        //3. 通过socket.getInputStream()读取客户端写入到 数据通道 的数据,显示
        //
        InputStream inputStream = socket.getInputStream();
        //4. IO读取
        byte[] bytes = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(bytes)) != -1) {
            //根据读取到的实际长度,显示内容
            System.out.println(new String(bytes, 0, readLen));
        }
        //5. 关闭流、socket、serverSocket
        inputStream.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端退出~~~");
    }
}
package com21.socket;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

/**
 * @author 甲柒
 * @version 1.0
 * @title SocketTCP01Client 客户端 发送”hello, server“给服务端
 * @package com21.socket
 * @time 2023/4/6 21:29
 */
public class SocketTCP01Client {
    public static void main(String[] args) throws IOException {
        //思路分析
        //1. 连接服务端(IP,端口)
        //   连接本机的9999 端口,如果连接成功,返回Socket对象
        Socket socket = new Socket(/*InetAddress.getLocalHost()*/"127.0.0.1", 9999);
        System.out.println("客户端 socket返回 = " + socket.getClass());
        //2. 连接上后,生成Socket,通过socket.getOutputStream()
        //   得到 和 socket对象 关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3. 通过输出流,写入到 数据通道
        outputStream.write("hello,server".getBytes());
        //4. 关闭对象和socket,必须关闭
        outputStream.close();
        socket.close();
        System.out.println("客户端退出....");
    }
}

  • 应用案例2(使用字节流)

  1. 编写一个服务器端,和一个客户端
  2. 服务器端在9999号端口监听
  3. 客户端连接到服务器端,发送"hello,server",并接收服务器端回发的"hello,client",再退出
  4. 服务器端接收到客户端发送的信息,输出,并发送"hello,client",再退出

思路分析:

        服务器端:

  1. socket.getOutputStream()
  2. 写入数据到数据通道
  3. 设置结束标记socket.shutdownOutput()
  4. 关闭socket和IO流...

        客户端:

  1. socket.getInputStream()
  2. 写入数据到数据通道
  3. 设置结束标记socket.shutdownOutput()
  4. 显示
  5. 关闭socket和IO...
package com21.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 甲柒
 * @version 1.0
 * @title SocketTCP02Server
 * @package com21.socket
 * @time 2023/4/6 22:07
 */
@SuppressWarnings({"all"})
public class SocketTCP02Server {
    public static void main(String[] args) throws IOException {
        //服务端:
        //
        //1. 在本机的9999号端口监听,等待连接
        //   细节:要求在本机没有其他服务在监听9999
        //   细节:这个ServerSocket可以通过accept() 返回多个Socket[多个客户端连接服务器的并发]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端,在9999端口监听,等待连接~~~~~");

        //2. 当没有客户端连接9999号端口时,程序会阻塞,等待连接
        //   如果有客户端连接,则返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        System.out.println("socket = " + socket.getClass());
        //
        //3. 通过socket.getInputStream()读取客户端写入到 数据通道 的数据,显示
        //
        InputStream inputStream = socket.getInputStream();
        //4. IO读取
        byte[] bytes = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(bytes)) != -1) {
            //根据读取到的实际长度,显示内容
            System.out.println(new String(bytes, 0, readLen));
        }

        //5. 获取socket相关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello,client".getBytes());
        //设置结束标记
        socket.shutdownOutput();

        //6. 关闭流、socket、serverSocket
        outputStream.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端退出~~~");
    }
}
package com21.socket;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * @author 甲柒
 * @version 1.0
 * @title SocketTCP02Client
 * @package com21.socket
 * @time 2023/4/6 22:08
 */
@SuppressWarnings({"all"})
public class SocketTCP02Client {
    public static void main(String[] args) throws IOException {
        //思路分析
        //1. 连接服务端(IP,端口)
        //   连接本机的9999 端口,如果连接成功,返回Socket对象
        Socket socket = new Socket(/*InetAddress.getLocalHost()*/"127.0.0.1", 9999);
        System.out.println("客户端 socket返回 = " + socket.getClass());
        //2. 连接上后,生成Socket,通过socket.getOutputStream()
        //   得到 和 socket对象 关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3. 通过输出流,写入到 数据通道
        outputStream.write("hello,server".getBytes());
        //设置结束标记
        socket.shutdownOutput();

        //4. 获取和socket相关联的输入流,读取数据(字节),并显示
        InputStream inputStream = socket.getInputStream();
        //5. IO读取
        byte[] bytes = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(bytes)) != -1) {
            //根据读取到的实际长度,显示内容
            System.out.println(new String(bytes, 0, readLen));
        }
        //6. 关闭对象和socket,必须关闭
        inputStream.close();
        outputStream.close();
        socket.close();
        System.out.println("客户端退出....");
    }
}

 

  • 应用案例3(使用字符流)

  1. 编写一个服务器端,和一个客户端
  2. 服务端再9999端口监听
  3. 客户端连接到服务器端,发送到"hello,world",并接收服务器端回发的"hello,client",再退出
  4. 服务器端接收到客户端发送的信息,输出,并发送"hello,client",再退出

 思路分析:

        服务器端:完成会接收客户端消息的黄色线,要求使用字符流

  1. socket.getInputStream()
  2. 将InputStream -> Reader
  3. 使用转换流InputStreamReader()
  4. 设置写入结束标记
    writer.newLine()//换行符,注意要使用readLine()读取

        客户端:完成会发送消息的黄色线,要求使用字符流

  1. socket.getOutputStream()
  2. 将OutputStream -> Writer
  3. 使用转换流OutputStreamWriter()
  4. 设置写入结束标记
    writer.newLine()//换行符,注意要使用readLine()读取
package com21.socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 甲柒
 * @version 1.0
 * @title SocketTCP03Server 服务器端,使用字符流方式读写
 * @package com21.socket
 * @time 2023/4/6 22:57
 */
@SuppressWarnings({"all"})
public class SocketTCP03Server {
    public static void main(String[] args) throws IOException {
        //服务端:
        //
        //1. 在本机的9999号端口监听,等待连接
        //   细节:要求在本机没有其他服务在监听9999
        //   细节:这个ServerSocket可以通过accept() 返回多个Socket[多个客户端连接服务器的并发]
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端,在9999端口监听,等待连接~~~~~");

        //2. 当没有客户端连接9999号端口时,程序会阻塞,等待连接
        //   如果有客户端连接,则返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        System.out.println("socket = " + socket.getClass());
        //
        //3. 通过socket.getInputStream()读取客户端写入到 数据通道 的数据,显示
        //
        InputStream inputStream = socket.getInputStream();
        //4. IO读取,使用字符流,使用InputStreamReader将inputStream转成字符流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);//输出


        //5. 获取socket相关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        //  使用字符输出流的方式回复信息
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello client 字符流");
        bufferedWriter.newLine();//插入一个换行符,表示回复内容的结束
        bufferedWriter.flush();//注意需要手动的flush

        //6. 关闭流、socket、serverSocket
        bufferedReader.close();
        bufferedWriter.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端退出~~~");
    }
}
package com21.socket;

import java.io.*;
import java.net.Socket;

/**
 * @author 甲柒
 * @version 1.0
 * @title SocketTCP03Client 客户端,发送"hello,server"给服务器端,使用字符流
 * @package com21.socket
 * @time 2023/4/6 22:57
 */
@SuppressWarnings({"all"})
public class SocketTCP03Client {
    public static void main(String[] args) throws IOException {
        //思路分析
        //1. 连接服务端(IP,端口)
        //   连接本机的9999 端口,如果连接成功,返回Socket对象
        Socket socket = new Socket(/*InetAddress.getLocalHost()*/"127.0.0.1", 9999);
        System.out.println("客户端 socket返回 = " + socket.getClass());
        //2. 连接上后,生成Socket,通过socket.getOutputStream()
        //   得到 和 socket对象 关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3. 通过输出流,写入到 数据通道,要求使用字符流
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("hello server 字符流");
        bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束,要求对方使用readLine() !!!!
        bufferedWriter.flush();//如果使用的字符流,需要手动刷新,否则数据不会写入数据通道


        //4. 获取和socket相关联的输入流,读取数据(字节),并显示
        InputStream inputStream = socket.getInputStream();
        //5. IO读取
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);

        //6. 关闭对象和socket,必须关闭
        bufferedReader.close();//关闭外层流
        bufferedWriter.close();
        socket.close();
        System.out.println("客户端退出....");
    }
}

  • 应用案例4

  1. 编写一个服务器端,和一个客户端
  2. 服务器端在8888端口监听
  3. 客户端连接到服务器端,发送一张图片比如E:\\qie.png
  4. 服务器端接收到客户端发送的图片,保存在src下,发送"收到图片"再退出
  5. 客户端接收到服务端发送的"收到图片",再退出
  6. 该程序要求使用StreamUtils.java

示意图:

        说明:使用BufferedInputStream和BufferedOutputStream字节流

思路分析:

        将客户端的图片,通过网路上传到服务器,服务器回复消息

  • 工具类
package com21.upload;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 工具类
 * 此类用于演示关于流的读写方法
 */
public class StreamUtils {
    /**
     * 功能:将输入流转换成byte[],即可以把文件的内容读入到byte[]
     *
     * @param is
     * @return
     * @throws Exception
     */
    public static byte[] streamToByteArray(InputStream is) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
        byte[] b = new byte[1024];//字节数组
        int len;
        while ((len = is.read(b)) != -1) {//循环读取
            bos.write(b, 0, len);//把读取到的数据,写入到bos
        }
        byte[] array = bos.toByteArray();//然后将bos 转成字节数组
        bos.close();
        return array;
    }

    /**
     * 功能:将InputStream转换成String
     *
     * @param is
     * @return
     * @throws Exception
     */

    public static String streamToString(InputStream is) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder builder = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) { //当读取到 null时,就表示结束
            builder.append(line + "\r\n");
        }
        return builder.toString();

    }

}
  • 服务器端
package com21.upload;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 甲柒
 * @version 1.0
 * @title TCPFileUploadServer
 * @package com21.upload
 * @time 2023/4/7 17:48
 */
public class TCPFileUploadServer {
    public static void main(String[] args) throws Exception {
        //1.服务器端在本机监听8888号端口
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务器端在8888号端口监听");
        //2.等待连接
        Socket socket = serverSocket.accept();

        //3.读取客户端发送的数据
        //  通过Socket得到输入流
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        //4.将得到的 bytes数组,写入到指定的路径,得到一个文件了
        String destFilePath = "E:\\java_study\\011-study\\src\\com21\\upload\\test.png";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        bos.write(bytes);
        bos.close();

        //5.向客户端回复"收到图片"
        //  同过socket得到字符输出流
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        writer.write("服务器端 收到图片");
        writer.flush();//把内容刷新到数据通道
        socket.shutdownOutput();//6.设置写入结束标志

        //7.关闭其他流
        writer.close();
        bis.close();
        socket.close();
        serverSocket.close();

    }
}
  • 客户端
package com21.upload;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.Socket;

/**
 * @author 甲柒
 * @version 1.0
 * @title TCPFileUploadClient
 * @package com21.upload
 * @time 2023/4/7 17:49
 */
public class TCPFileUploadClient {
    public static void main(String[] args) throws Exception {

        //客户端连接服务器端8888号端口
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
        //创建读取磁盘文件的输入流
        String filePath = "E:\\java_study\\011-study\\src\\com21\\upload\\胡桃.png";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));

        //bytes就是filePath对应的字节数组
        byte[] bytes = StreamUtils.streamToByteArray(bis);

        //通过socket获取到输出流,将bytes数据发送给服务器端
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        bos.write(bytes);//将文件对应的字节数组的内容,写入到数据通道
        //设置结束标志
        bis.close();
        socket.shutdownOutput();//设置写入数据的结束标志

        //接收从服务器端回复的消息
        InputStream inputStream = socket.getInputStream();
        //使用StreamUtils 的方法,直接将 inputStream 读取的内容 转成字符串
        String s = StreamUtils.streamToString(inputStream);
        System.out.println(s);
        inputStream.close();//关闭流

        //关闭相的流
        bos.close();
        socket.close();
    }
}
  • 运行结果

netstat指令

  1. netstat -an 可以查看当前主机网络情况,包括端口监听情况网络连接情况
  2. netstat -an | more 可以分页显示,按空格显示下一页
  3. 要求在dos控制台下执行,即cmd命令框里执行

说明:

  1. Listening表示某个端口在监听,Established表示当前行的本地地址与外部地址已连接成功
  2. 如果有一个外部程序(客户端)连接到该端口,就会显示一条连接信息
  3. 可以输入ctrl+c退出指令
  4. netstat -anb | more 分页显示 正在运行的程序 的端口 监听情况和网络连接情况

  • TCP网络通讯的特殊内容

  1.  当客户端连接到服务器端后,实际上客户端也是通过一个端口和服务器端进行通讯的,这个端口是TCP/IP来分配的
  2. 示意图
  3. 程序验证 + netstat - an | more

        使用应用案例4来验证

  • 说明:
  1. 第一行显示的是本机(客户端)建立的TCP数据通道
  2. 第二行显示的是服务器端建立的TCP数据通道,由于本机地址与服务器地址都在一台设备上,所以才会显示在这里

UDP网络通信编程

  • 基本介绍 

  1. 类DatagramSocket和DatagramPacket[数据报/数据包]实现了基于UDP协议网络程序
  2. UDP数据报通过数据报套接字DatagramSocket发送和接收,系统不保证UDP数据报一定能安全送到目的地,也不确定什么时候可以抵达
  3. DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号
  4. UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接
  • 示意图

  • UDP说明:

  1. 没有明确的服务器端和客户端,演变成数据的发送端和接收端
  2. 接收数据和发送数据是通过DatagramSocket对象完成的
  3. 将数据封装到DatagramPacket对象/装包
  4. 当接收到DatagramPacket对象,需要进行拆包,取出数据
  5. DatagramSocket可以指定在哪个端口接收数据
  • 基本流程

  1. 核心两个类/对象DatagramSocket和DatagramPacket
  2. 建立发送端,接收端(没有服务器端和客户端的概念)
  3. 发送数据前,建立数据包/报 DatagramPacket对象
  4. 调用DatagramSocket的发送、接收方法
  5. 关闭DatagramSocket

  • 应用案例

  1. 编写一个接收端A,和一个发送端B
  2. 接收端A在9999号端口等待接收数据(receive)
  3. 发送端B向接收端A发送数据"hello,想邀请你明天一起干饭~~~"
  4. 接收端A接收到发送端B发送的数据,回复"不去",再退出
  5. 发送端接收回复的数据,再退出
  • 示意图

  • UDP接收端A
package com21.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * @author 甲柒
 * @version 1.0
 * @title UDPReceiverA UDP接收端A
 * @package com21.udp
 * @time 2023/4/7 21:50
 */
public class UDPReceiverA {
    public static void main(String[] args) throws IOException {
        //1.创建一个DatagramSocket对象,准备在9999接收数据
        DatagramSocket socket = new DatagramSocket(9999);
        //2.构建一个DatagramPacket对象,准备接收数据
        //  一个数据包最大是64K
        byte[] buf = new byte[64];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

        //3.调用 接收方法,将通过网络传输的 DatagramPacket对象
        //  填充到 packet对象
        //提示:当有数据包发送到 本机的9999号端口时,就会接收到数据
        //      如果没有数据包发送到 本机的9999号端口,就会阻塞等待
        System.out.println("接收端A,等待接收数据~~~");
        socket.receive(packet);

        //4.可以把packet进行拆包,取出数据,并显示
        int length = packet.getLength();//实际收到的数据字节长度
        byte[] data = packet.getData();//接收到数据
        String s = new String(data, 0, length);
        System.out.println(s);

        //=================回复信息给B端
        //2.将需要发送的数据,封装到DatagramPacket对象
        data = "不去".getBytes();//
        //说明:封装的DatagramPacket对象 data 内容字节数组, data.length, 主机IP, 端口号
        packet =
                new DatagramPacket(data, data.length, InetAddress.getByName("192.168.56.1"), 9998);

        socket.send(packet);//发送
        System.out.println("A端发送数据成功~~~");

        //关闭资源
        socket.close();
        System.out.println("A端退出....");
    }
}
  • UDP发送端B 
package com21.udp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * @author 甲柒
 * @version 1.0
 * @title UDPSenderB UDP发送端B
 * @package com21.udp
 * @time 2023/4/7 21:50
 */
public class UDPSenderB {
    public static void main(String[] args) throws IOException {
        //1.创建DatagramSocket对象,准备发送和接收数据
        DatagramSocket socket = new DatagramSocket(9998);

        //2.将需要发送的数据,封装到DatagramPacket对象
        byte[] data = "hello,想邀请你明天一起干饭~~~".getBytes();//
        //说明:封装的DatagramPacket对象 data 内容字节数组, data.length, 主机IP, 端口号
        DatagramPacket packet =
                new DatagramPacket(data, data.length, InetAddress.getByName("192.168.56.1"), 9999);

        socket.send(packet);
        System.out.println("B端发送数据成功~~~");

        //===============接收从A端回复的信息
        //3.构建一个DatagramPacket对象,准备接收数据
        //  一个数据包最大是64K
        byte[] buf = new byte[64];
        packet = new DatagramPacket(buf, buf.length);

        //4.调用 接收方法,将通过网络传输的 DatagramPacket对象
        //  填充到 packet对象
        //提示:当有数据包发送到 本机的9998号端口时,就会接收到数据
        //      如果没有数据包发送到 本机的9998号端口,就会阻塞等待
        System.out.println("接收端B,等待接收数据~~~");
        socket.receive(packet);

        //5.可以把packet进行拆包,取出数据,并显示
        int length = packet.getLength();//实际收到的数据字节长度
        data = packet.getData();//接收到数据
        String s = new String(data, 0, length);
        System.out.println(s);

        //6.关闭资源
        socket.close();
        System.out.println("B端退出....");
    }
}
  • 运行结果

网络编程章节练习

1.编程题

  1. 使用字符流的方式,编写一个客户端程序和服务器端程序
  2. 客户端发送"name",服务器端接收到后,返回"哼~~~不告诉你"
  3. 客户端发送"hobby",服务器端接收到后,返回"java是世界上最好的语言"
  4. 不是这两个问题,回复"对不起,我不理解你的意思..."

package com21.exercise;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 甲柒
 * @version 1.0
 * @title Exercise01Server
 * @package com21.exercise
 * @time 2023/4/8 16:44
 */
public class Exercise01Server {
    public static void main(String[] args) throws IOException {
        //服务端:
        //
        //1. 在本机的8888号端口监听,等待连接
        //   细节:要求在本机没有其他服务在监听8888
        //   细节:这个ServerSocket可以通过accept() 返回多个Socket[多个客户端连接服务器的并发]
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端,在8888端口监听,等待连接~~~~~");

        //2. 当没有客户端连接8888号端口时,程序会阻塞,等待连接
        //   如果有客户端连接,则返回Socket对象,程序继续
        Socket socket = serverSocket.accept();

        System.out.println("socket = " + socket.getClass());
        //
        //3. 通过socket.getInputStream()读取客户端写入到 数据通道 的数据,显示
        //
        InputStream inputStream = socket.getInputStream();
        //4. IO读取,使用字符流,使用InputStreamReader将inputStream转成字符流
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        String answer = "";
        if ("name".equals(s)) {
            answer = "哼~~~不告诉你";
        } else if ("hobby".equals(s)) {
            answer = "java是世界上最好的语言";
        } else {
            answer = "对不起,我不理解你的意思...";
        }

        //5. 获取socket相关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        //  使用字符输出流的方式回复信息
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write(answer);
        bufferedWriter.newLine();//插入一个换行符,表示回复内容的结束
        bufferedWriter.flush();//注意需要手动的flush

        //6. 关闭流、socket、serverSocket
        bufferedReader.close();
        bufferedWriter.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端退出~~~");
    }
}
package com21.exercise;

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author 甲柒
 * @version 1.0
 * @title Exercise01Client
 * @package com21.exercise
 * @time 2023/4/8 16:44
 */
@SuppressWarnings({"all"})
public class Exercise01Client {
    public static void main(String[] args) throws IOException {
        //思路分析
        //1. 连接服务端(IP,端口)
        //   连接本机的8888 端口,如果连接成功,返回Socket对象
        Socket socket = new Socket(/*InetAddress.getLocalHost()*/"127.0.0.1", 8888);
        System.out.println("客户端 socket返回 = " + socket.getClass());
        //2. 连接上后,生成Socket,通过socket.getOutputStream()
        //   得到 和 socket对象 关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();
        //3. 通过输出流,写入到 数据通道,要求使用字符流
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));

        //从键盘读取用户的问题
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你的问题");
        String question = scanner.next();

        bufferedWriter.write(question);
        bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束,要求对方使用readLine() !!!!
        bufferedWriter.flush();//如果使用的字符流,需要手动刷新,否则数据不会写入数据通道

        //4. 获取和socket相关联的输入流,读取数据(字节),并显示
        InputStream inputStream = socket.getInputStream();
        //5. IO读取
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);

        //6. 关闭对象和socket,必须关闭
        bufferedReader.close();//关闭外层流
        bufferedWriter.close();
        socket.close();
        System.out.println("客户端退出....");
    }
}

2.编程题

  1. 编写一个接收端A和一个发送端B,使用UDP协议完成
  2. 接收端在8888端口等待接收数据(receive)
  3. 发送端向接收端发送数据"四大名著是哪些"
  4. 接收端收到发送端发送的问题后,返回"《红楼梦》《西游记》《三国演义》《水浒传》",否则返回"我不理解你的意思,我只知道四大名著是哪些"
  5. 接收端和发送端程序退出
package com21.exercise;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * @author 甲柒
 * @version 1.0
 * @title Exercise02ReceiverA
 * @package com21.exercise
 * @time 2023/4/8 23:43
 */
public class Exercise02ReceiverA {
    public static void main(String[] args) throws IOException {
        //1.创建一个DatagramSocket对象,准备在9999接收数据
        DatagramSocket socket = new DatagramSocket(9999);
        //2.构建一个DatagramPacket对象,准备接收数据
        //  一个数据包最大是64K
        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

        //3.调用 接收方法,将通过网络传输的 DatagramPacket对象
        //  填充到 packet对象
        //提示:当有数据包发送到 本机的9999号端口时,就会接收到数据
        //      如果没有数据包发送到 本机的9999号端口,就会阻塞等待
        System.out.println("接收端A,等待接收数据~~~");
        socket.receive(packet);

        //4.可以把packet进行拆包,取出数据,并显示
        int length = packet.getLength();//实际收到的数据字节长度
        byte[] data = packet.getData();//接收到数据
        String s = new String(data, 0, length);
        if ("四大名著是哪些".equals(s)) {
            //=================回复信息给B端
            //2.将需要发送的数据,封装到DatagramPacket对象
            data = "《红楼梦》《西游记》《三国演义》《水浒传》".getBytes();


        } else {
            data = "我不理解你的意思,我只知道四大名著是哪些".getBytes();
        }
        System.out.println(s);
        //说明:封装的DatagramPacket对象 data 内容字节数组, data.length, 主机IP, 端口号
        packet =
                new DatagramPacket(data, data.length, InetAddress.getByName("192.168.56.1"), 9998);

        socket.send(packet);//发送
        System.out.println("A端发送数据成功~~~");

        //关闭资源
        socket.close();
        System.out.println("A端退出....");
    }
}
package com21.exercise;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

/**
 * @author 甲柒
 * @version 1.0
 * @title Exercise02SenderB
 * @package com21.exercise
 * @time 2023/4/8 23:43
 */
public class Exercise02SenderB {
    public static void main(String[] args) throws IOException {
        //1.创建DatagramSocket对象,准备发送和接收数据
        DatagramSocket socket = new DatagramSocket(8888);
        System.out.println("请输入问题");
        Scanner scanner = new Scanner(System.in);
        String s1 = scanner.next();

        //2.将需要发送的数据,封装到DatagramPacket对象
        byte[] data = s1.getBytes();//
        //说明:封装的DatagramPacket对象 data 内容字节数组, data.length, 主机IP, 端口号
        DatagramPacket packet =
                new DatagramPacket(data, data.length, InetAddress.getByName("192.168.56.1"), 9999);

        socket.send(packet);
        System.out.println("B端发送数据成功~~~");

        //===============接收从A端回复的信息
        //3.构建一个DatagramPacket对象,准备接收数据
        //  一个数据包最大是64K
        byte[] buf = new byte[1024];
        packet = new DatagramPacket(buf, buf.length);

        //4.调用 接收方法,将通过网络传输的 DatagramPacket对象
        //  填充到 packet对象
        //提示:当有数据包发送到 本机的9998号端口时,就会接收到数据
        //      如果没有数据包发送到 本机的9998号端口,就会阻塞等待
        System.out.println("接收端B,等待接收数据~~~");
        socket.receive(packet);

        //5.可以把packet进行拆包,取出数据,并显示
        int length = packet.getLength();//实际收到的数据字节长度
        data = packet.getData();//接收到数据
        String s = new String(data, 0, length);
        System.out.println(s);

        //6.关闭资源
        socket.close();
        System.out.println("B端退出....");
    }
}

3.编程题

  1. 编写客户端程序和服务器端程序
  2. 客户端可以输入一个音乐文件名,比如醉赤壁,服务器端收到音乐名后,可以给客户端返回这个音乐文件,如果服务器没有这个文件,返回一个默认的音乐即可
  3. 客户端收到文件后,保存到本地E:\\
  4. 该程序可以使用StreamUtils.java

package com21.exercise;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;

/**
 * 工具类
 * 此类用于演示关于流的读写方法
 */
public class StreamUtils {
    /**
     * 功能:将输入流转换成byte[],即可以把文件的内容读入到byte[]
     *
     * @param is
     * @return
     * @throws Exception
     */
    public static byte[] streamToByteArray(InputStream is) throws Exception {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
        byte[] b = new byte[1024];//字节数组
        int len;
        while ((len = is.read(b)) != -1) {//循环读取
            bos.write(b, 0, len);//把读取到的数据,写入到bos
        }
        byte[] array = bos.toByteArray();//然后将bos 转成字节数组
        bos.close();
        return array;
    }

    /**
     * 功能:将InputStream转换成String
     *
     * @param is
     * @return
     * @throws Exception
     */

    public static String streamToString(InputStream is) throws Exception {
        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
        StringBuilder builder = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) { //当读取到 null时,就表示结束
            builder.append(line + "\r\n");
        }
        return builder.toString();

    }

}
package com21.exercise;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author 甲柒
 * @version 1.0
 * @title Exercise03Server
 * @package com21.exercise
 * @time 2023/4/9 9:36
 */
public class Exercise03Server {
    public static void main(String[] args) throws Exception {

        //1. 监听 9999号端口
        ServerSocket serverSocket = new ServerSocket(9999);
        //2. 等待客户端连接
        System.out.println("服务器端在9999号端口监听,等待下载文件");
        Socket socket = serverSocket.accept();
        //3. 读取 客户端发送要下载的文件名
        //   这里使用了while循环读取文件名, 考虑客户端发送的数据较大的情况
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = 0;
        String downLoadFileName = "";
        while ((len = inputStream.read(bytes)) != -1) {
            downLoadFileName += new String(bytes, 0, len);
        }
        System.out.println("客户端需要下载的文件名=" + downLoadFileName);
        //在服务器上有两个文件,稻香.mp3  醉赤壁.mp3
        //如果客户端下载的是 稻香 就返回该文件,否则一律返回 醉赤壁.mp3
        String resFileName = "";
        if ("稻香".equals(downLoadFileName)) {
            resFileName = "E:\\java_study\\011-study\\src\\com21\\exercise\\稻香.mp3";
        } else {
            resFileName = "E:\\java_study\\011-study\\src\\com21\\exercise\\醉赤壁.mp3";
        }

        //4. 创建一个输入流,读取文件
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(resFileName));

        //5. 使用工具类StreamUtils,读取文件到一个字节数组
        byte[] bytes1 = StreamUtils.streamToByteArray(bufferedInputStream);

        //6. 得到Socket关联的输出流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());

        //7. 写入到数据通道,返回给客户端
        bufferedOutputStream.write(bytes1);
        socket.shutdownOutput();//设置结束标记

        //8. 关闭资源
        bufferedInputStream.close();
        bufferedOutputStream.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务器端退出~~~~");
    }
}
package com21.exercise;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

/**
 * @author 甲柒
 * @version 1.0
 * @title Exercise03Client
 * @package com21.socket
 * @time 2023/4/9 10:01
 */
public class Exercise03Client {
    public static void main(String[] args) throws Exception {
        //1. 接收用户输入,指定下载文件名
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入要下载的文件名");
        String downloadFileName = scanner.next();

        //2. 客户端连接服务器端,准备发送
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        //3. 获取和Socket关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write(downloadFileName.getBytes());
        //设置写入结束标志
        socket.shutdownOutput();
        //4. 读取服务器端返回的文件(字节数组)
        BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToByteArray(bufferedInputStream);

        //5. 得到一个输出流,准备将bytes写入到磁盘文件
        String filePath = "E:\\" + downloadFileName + ".mp3";
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath));
        bufferedOutputStream.write(bytes);

        //6. 关闭相关资源

        bufferedOutputStream.close();
        bufferedInputStream.close();
        outputStream.close();
        socket.close();
        System.out.println("客户端下载完毕,正确退出...");

    }
}

猜你喜欢

转载自blog.csdn.net/qq_59621600/article/details/129988740