java 网络编程

一、网络的相关概念

1.1 网络通信

        1、概念:两台设备之间通过网络实现数据传输

        2、网络通信:将数据通过网络从一台设备传输到另一台设备

        3、java.net 包下提供了一系列的类或接口,供程序员使用,完成网络通信

1.2 网络

        1、概念:两台或多台设备通过一定物理设备连接起来构成了网络

        2、根据网络的覆盖范围不同,对网络可以分为局域网、地域网、广域网等

1.3 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 的使用,不仅能解决网络地址资源数量的问题,而且也解决了多种设备接入互联网的障碍。

1.4 ipv4 地址分类

1.5 域名

        1、www.baidu.com

        2、好处:为了方便记忆,解决记 ip 的困难

        3、概念:将 ip 地址映射成域名

1.6 端口

        1、概念:用于标记计算机上某个特定的网络程序

        2、表示形式:以整数的形式,范围 0 ~ 65535

        3、0 ~ 1024 已经被占用,比如 ssh 22ftp 21smtp 25http 80

        4、常见的网络程序端口号:tomcat(8080)mysql(3306)oracle(1521)sqlserver(1433)

1.7 网络通信协议

        协议(tcp/ip),TCP/IPTransmission Control Protocol/Internet Protocol)的简写,中文译名为传输控制协议/因特网互联协议,又叫网络通信协议,这个协议是 Internet 最基本的协议,Internet 国际互联网络的基础,简单地说,就是由网络层的 IP 地址和传输层的 TCP 协议组成的,如下图

        OSI 模型为理论模型,在现实里面是没有这个模型的,因为分的太细了,没有人用。最终给它做了一个简化 ,简化成了 TCP/IP 模型,其中传输层就是 TCP 层,网络层就是 IP 层。

1.8 TCP 和 UDP

1.8.1 TCP 传输控制协议

        1、使用 TCP 协议前,须先建立 TCP 连接,形成传输数据通道。

        2、传输前,采用 “三次握手” 方式,是可靠的

        3、TCP 协议进行通信的两个应用进程:客户端、服务端

        4、在连接中可进行大数据量的传输

        5、传输完毕,须释放已经建立的连接,效率低

1.8.2 UDP 用户数据协议 

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

        2、每个数据包的大小限制在 64K 内,不适合传输大量数据

        3、因无需连接,故是不可靠的

        4、发送数据结束时无需释放资源(因为不是面向连接的),速度快

二、InetAddress 类

2.1 相关方法

// 获取本机 InetAddress 对象
getLocalHost()
// 根据指定主机名/域名获取 ip 地址对象
getByName()
// 获取 InetAddress 对象的主机名
getHostName()
// 获取 InetAddress 对象的地址
getHostAdderss()

2.2 示例代码

public static void main(String[] args) throws Exception{
	// 1、获取本机 InetAddress 对象
	InetAddress localHost = Inet4Address.getLocalHost();
	System.out.println(localHost);// DESKTOP-B0B82CP/192.168.229.1

	// 2、根据指定主机名/域名获取 ip 地址对象
	InetAddress host1 = Inet4Address.getByName("DESKTOP-B0B82CP");
	System.out.println(host1);// DESKTOP-B0B82CP/192.168.229.1

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

	// 4、通过 InetAddress 对象获取对应的地址
	String hostAddress = host2.getHostAddress();
	System.out.println(hostAddress);// 110.242.68.4

	// 5、通过 InetAddress 对象获取对应的主机名或域名
	String hostName = host2.getHostName();
	System.out.println(hostName);// www.baidu.com
}

2.3 作用

        1、这个类的主要的作用是获取本机的主机名/IP 信息。

        2、通过域名获取远程服务器的 IP 等信息。

三、Socket

3.1 基本介绍

        1、套接字(Socket)开发网络应用程序被广泛采用,以至于成为事实上的标准。

        2、通信的两端都要有 Socket,是两台机器间通信的端点。

        3、网络通信其实就是 Socket 间的通信。

        4、Socket 允许程序将网络连接当成一个流,数据在两个 Socket 间通过 IO 传输。

        5、一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。

3.2 示意图

四、TCP 网络通信编程

4.1 基本介绍

        1、基于客户端--服务器的网络通信

        2、底层使用的是 TCP/IP 协议

        3、应用场景为:客户端发送数据,服务端接收并显示

        4、是基于 Socket TCP 编程

4.2 案例一

4.2.1 需求分析

        1、使用字节流

        2、编写一个服务器端和一个客户端。

        3、服务端在 9999 端口监听。

        4、客户端连接到服务器端,发送 "hello server" ,然后退出。

        5、服务器端接收到客户端发送的信息,输出并退出。

4.2.2 代码编写

        服务端代码如下

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、通过 ocket.getInputStream() 读取客户端写入到数据通道的数据
        InputStream inputStream = socket.getInputStream();
        // 4、IO 读取
        byte [] buff = new byte[1024];
        int readline =0;
        while((readline = inputStream.read(buff)) != -1){
            // 根据实际读取到的字符串显示内容
            System.out.println(new String(buff,0,readline));
        }
        // 5、关闭相关的流和 socket
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

        客户端代码如下

public class SocketTcp01Client {
    public static void main(String[] args) throws IOException {
        // 1、连接服务端(ip,端口)
        // 连接本机的 9999 端口,如果连接成功,返回 Socket 对象
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);
        System.out.println("客户端 socket 返回="+socket.getClass());

        // 2、连接上后,生成 socket 对象
        // 再获取到和 socket 对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();

        // 3、通过输出流写入数据到数据通道
        outputStream.write("hello server".getBytes());
        // 4、关闭流对象和 socket
        outputStream.close();
        socket.close();
        System.out.println("客户端退出了");
    }
}

4.3 案例二

4.3.1 需求分析

        1、使用字节流

        2、编写一个服务器端和一个客户端。

        3、服务端在 9999 端口监听。

        4、客户端连接到服务器端,发送 "hello server" ,并接收服务端回发的 "hello client" 后退出。

        5、服务器端接收到客户端发送的信息,输出并发送 "hello client" 再退出。

4.3.2 代码编写

        服务端代码如下

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、通过 ocket.getInputStream() 读取客户端写入到数据通道的数据
        InputStream inputStream = socket.getInputStream();
        // 4、IO 读取
        byte [] buff = new byte[1024];
        int readline =0;
        while((readline = inputStream.read(buff)) != -1){
            // 根据实际读取到的字符串显示内容
            System.out.println(new String(buff,0,readline));
        }
        // 5、获取 socket 相关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello client".getBytes());
        // 设置结束标记
        socket.shutdownOutput();

        // 5、关闭相关的流和 socket
        outputStream.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
    }
}

        客户端代码如下

public class SocketTcp02Client {
    public static void main(String[] args) throws IOException {
        // 1、连接服务端(ip,端口)
        // 连接本机的 9999 端口,如果连接成功,返回 Socket 对象
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);
        System.out.println("客户端 socket 返回="+socket.getClass());

        // 2、连接上后,生成 socket 对象
        // 再获取到和 socket 对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();

        // 3、通过输出流写入数据到数据通道
        outputStream.write("hello server".getBytes());
        // 设置结束标记
        socket.shutdownOutput();

        // 获取和 socket 关联的输入流,读取数据(字节)并显示
        InputStream inputStream = socket.getInputStream();
        byte [] buff = new byte[1024];
        int readLine = 0;
        while((readLine = inputStream.read(buff)) != -1){
            System.out.println(new String(buff,0,readLine));
        }

        // 4、关闭流对象和 socket
        inputStream.close();
        outputStream.close();
        socket.close();
        System.out.println("客户端退出了");
    }
}

4.4 案例三

4.4.1 需求分析

        1、使用字符流

        2、编写一个服务器端和一个客户端。

        3、服务端在 9999 端口监听。

        4、客户端连接到服务器端,发送 "hello server" ,并接收服务端回发的 "hello client" 后退出。

        5、服务器端接收到客户端发送的信息,输出并发送 "hello client" 再退出。

4.4.2 代码编写

        服务端代码如下

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 读取,使用字符流
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        String s = br.readLine();
        System.out.println(s);

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

        // 5、关闭相关的流和 socket
        bw.close();// 只关闭外层流即可
        br.close();// 只关闭外层流即可
        socket.close();
        serverSocket.close();
    }
}

        客户端代码如下

public class SocketTcp03Client {
    public static void main(String[] args) throws IOException {
        // 1、连接服务端(ip,端口)
        // 连接本机的 9999 端口,如果连接成功,返回 Socket 对象
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);
        System.out.println("客户端 socket 返回="+socket.getClass());

        // 2、连接上后,生成 socket 对象
        // 再获取到和 socket 对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();

        // 3、通过输出流,写入数据到数据通道,使用字符流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
        bw.write("hello server 字符流");
        bw.newLine();// 插入一个换行符,表示写入的内容结束
        bw.flush();// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道

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

        // 5、关闭流对象和 socket
        br.close();// 只关闭外层流即可
        bw.close();// 只关闭外层流即可
        socket.close();
        System.out.println("客户端退出了");
    }
}

4.5 案例四

4.5.1 需求分析

        1、编写一个服务器端和一个客户端。

        2、服务端在 8888 端口监听。

        3、客户端连接到服务器端,发送 一张图片 f:\\pic.jpg

        4、服务器端接收到客户端发送的图片,保存到 src 下,发送 "收到图片" 再退出。

        5、客户端接收到服务端发送的 "收到图片",再退出。

        6、使用 BufferedInputStream BufferedOutputStream 字节流

4.5.2 代码编写

        服务端代码如下

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();
        System.out.println("服务端 socket 返回=" + socket.getClass());

        // 3、读取客户端发送过来的数据
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToByteArray(bis);

        // 4、将 bytes 数组,写入到指定的路径下
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("src\\pic.jpg"));
        bos.write(bytes);
        bos.close();

        // 5、获取 socket 相关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
        bw.write("收到图片");
        bw.flush();// 注意需要手动的 flush
        socket.shutdownOutput();

        // 6、关闭其他资源
        bw.close();
        bis.close();
        socket.close();
        serverSocket.close();
    }
}

        客户端代码如下

public class TcpFileUploadClient {
    public static void main(String[] args) throws Exception {
        // 1、客户端连接的 8888 端口,返回 Socket 对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);

        // 2、创建读取磁盘文件的输入流
        String path = "f:\\pic.jpg";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));

        // 3、将文件读取到字节数组中
        byte[] bytes = StreamUtils.streamToByteArray(bis);

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

        // 5、接收从服务端回复的消息
        InputStream inputStream = socket.getInputStream();
        String s = StreamUtils.streamToString(inputStream);
        System.out.println(s);

        // 6、关闭流对象和 socket
        inputStream.close();
        bos.close();
        socket.close();
    }
}

        工具类代码如下

public class StreamUtils {
    /**
     * 功能:将输入流转换成 byte 数组,即把文件的内容读入到 byte 数组
     */
    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);
        }
        byte[] array = bos.toByteArray();
        bos.close();
        return array;
    }
    /**
     * 功能:将输入流的数据直接转换为字符串
     */
    public static String streamToString(InputStream is) throws Exception{
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        StringBuilder sb = new StringBuilder();
        String line;
        while((line = br.readLine()) != null){
            sb.append(line+"\r\n");
        }
        return sb.toString();
    }
}

4.6 netstat 指令

4.6.1 简介

        1、netstat -an 可以查看当前主机网络情况,包括端口监听和网络连接情况

        2、netstat -an | more 可以分页显示,空格显示下一页,回车显示下一条记录

        3、需要在 dos 控制台下执行

4.6.2 演示

        1、LISTENING 表示某个端口在监听,ESTABLISHED 表示已经建立连接

        2、如果有一个外部程序(客户端)连接到监听端口,就会显示一条 ESTABLISHED 连接信息

        3、可以使用 ctrl+c 退出指令

4.7 客户端随机端口

        当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是 TCP/IP 来分配的,是不确定的,是随机的。

五、UDP 网络通信编程

5.1 基本介绍

        1、DatagramSocket 类和 DatagramPacket (数据包/数据报)实现了基于 UDP 协议网络程序。

        2、UDP 数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证 UDP 数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。

        3、DatagramPacket 对象封装了 UDP 数据报,在数据报中包含了发送端的 IP 地址和端口号以及接收端的 IP 地址和端口号。

        4、UDP 协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接。

5.2 UDP 原理示意图

5.3 基本流程

        1、核心的两个类/对象 DatagramSocket  DatagramPacket

        2、需要建立发送端和接收端(没有服务端和客户端概念)

        3、发送数据前,建立数据包/报 DatagramPacket 对象

        4、调用 DatagramSocket 的发送、接收方法

        5、关闭 DatagramSocket

5.4 实战演练

5.4.1 需求分析

        1、编写一个接收端 A 和发送端 B

        2、接收端 A 9999 端口等待接收数据

        3、发送端 B 向接收端 A 发送数据 "hello,明天吃火锅~"

        4、接收端 A 接收到发送端 B 发送的数据,回复 ”好的,明天见“,再退出

        5、发送端接收回复的数据后,再推出。

5.4.2 代码编写

        UDP 接收端代码如下

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[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);
        System.out.println(s);

        /** 回复消息给 B 端 **/
        // 将需要发送的数据,封装到 DatagramPacket 对象
        data = "好的明天见".getBytes();
        packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"),9998);

        // 发送数据
        socket.send(packet);

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

        UDP 发送端代码如下

public class UDPReceiverB {
    public static void main(String[] args) throws IOException {

        // 1、创建一个 DatagramSocket 对象,准备在 9998 接收数据
        DatagramSocket socket = new DatagramSocket(9998);

        // 2、将需要发送的数据,封装到 DatagramPacket 对象
        byte[] data = "hello 明天吃火锅".getBytes();

        // new DatagramPacket(data内容字节数组,data.length,主机(IP),端口号)
        DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"),9999);

        // 3、发送数据
        socket.send(packet);

        /** 接收 A 端回复的消息 **/
        // 创建一个 DatagramPacket 对象,准备接收数据
        byte [] buf = new byte[1024];
        packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);

        // 对 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端退出");
    }
}

六、习题练习

6.1 练习一

6.1.1 需求分析

        1、使用字符流的方式,编写一个客户端程序和服务器端程序

        2、客户端发送 ”name“ ,服务端接收到后,返回 ”我是 nova

        3、客户端发送 ”hobby“ ,服务端接收到后,返回 ”编写 java 程序“

        4、若问的不是这两个问题,返回 ”你说啥呢“

6.1.2 代码实现

        客户端代码如下

public class SocketTcp03Client {
    public static void main(String[] args) throws IOException {
        // 1、连接服务端(ip,端口)
        // 连接本机的 9999 端口,如果连接成功,返回 Socket 对象
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);

        // 2、连接上后,生成 socket 对象
        // 再获取到和 socket 对象关联的输出流对象
        OutputStream outputStream = socket.getOutputStream();

        // 3、通过输出流,写入数据到数据通道,使用字符流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));

        // 从键盘获取输入的值
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你的问题");
        String next = scanner.next();
        bw.write(next);
        bw.newLine();// 插入一个换行符,表示写入的内容结束
        bw.flush();// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道

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

        // 5、关闭流对象和 socket
        br.close();// 只关闭外层流即可
        bw.close();// 只关闭外层流即可
        socket.close();
        System.out.println("客户端退出了");
    }
}

        服务端代码如下

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

        // 3、通过 socket.getInputStream() 读取客户端写入到数据通道的数据
        InputStream inputStream = socket.getInputStream();
        // 4、IO 读取,使用字符流
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        String s = br.readLine();
        System.out.println("服务端接收到的消息为:"+s);

        String answer = "";
        if("name".equals(s)){
            answer = "我是nova";
        }else if("hobby".equals(s)){
            answer = "编写java程序";
        }else{
            answer = "你说啥呢";
        }
        // 5、获取 socket 相关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));
        bw.write(answer);
        bw.newLine();// 插入一个换行符,表示写入的内容结束
        bw.flush();// 注意需要手动的 flush

        // 5、关闭相关的流和 socket
        bw.close();// 只关闭外层流即可
        br.close();// 只关闭外层流即可
        socket.close();
        serverSocket.close();
    }
}

6.2 练习二

6.2.1 需求分析

        1、编写一个接收端 A 和一个发送端 B,使用 UDP 协议完成

        2、接收端在 8888 端口等待接收数据

        3、发送端向接收端发送数据 ”四大名著有哪些“

        4、接收端接收到发送端发送的问题后,返回 ”四大名著是《西游记》《水浒传》《红楼梦》《三国演义》“,否则返回 ”what?

        5、接收端和发送端程序退出

6.2.2 代码实现

        接收端代码如下

public class UDPReceiverA {
    public static void main(String[] args) throws IOException {

        // 1、创建一个 DatagramSocket 对象,准备在 9999 接收数据
        DatagramSocket socket = new DatagramSocket(8888);

        // 2、创建一个 DatagramPacket 对象,准备接收数据
        byte [] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

        // 3、调用接收方法,将通过网络传输的 DatagramPacket 对象 填充到 packet 对象

        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);
        String answer = "";
        if("四大名著有哪些".equals(s)){
            answer = "四大名著是《西游记》《水浒传》《红楼梦》《三国演义》";
        }else{
            answer = "what?";
        }

        /** 回复消息给 B 端 **/
        // 将需要发送的数据,封装到 DatagramPacket 对象
        data = answer.getBytes();
        packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"),8887);

        // 发送数据
        socket.send(packet);

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

        发送端代码如下

public class UDPReceiverB {
    public static void main(String[] args) throws IOException {

        // 1、创建一个 DatagramSocket 对象,准备在 9998 接收数据
        DatagramSocket socket = new DatagramSocket(8887);

        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入内容");
        String next = scanner.next();
        // 2、将需要发送的数据,封装到 DatagramPacket 对象
        byte[] data = next.getBytes();

        // new DatagramPacket(data内容字节数组,data.length,主机(IP),端口号)
        DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"),8888);

        // 3、发送数据
        socket.send(packet);

        /** 接收 A 端回复的消息 **/
        // 创建一个 DatagramPacket 对象,准备接收数据
        byte [] buf = new byte[1024];
        packet = new DatagramPacket(buf, buf.length);
        socket.receive(packet);

        // 对 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端退出");
    }
}

6.3 练习三

6.3.1 需求分析

        1、编写客户端和服务端程序

        2、客户端可以输入一个音乐文件名,比如 ”高山流水“,服务端收到音乐名后,可以给客户端返回这个音乐文件,如果服务器没有这个文件,返回一个默认的音乐即可

        3、客户端收到文件后,保存到本地 e:\\

        4、提示:该程序可以使用 StreamUtils.java 

6.3.2 代码实现

        客户端代码如下

public class SocketTcp03Client {
    public static void main(String[] args) throws Exception {
        // 1、连接服务端(ip,端口)
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);

        // 2、连接上后,生成 socket 对象
        OutputStream outputStream = socket.getOutputStream();

        // 3、通过输出流,写入数据到数据通道,使用字符流
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream));

        // 4、从键盘获取输入的值
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入音乐文件名");
        String next = scanner.next();
        bw.write(next);
        bw.newLine();// 插入一个换行符,表示写入的内容结束
        bw.flush();// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道

        // 5、获取和 socket 关联的输入流
        InputStream inputStream = socket.getInputStream();
        BufferedInputStream br = new BufferedInputStream(inputStream);

        byte[] bytes = StreamUtils.streamToByteArray(br);
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("e:\\"+next+".ncm"));
        bos.write(bytes);

        // 6、关闭流对象和 socket
        br.close();// 只关闭外层流即可
        bw.close();// 只关闭外层流即可
        socket.close();
        System.out.println("客户端退出了");
    }
}

        服务端代码如下

public class SocketTcp03Server {
    public static void main(String[] args) throws Exception {

        // 1、监听本机的 9999 端口,等待连接
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在 9999 端口等待连接");
        // 2、等待客户端连接
        Socket socket = serverSocket.accept();

        // 3、读取客户端发送的文件名
        InputStream inputStream = socket.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
        String s = br.readLine();
        System.out.println("服务端接收到的消息为:"+s);

        // 4、判断指定目录下书否存在该音乐,如果存在则返回该音乐,如果不存在,则返回一首默认的音乐
        File file = new File("src\\" + s+".ncm");
        String fileName = "";
        if(file.exists()){
            fileName = "src\\"+s+".ncm";
        }else{
            fileName = "src\\不得不爱.ncm";
        }
        // 5、读取音频文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName));
        byte[] bytes = StreamUtils.streamToByteArray(bis);

        // 6、获取 socket 相关联的输出流
        OutputStream outputStream = socket.getOutputStream();
        BufferedOutputStream bws = new BufferedOutputStream(outputStream);
        bws.write(bytes);
        socket.shutdownOutput();
        bws.flush();// 注意需要手动的 flush

        // 7、关闭相关的流和 socket
        bws.close();// 只关闭外层流即可
        br.close();// 只关闭外层流即可
        socket.close();
        serverSocket.close();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Java网络编程是一种通过使用Java编程语言,在计算机网络上实现通信的技术。它涉及到协议、IP地址和端口号等三个重要要素。Java编写的应用程序可以在单个计算机上运行,也可以在网络的服务器端和客户端上分布运行。此外,Java还可以用于编写小型应用程序模块或applet,作为网页的一部分使用。applet可以嵌入网页,并在浏览器运行。在Java,使用URI可以代表绝对的或相对的资源,而URL则包含了定位资源的信息,并且不能是相对的。Java提供了一系列的类和接口,例如InetAddress、Socket、ServerSocket、URL和URLConnection等,用于实现各种网络应用。通过使用这些类和接口,可以创建连接客户端和服务器之间的套接字,并实现面向连接的通信,保证数据的可靠传输。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [Java网络编程](https://blog.csdn.net/weixin_42784609/article/details/130388655)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [超详细入门到精通自学视频课程(阶段01:JavaSE基础02 编程思维编程思维和编程能力、综合应用专题-04、案例...](https://download.csdn.net/download/weixin_54787054/88224199)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [网络编程](https://blog.csdn.net/zhixingwu/article/details/103226003)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

快乐的小三菊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值