第七章 - Java网络编程(四)

系列文章目录

Java基础篇之jdk、jre环境变量的配置
第一章 - Java基本语法
第六章 - Java集合
第七章 - Java网络编程(一)
第七章 - Java网络编程(二)
第七章 - Java网络编程(三)



前言

本章节介绍两个网络编程的经典案例,涉及字节流编程、字符流编程,以及网络文件下载操作


一、TCP协议使用字符流完成数据传输

要求:

  • 使用字符流的方式,编写一客户端程序和服务端程序
  • 客户端发送"name",服务器端接收到后,返回"我是nova",nova是你自己的名字
  • 客户端发送"hobby",服务器端接收到后,返回"编写java程序"
  • 不是这两个问题,回复"你说啥呢"

实现思路:

服务端:

  • 首先服务端需要在IP:端口进行监听,即创建一个服务端
  • 服务端等待客户端连接,如果没有客户端连接,将卡在这儿不往下执行
  • 有客户端连接,则拿到这个socket,获取到关联socket的字符输入流,获取客户端从数据通道发送的数据
  • 拿到数据后,判断客户端说了啥(可用switch,也可用if-else结构,判断,目前只支持一次通讯,如果可以多次通讯即扩展为聊天功能),按要求返回对应的字符串
  • 获取socket相关联的字符输出流,将字符串写入数据通道
  • 设置写入结束标记(很重要,如果不设置客户端不知道是不是写完了,将会卡顿)
  • 发送完数据后,将相关流资源关闭

客户端:

  • 创建一个客户端,通过IP:端口连接上服务端
  • 获取socket相关联的字符输出流,将数据写入数据通道
  • 设置写入结束标记
  • 获取socket相关的字符输入流,获取服务端返回的数据
  • 将数据打印出来
  • 关闭相关资源

代码实现:

  • 服务端:
package com.cuinn.homework;

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

/**
 * 服务端
 * 使用字符流
 */
@SuppressWarnings({"all"})
public class Homework01Server {
    public static void main(String[] args) throws IOException {
        // 1.创建服务端socket
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端,在9999端口监听,等待连接...");
        // 2.等待客户端连接
        Socket socket = serverSocket.accept();
        System.out.println("socket=" + socket.getClass());
        // 3.获取客户端在数据通道写入的数据
        InputStream inputStream = socket.getInputStream();
        // 4.IO读取
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);
        // 5.向数据通道写入数据
        OutputStream outputStream = socket.getOutputStream();
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        // 6.判断从数据通道获取的数据是什么
        switch (s){
            case "name":
                bufferedWriter.write("我是nova");
                break;
            case "hobby": bufferedWriter.write("编写java程序");
                break;
            default: bufferedWriter.write("你说啥呢");
        }
        bufferedWriter.newLine();
        bufferedWriter.flush();
        // 6.关闭外层流和socket
        bufferedWriter.close();
        bufferedReader.close();
        socket.close();
        serverSocket.close();
    }
}
  • 客户端:
package com.cuinn.homework;

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

/**
 * 客户端
 * 使用字符流
 */
@SuppressWarnings({"all"})
public class Homework01Client {
    public static void main(String[] args) throws IOException {
        // 1.创建客户端socket
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        System.out.println("客户端 socket返回=" + socket.getClass());
        // 2.获取输出流
        OutputStream outputStream = socket.getOutputStream();
        // 3.向数据通道写数据
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        // 4.从键盘读取用户的问题
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入你的问题");
        String question = scanner.next();

        bufferedWriter.write(question);
        bufferedWriter.newLine();// 插入一个换行符,表示写入内容结束,要求对方使用readLine()
        bufferedWriter.flush();// 如果使用字符流需要手动刷新,否则数据不会写入数据通道
        // 5.获取输入流
        InputStream inputStream = socket.getInputStream();
        // 6.读取服务端写入数据通道的数据
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);
        // 7.关闭外层流和socket
        bufferedWriter.close();
        bufferedReader.close();
        socket.close();
        System.out.println("客户端退出...");
    }
}

二、UDP协议实现单次通讯

要求:

  • 编写一个接收端A,和一个发送端B,使用UDP协议完成
  • 接收端在8888端口等待接收数据(receive)
  • 发送端向接收端发送数据"四大名著是哪些"
  • 接收端接收到发送端发送的问题后,返回"四大名著是《红楼梦》…",否则返回what?
  • 接收端和发送端程序退出

实现思路:

接收端:

  • 创建一个DatagramSocket对象,固定一个端口,准备在这个端口接收数据
  • 构建一个DatagramPacket对象,准备接收数据
  • 调用接收方法, 将通过网络传的DatagramPacket对象填充到packet对象
  • 可以把packet进行拆包,取出数据,并显示。
  • 判断发送端发送的是什么,按要求回复
  • 创建DatagramPacket对象,装包,发送给对方
  • 关闭相关资源

发送端:

  • 创建一个DatagramSocket对象,固定一个端口,准备在这个端口接收数据
  • 从键盘读取数据,将需要发送的数据,封装到DatagramPacket对象
  • 装包,发送数据(data内容字节数组,data.length, 主机(IP),端口)
  • 接收发送端发送的数据报
  • 数据填充到packaet
  • 拆包
  • 关闭相关资源

代码实现:

  • 接收端A:
package com.cuinn.homework;

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

/**
 * @author ckwu
 * @version 1.0
 * @project JavaProject
 * @description 接收端A
 * @date 2022-10-06 12:58:54
 */
@SuppressWarnings({"all"})
public class Homework02ReceiveA {
    public static void main(String[] args) throws IOException {
        // 1.创建一个DatagramSocket对象,准备在9999接收数据
        DatagramSocket socket = new DatagramSocket(8888);
        // 2.构建一个DatagramPacket对象,准备接收数据
        // UDP协议数据包最大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);
        String answer = "";
        if ("四大名著是哪些".equals(s)){
            answer = "四大名著是《红楼梦》《西游记》《三国演义》《水浒传》";
        }else {
           answer = "what?";
        }

        // 5.向接收端B回复“好的,明天见”
        data = answer.getBytes();
        packet = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.1.107"), 9998);
        // 6.发送数据包
        socket.send(packet);
        // 5.关闭资源
        socket.close();
        System.out.println("A端退出...");
    }
}
  • 发送端B:
package com.cuinn.homework;


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

/**
 * @author ckwu
 * @version 1.0
 * @project JavaProject
 * @description 发送端B
 * @date 2022-10-06 13:01:00
 */
@SuppressWarnings({"all"})
public class Homework02SenderB {
    public static void main(String[] args) throws IOException {
        // 1.创建DatagramSocket对象,准备在9998端口接收数据
        DatagramSocket socket = new DatagramSocket(9998);
        // 2.将需要发送的数据,封装到DatagramPacket对象
        // 从键盘读取数据
        System.out.println("请输入你的问题:");
        Scanner scanner = new Scanner(System.in);
        String question = scanner.next();
        byte[] data = question.getBytes();
        // 说明:封装的DatagramPacket对象 data内容字节数组,data.length, 主机(IP),端口
        DatagramPacket packet =
                new DatagramPacket(data, data.length, InetAddress.getByName("192.168.1.107"), 8888);
        socket.send(packet);
        // 3.接收发送端A发送的数据报
        data = new byte[1024];
        packet = new DatagramPacket(data, data.length);
        // 4.数据填充到packaet
        socket.receive(packet);
        // 5.拆包
        data = packet.getData();
        int length = packet.getLength();
        String s = new String(data, 0, length);
        System.out.println(s);

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

三、网络文件下载

要求:

  • 编写客户端程序和服务端程序
  • 客户端可以输入一个音乐文件名,比如高山流水,服务端收到音乐名后,可以给客户端返回个音乐文件,如果服务器没有这个文件,返回一个默认的音乐即可。
  • 客户端收到文件后,保存到本地d:\

图解:
在这里插入图片描述
代码实现:

  • 服务端:
package com.cuinn.homework;

import com.cuinn.upload.StreamUtils;

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

/**
 * @author ckwu
 * @version 1.0
 * @project JavaProject
 * @description 服务端
 * @date 2022-10-06 22:15:42
 */
public class Homework03Server {
    public static void main(String[] args) throws Exception {
        // 1.创建服务端,在9999端口监听
        ServerSocket serverSocket = new ServerSocket(9999);
        // 2.等待连接
        System.out.println("等待客户端连接...");
        Socket socket = serverSocket.accept();
        // 3.读取客户端写入数据通道的数据
        InputStream inputStream = socket.getInputStream();
        byte[] buf = new byte[1024];
        int len = 0;
        String downloadFileName = "";
        while((len = inputStream.read(buf)) != -1){
            downloadFileName += new String(buf, 0, len);
        }
        System.out.println("客户端希望下载的文件名:" + downloadFileName);
        // 如果客户下载的是空心,返回空心.mp3,否则一律返回声声唱.mp3
        String resFileName = "";
        if("空心".equals(downloadFileName)){
            resFileName = "src\\空心.mp3";
        }else {
            resFileName = "src\\声声唱.mp3";
        }
        // 4.创建一个输入流读取文件
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(resFileName));
        // 5.使用工具类读取到一个字节数组
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        // 6.得到socket关联的输出流
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        // 7.写入到数据通道,返回给客户端
        bos.write(bytes);
        socket.shutdownOutput();// 设置结束标记

        // 8.关闭相关资源
        bis.close();
        inputStream.close();
        socket.close();
        serverSocket.close();
        System.out.println("服务端退出...");
    }

}
  • 客户端:
package com.cuinn.homework;

import com.cuinn.upload.StreamUtils;

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

/**
 * @author ckwu
 * @version 1.0
 * @project JavaProject
 * @description 客户端
 * @date 2022-10-06 22:16:04
 */
public class Homework03Client {
    public static void main(String[] args) throws Exception {
        // 1.接收用户指定下载的文件名
        System.out.println("请输入要下载的文件名;");
        Scanner scanner = new Scanner(System.in);
        String fileName = scanner.next();
        // 2.连接服务端,准备发送文件名
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
        // 3.获取要写入文件名的输出流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write(fileName.getBytes());
        // 设置写入结束标记
        socket.shutdownOutput();
        // 4.读取服务端返回的字节数据
        BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        // 5.得到一个输出流,准备将bytes写入到磁盘
        String destFileName = "d:\\" + fileName + ".mp3";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFileName));
        bos.write(bytes);
        // 6.关闭相关资源
        bos.close();
        bis.close();
        outputStream.close();
        socket.close();
        System.out.println("客户端下载完毕,正确退出...");
    }
}

工具类:

package com.cuinn.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.close();
        return array;
    }

    /**
     * 功能,将输入流获取的内容转换成字符串
     * @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){
            builder.append(line+"\r\n");
        }
        return builder.toString();
    }


}

总结

网络章节的相关知识及案例分了几篇文章来讲解,这样或许更详细点,不至于显得一篇下来,文章内容臃肿,接下来会写一些实战类类似于聊天的功能,希望支持,和大家一起进步。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

程序员阿坤...

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

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

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

打赏作者

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

抵扣说明:

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

余额充值