1. 网络相关概念

网络通信

第19章 网络编程 - 图1

网络

第19章 网络编程 - 图2

ip地址

第19章 网络编程 - 图3

ipv4 地址分类

第19章 网络编程 - 图4

第19章 网络编程 - 图5

域名

第19章 网络编程 - 图6

网络通信协议

第19章 网络编程 - 图7

第19章 网络编程 - 图8

网络通信协议

第19章 网络编程 - 图9

TCP 和 UDP

第19章 网络编程 - 图10

2. InetAddress 类

相关方法

  1. 获取本机InetAddress对象 getLocalHost
  2. 根据指定主机名/域名获取ip地址对象 getByName
  3. 获取InetAddress对象的主机名getHostName
  4. 获取InetAddress对象的地址 getHostAddress
  1. public class API_ {
  2. public static void main(String[] args) throws UnknownHostException {
  3. //1. 获取本机的InetAddress 对象
  4. InetAddress localHost = InetAddress.getLocalHost();
  5. System.out.println(localHost);//DESKTOP-S4MP84S/192.168.12.1
  6. //2. 根据指定主机名 获取 InetAddress对象
  7. InetAddress host1 = InetAddress.getByName("DESKTOP-S4MP84S");
  8. System.out.println("host1=" + host1);//DESKTOP-S4MP84S/192.168.12.1
  9. //3. 根据域名返回 InetAddress对象, 比如 www.baidu.com 对应
  10. InetAddress host2 = InetAddress.getByName("www.baidu.com");
  11. System.out.println("host2=" + host2);//www.baidu.com / 110.242.68.4
  12. //4. 通过 InetAddress 对象,获取对应的地址
  13. String hostAddress = host2.getHostAddress();//IP 110.242.68.4
  14. System.out.println("host2 对应的ip = " + hostAddress);//110.242.68.4
  15. //5. 通过 InetAddress 对象,获取对应的主机名/或者的域名
  16. String hostName = host2.getHostName();
  17. System.out.println("host2对应的主机名/域名=" + hostName); // www.baidu.com
  18. }
  19. }

3. Socket

基本介绍

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

示意图:

第19章 网络编程 - 图11

4. TCP 网络通信编程

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

第19章 网络编程 - 图12

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

第19章 网络编程 - 图13

第19章 网络编程 - 图14

客户端

  1. public class SocketTCP01Client {
  2. public static void main(String[] args) throws IOException {
  3. //思路
  4. //1. 连接服务端 (ip , 端口)
  5. //解读: 连接本机的 9999端口, 如果连接成功,返回Socket对象
  6. Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
  7. System.out.println("客户端 socket返回=" + socket.getClass());
  8. //2. 连接上后,生成Socket, 通过socket.getOutputStream()
  9. // 得到 和 socket对象关联的输出流对象
  10. OutputStream outputStream = socket.getOutputStream();
  11. //3. 通过输出流,写入数据到 数据通道
  12. outputStream.write("hello, server".getBytes());
  13. //4. 关闭流对象和socket, 必须关闭
  14. outputStream.close();
  15. socket.close();
  16. System.out.println("客户端退出.....");
  17. }
  18. }

服务端

  1. public class SocketTCP01Server {
  2. public static void main(String[] args) throws IOException {
  3. //思路
  4. //1. 在本机 的9999端口监听, 等待连接
  5. // 细节: 要求在本机没有其它服务在监听9999
  6. // 细节:这个 ServerSocket 可以通过 accept() 返回多个Socket[多个客户端连接服务器的并发]
  7. ServerSocket serverSocket = new ServerSocket(9999);
  8. System.out.println("服务端,在9999端口监听,等待连接..");
  9. //2. 当没有客户端连接9999端口时,程序会 阻塞, 等待连接
  10. // 如果有客户端连接,则会返回Socket对象,程序继续
  11. Socket socket = serverSocket.accept();
  12. System.out.println("服务端 socket =" + socket.getClass());
  13. //
  14. //3. 通过socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
  15. InputStream inputStream = socket.getInputStream();
  16. //4. IO读取
  17. byte[] buf = new byte[1024];
  18. int readLen = 0;
  19. while ((readLen = inputStream.read(buf)) != -1) {
  20. System.out.println(new String(buf, 0, readLen));//根据读取到的实际长度,显示内容.
  21. }
  22. //5.关闭流和socket
  23. inputStream.close();
  24. socket.close();
  25. serverSocket.close();//关闭
  26. }
  27. }

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

第19章 网络编程 - 图15

第19章 网络编程 - 图16

客户端

  1. public class SocketTCP02Client {
  2. public static void main(String[] args) throws IOException {
  3. //思路
  4. //1. 连接服务端 (ip , 端口)
  5. //解读: 连接本机的 9999端口, 如果连接成功,返回Socket对象
  6. Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
  7. System.out.println("客户端 socket返回=" + socket.getClass());
  8. //2. 连接上后,生成Socket, 通过socket.getOutputStream()
  9. // 得到 和 socket对象关联的输出流对象
  10. OutputStream outputStream = socket.getOutputStream();
  11. //3. 通过输出流,写入数据到 数据通道
  12. outputStream.write("hello, server".getBytes());
  13. // 设置结束标记
  14. socket.shutdownOutput();
  15. //4. 获取和socket关联的输入流. 读取数据(字节),并显示
  16. InputStream inputStream = socket.getInputStream();
  17. byte[] buf = new byte[1024];
  18. int readLen = 0;
  19. while ((readLen = inputStream.read(buf)) != -1) {
  20. System.out.println(new String(buf, 0, readLen));
  21. }
  22. //5. 关闭流对象和socket, 必须关闭
  23. inputStream.close();
  24. outputStream.close();
  25. socket.close();
  26. System.out.println("客户端退出.....");
  27. }
  28. }

服务端

  1. public class SocketTCP02Server {
  2. public static void main(String[] args) throws IOException {
  3. //思路
  4. //1. 在本机 的9999端口监听, 等待连接
  5. // 细节: 要求在本机没有其它服务在监听9999
  6. // 细节:这个 ServerSocket 可以通过 accept() 返回多个Socket[多个客户端连接服务器的并发]
  7. ServerSocket serverSocket = new ServerSocket(9999);
  8. System.out.println("服务端,在9999端口监听,等待连接..");
  9. //2. 当没有客户端连接9999端口时,程序会 阻塞, 等待连接
  10. // 如果有客户端连接,则会返回Socket对象,程序继续
  11. Socket socket = serverSocket.accept();
  12. System.out.println("服务端 socket =" + socket.getClass());
  13. //
  14. //3. 通过socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
  15. InputStream inputStream = socket.getInputStream();
  16. //4. IO读取
  17. byte[] buf = new byte[1024];
  18. int readLen = 0;
  19. while ((readLen = inputStream.read(buf)) != -1) {
  20. System.out.println(new String(buf, 0, readLen));//根据读取到的实际长度,显示内容.
  21. }
  22. //5. 获取socket相关联的输出流
  23. OutputStream outputStream = socket.getOutputStream();
  24. outputStream.write("hello, client".getBytes());
  25. // 设置结束标记
  26. socket.shutdownOutput();
  27. //6.关闭流和socket
  28. outputStream.close();
  29. inputStream.close();
  30. socket.close();
  31. serverSocket.close();//关闭
  32. }
  33. }

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

第19章 网络编程 - 图17

客户端:

  1. public class SocketTCP03Client {
  2. public static void main(String[] args) throws IOException {
  3. //思路
  4. //1. 连接服务端 (ip , 端口)
  5. //解读: 连接本机的 9999端口, 如果连接成功,返回Socket对象
  6. Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
  7. System.out.println("客户端 socket返回=" + socket.getClass());
  8. //2. 连接上后,生成Socket, 通过socket.getOutputStream()
  9. // 得到 和 socket对象关联的输出流对象
  10. OutputStream outputStream = socket.getOutputStream();
  11. //3. 通过输出流,写入数据到 数据通道, 使用字符流
  12. BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
  13. bufferedWriter.write("hello, server 字符流");
  14. bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束, 注意,要求对方使用readLine()!!!!
  15. bufferedWriter.flush();// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道
  16. //4. 获取和socket关联的输入流. 读取数据(字符),并显示
  17. InputStream inputStream = socket.getInputStream();
  18. BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
  19. String s = bufferedReader.readLine();
  20. System.out.println(s);
  21. //5. 关闭流对象和socket, 必须关闭
  22. bufferedReader.close();//关闭外层流
  23. bufferedWriter.close();
  24. socket.close();
  25. System.out.println("客户端退出.....");
  26. }
  27. }

服务端:

  1. public class SocketTCP03Server {
  2. public static void main(String[] args) throws IOException {
  3. //思路
  4. //1. 在本机 的9999端口监听, 等待连接
  5. // 细节: 要求在本机没有其它服务在监听9999
  6. // 细节:这个 ServerSocket 可以通过 accept() 返回多个Socket[多个客户端连接服务器的并发]
  7. ServerSocket serverSocket = new ServerSocket(9999);
  8. System.out.println("服务端,在9999端口监听,等待连接..");
  9. //2. 当没有客户端连接9999端口时,程序会 阻塞, 等待连接
  10. // 如果有客户端连接,则会返回Socket对象,程序继续
  11. Socket socket = serverSocket.accept();
  12. System.out.println("服务端 socket =" + socket.getClass());
  13. //
  14. //3. 通过socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
  15. InputStream inputStream = socket.getInputStream();
  16. //4. IO读取, 使用字符流, 老师使用 InputStreamReader 将 inputStream 转成字符流
  17. BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
  18. String s = bufferedReader.readLine();
  19. System.out.println(s);//输出
  20. //5. 获取socket相关联的输出流
  21. OutputStream outputStream = socket.getOutputStream();
  22. // 使用字符输出流的方式回复信息
  23. BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
  24. bufferedWriter.write("hello client 字符流");
  25. bufferedWriter.newLine();// 插入一个换行符,表示回复内容的结束
  26. bufferedWriter.flush();//注意需要手动的flush
  27. //6.关闭流和socket
  28. bufferedWriter.close();
  29. bufferedReader.close();
  30. socket.close();
  31. serverSocket.close();//关闭
  32. }
  33. }

bufferedWriter.newLine() 插入一个换行符,表示写入的内容结束, 注意,要求对方使用readLine()!!!!

bufferedWriter.flush() 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道

应用案例 4 : 网络上传文件

思路 : 从磁盘上读取文件到客户端存到一个文件数据对应的字节数组中 - 用Socket把文件传到数据通道 - 在服务端用Socket把文件读到文件数据对应的字节数组中 - 再把文件刷新到磁盘上 - 刷新后服务端通过Socket得到一个输出流 - 通过数据通道 - 客户端通过Socket把数据通道中的消息读出来 - 在输出到显示器中显示出来.

客户端

  1. public class TCPFileUploadClient {
  2. public static void main(String[] args) throws Exception {
  3. //客户端连接服务端 8888,得到Socket对象
  4. Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
  5. //创建读取磁盘文件的输入流
  6. //String filePath = "e:\\qie.png";
  7. String filePath = "e:\\abc.mp4";
  8. BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
  9. //bytes 就是filePath对应的字节数组
  10. byte[] bytes = StreamUtils.streamToByteArray(bis);
  11. //通过socket获取到输出流, 将bytes数据发送给服务端
  12. BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
  13. bos.write(bytes);//将文件对应的字节数组的内容,写入到数据通道
  14. bis.close();
  15. socket.shutdownOutput();//设置写入数据的结束标记
  16. //=====接收从服务端回复的消息=====
  17. InputStream inputStream = socket.getInputStream();
  18. //使用StreamUtils 的方法,直接将 inputStream 读取到的内容 转成字符串
  19. String s = StreamUtils.streamToString(inputStream);
  20. System.out.println(s);
  21. //关闭相关的流
  22. inputStream.close();
  23. bos.close();
  24. socket.close();
  25. }

服务端

  1. public class TCPFileUploadServer {
  2. public static void main(String[] args) throws Exception {
  3. //1. 服务端在本机监听8888端口
  4. ServerSocket serverSocket = new ServerSocket(8888);
  5. System.out.println("服务端在8888端口监听....");
  6. //2. 等待连接
  7. Socket socket = serverSocket.accept();
  8. //3. 读取客户端发送的数据
  9. // 通过Socket得到输入流
  10. BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
  11. byte[] bytes = StreamUtils.streamToByteArray(bis);
  12. //4. 将得到 bytes 数组,写入到指定的路径,就得到一个文件了
  13. String destFilePath = "D:/hh.jpg";
  14. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
  15. bos.write(bytes);
  16. bos.close();
  17. // 向客户端回复 "收到图片"
  18. // 通过socket 获取到输出流(字符)
  19. BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
  20. writer.write("收到图片");
  21. writer.flush();//把内容刷新到数据通道
  22. socket.shutdownOutput();//设置写入结束标记
  23. //关闭其他资源
  24. writer.close();
  25. bis.close();
  26. socket.close();
  27. serverSocket.close();
  28. }
  29. }

StreamUtils.java

  1. public class StreamUtils {
  2. /**
  3. * 功能:将输入流转换成byte[], 即可以把文件的内容读入到byte[]
  4. * @param is
  5. * @return
  6. * @throws Exception
  7. */
  8. public static byte[] streamToByteArray(InputStream is) throws Exception{
  9. ByteArrayOutputStream bos = new ByteArrayOutputStream();//创建输出流对象
  10. byte[] b = new byte[1024];//字节数组
  11. int len;
  12. while((len=is.read(b))!=-1){//循环读取
  13. bos.write(b, 0, len);//把读取到的数据,写入bos
  14. }
  15. byte[] array = bos.toByteArray();//然后将bos 转成字节数组
  16. bos.close();
  17. return array;
  18. }
  19. /**
  20. * 功能:将InputStream转换成String
  21. * @param is
  22. * @return
  23. * @throws Exception
  24. */
  25. public static String streamToString(InputStream is) throws Exception{
  26. BufferedReader reader = new BufferedReader(new InputStreamReader(is));
  27. StringBuilder builder= new StringBuilder();
  28. String line;
  29. while((line=reader.readLine())!=null){ //当读取到null时,就表示结束
  30. builder.append(line+"\r\n");
  31. }
  32. return builder.toString();
  33. }
  34. }

5. netstat 指令

  1. netstat -an 可以查看当前主机网络情况,包括端口监听情况和网络连接情况
  2. netstat -an | more 可以分页显示
  3. 要求在dos控制台下执行
    说明:
    1. Listening 表示某个端口在监听
    2. 如果有一个外部程序(客户端)连接到该端口,就会显示一条连接信息.
    3. 可以输入ctrl+c 退出指令

第19章 网络编程 - 图18

6. TCP 网络通讯不为人知的秘密

第19章 网络编程 - 图19

  1. 当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是TCP/IP 协议来分
    配的,是不确定的,是随机的。
  2. 示意图
  3. 程序验证+netstat

7. UDP 网络通信编程(了解)

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

基本流程

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

第19章 网络编程 - 图20

构造器

第19章 网络编程 - 图21

应用案例

第19章 网络编程 - 图22

发送端A:

  1. public class UDPReceiverA {
  2. public static void main(String[] args) throws IOException {
  3. //1. 创建一个 DatagramSocket 对象,准备在9999接收数据
  4. DatagramSocket socket = new DatagramSocket(9999);
  5. //2. 构建一个 DatagramPacket 对象,准备接收数据
  6. // 在前面讲解UDP 协议时,老师说过一个数据包最大 64k
  7. byte[] buf = new byte[1024];
  8. DatagramPacket packet = new DatagramPacket(buf, buf.length);
  9. //3. 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
  10. // 填充到 packet对象
  11. //老师提示: 当有数据包发送到 本机的9999端口时,就会接收到数据
  12. // 如果没有数据包发送到 本机的9999端口, 就会阻塞等待.
  13. System.out.println("接收端A 等待接收数据..");
  14. socket.receive(packet);
  15. //4. 可以把packet 进行拆包,取出数据,并显示.
  16. int length = packet.getLength();//实际接收到的数据字节长度
  17. byte[] data = packet.getData();//接收到数据
  18. String s = new String(data, 0, length);
  19. System.out.println(s);
  20. //===回复信息给B端
  21. //将需要发送的数据,封装到 DatagramPacket对象
  22. data = "好的, 明天见".getBytes();
  23. //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
  24. packet =
  25. new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9998);
  26. socket.send(packet);//发送
  27. //5. 关闭资源
  28. socket.close();
  29. System.out.println("A端退出...");
  30. }
  31. }

接收端B:

  1. public class UDPSenderB {
  2. public static void main(String[] args) throws IOException {
  3. //1.创建 DatagramSocket 对象,准备在9998端口 接收数据
  4. DatagramSocket socket = new DatagramSocket(9998);
  5. //2. 将需要发送的数据,封装到 DatagramPacket对象
  6. byte[] data = "hello 明天吃火锅~".getBytes(); //
  7. //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
  8. DatagramPacket packet =
  9. new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9999);
  10. socket.send(packet);
  11. //3.=== 接收从A端回复的信息
  12. //(1) 构建一个 DatagramPacket 对象,准备接收数据
  13. // 在前面讲解UDP 协议时,老师说过一个数据包最大 64k
  14. byte[] buf = new byte[1024];
  15. packet = new DatagramPacket(buf, buf.length);
  16. //(2) 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
  17. // 填充到 packet对象
  18. //老师提示: 当有数据包发送到 本机的9998端口时,就会接收到数据
  19. // 如果没有数据包发送到 本机的9998端口, 就会阻塞等待.
  20. socket.receive(packet);
  21. //(3) 可以把packet 进行拆包,取出数据,并显示.
  22. int length = packet.getLength();//实际接收到的数据字节长度
  23. data = packet.getData();//接收到数据
  24. String s = new String(data, 0, length);
  25. System.out.println(s);
  26. //关闭资源
  27. socket.close();
  28. System.out.println("B端退出");
  29. }
  30. }

8. 网络编程作业

第19章 网络编程 - 图23

客户端(先写)

  1. public class Homework01Client {
  2. public static void main(String[] args) {
  3. try (
  4. //先连接
  5. Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
  6. //连接上后 创建写入流
  7. BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
  8. //创建读取流
  9. BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))
  10. ) {
  11. //从键盘读取用户的问题
  12. Scanner scanner = new Scanner(System.in);
  13. System.out.println("请输入你的问题");
  14. String question = scanner.next();
  15. //将问题写入连接
  16. bw.write(question);
  17. bw.newLine();//插入一个换行符,表示写入的内容结束, 注意,要求对方使用readLine()!!!!
  18. bw.flush();// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道
  19. //读取流中的数据
  20. String s = br.readLine();
  21. System.out.println(s);
  22. } catch (IOException e) {
  23. e.printStackTrace();
  24. }
  25. }
  26. }

服务端

  1. public class Homework01Server {
  2. public static void main(String[] args) throws IOException {
  3. //思路
  4. //1. 在本机 的9999端口监听, 等待连接
  5. // 细节: 要求在本机没有其它服务在监听9999
  6. // 细节:这个 ServerSocket 可以通过 accept() 返回多个Socket[多个客户端连接服务器的并发]
  7. ServerSocket serverSocket = new ServerSocket(9999);
  8. System.out.println("服务端,在9999端口监听,等待连接..");
  9. //2. 当没有客户端连接9999端口时,程序会 阻塞, 等待连接
  10. // 如果有客户端连接,则会返回Socket对象,程序继续
  11. Socket socket = serverSocket.accept();
  12. //
  13. //3. 通过socket.getInputStream() 读取客户端写入到数据通道的数据, 显示
  14. InputStream inputStream = socket.getInputStream();
  15. //4. IO读取, 使用字符流, 老师使用 InputStreamReader 将 inputStream 转成字符流
  16. BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
  17. String s = bufferedReader.readLine();
  18. String answer = "";
  19. if ("name".equals(s)) {
  20. answer = "我是韩顺平";
  21. } else if("hobby".equals(s)) {
  22. answer = "编写java程序";
  23. } else {
  24. answer = "你说的啥子";
  25. }
  26. //5. 获取socket相关联的输出流
  27. OutputStream outputStream = socket.getOutputStream();
  28. // 使用字符输出流的方式回复信息
  29. BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
  30. bufferedWriter.write(answer);
  31. bufferedWriter.newLine();// 插入一个换行符,表示回复内容的结束
  32. bufferedWriter.flush();//注意需要手动的flush
  33. //6.关闭流和socket
  34. bufferedWriter.close();
  35. bufferedReader.close();
  36. socket.close();
  37. serverSocket.close();//关闭
  38. }
  39. }

第19章 网络编程 - 图24

发送端(先写)

  1. public class Homework02SenderB {
  2. public static void main(String[] args) {
  3. try (
  4. //创建DatagramSocket对象,在9998接受数据
  5. DatagramSocket socket = new DatagramSocket(9998);
  6. ) {
  7. //将要发送的数据包装DatagramPacket对象
  8. Scanner scanner = new Scanner(System.in);
  9. System.out.println("请输入你的问题: ");
  10. String question = scanner.next();
  11. //创建数据包
  12. byte[] data = question.getBytes();
  13. //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
  14. DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.231.1"),8888);
  15. //发送
  16. socket.send(packet);
  17. //=== 接收从A端回复的信息
  18. //(1) 构建一个 DatagramPacket 对象,准备接收数据
  19. // 在前面讲解UDP 协议时,老师说过一个数据包最大 64k
  20. byte[] buf = new byte[1024];
  21. packet = new DatagramPacket(buf, buf.length);
  22. //(2) 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
  23. // 填充到 packet对象
  24. //老师提示: 当有数据包发送到 本机的9998端口时,就会接收到数据
  25. // 如果没有数据包发送到 本机的9998端口, 就会阻塞等待.
  26. socket.receive(packet);
  27. //(3) 可以把packet 进行拆包,取出数据,并显示.
  28. int length = packet.getLength();//实际接收到的数据字节长度
  29. data = packet.getData();//接收到数据
  30. String s = new String(data, 0, length);
  31. System.out.println(s);
  32. } catch (IOException e) {
  33. e.printStackTrace();
  34. }
  35. System.out.println("B端退出");
  36. }
  37. }

接收端

  1. public class Homework02ReceiverA {
  2. public static void main(String[] args) {
  3. try (
  4. //创建一个 DatagramSocket 对象,准备在8888接收数据
  5. DatagramSocket socket = new DatagramSocket(8888);
  6. ) {
  7. //构建一个 DatagramPacket 对象,准备接收数据
  8. //在前面讲解UDP 协议时,老师说过一个数据包最大 64k
  9. //创建一个数据包
  10. byte[] buf = new byte[1024];
  11. DatagramPacket packet = new DatagramPacket(buf,buf.length);
  12. //调用 接收方法, 将通过网络传输的 DatagramPacket 对象
  13. //填充到 packet对象
  14. System.out.println("接收端 等待接收问题 ");
  15. socket.receive(packet);
  16. //可以把packet 进行拆包,取出数据,并显示.
  17. int length = packet.getLength(); //实际接收到的数据字节长度
  18. byte[] data = packet.getData();//接收到数据
  19. String s = new String(data,0, length);
  20. //判断接收到的数据是什么
  21. String answer = "";
  22. if("四大名著".equals(s)) {
  23. answer = "四大名著 <<红楼梦>> <<三国演示>> <<西游记>> <<水浒传>>";
  24. } else {
  25. answer = "what?";
  26. }
  27. //===回复信息给B端
  28. //将需要发送的数据,封装到 DatagramPacket对象
  29. data = answer.getBytes();
  30. //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
  31. packet = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.231.1"), 9998);
  32. socket.send(packet);//发送
  33. } catch (IOException e) {
  34. e.printStackTrace();
  35. }
  36. System.out.println("A端退出...");
  37. }
  38. }

第19章 网络编程 - 图25

第19章 网络编程 - 图26

客户端

  1. public class Homework03Client {
  2. public static void main(String[] args) {
  3. //接收用户输入,指定文件下载名
  4. Scanner scanner = new Scanner(System.in);
  5. System.out.println("请输入下载的文件名");
  6. String downloadFileName = scanner.next();
  7. try (
  8. //客户端连接服务器
  9. Socket socket = new Socket(InetAddress.getLocalHost(), 9999);
  10. //获取和Socket关联的输出流
  11. OutputStream outputStream = socket.getOutputStream();
  12. ) {
  13. //获取和Socket关联的输出流
  14. outputStream.write(downloadFileName.getBytes());
  15. //设置写入结束的标志
  16. socket.shutdownOutput();
  17. //读取服务端返回的文件(字节数据)
  18. BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
  19. byte[] bytes = StreamUtils.streamToByteArray(bis);
  20. //得到一个输出流,准备将 bytes 写入到磁盘文件
  21. //比如你下载的是 高山流水 => 下载的就是 高山流水.mp3
  22. // 你下载的是 韩顺平 => 下载的就是 无名.mp3 文件名 韩顺平.mp3
  23. String filePath = "D:\\mydat\\" + downloadFileName + ".mp3";
  24. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
  25. bos.write(bytes);
  26. System.out.println("客户端下载完毕,正确退出..");
  27. } catch (Exception e) {
  28. e.printStackTrace();
  29. }
  30. }
  31. }

服务端

  1. public class Homework03Server {
  2. public static void main(String[] args) {
  3. try (
  4. //1 监听 9999端口
  5. ServerSocket serverSocket = new ServerSocket(9999)
  6. ) {
  7. //2.等待客户端连接
  8. System.out.println("服务端,在9999端口监听,等待下载文件");
  9. Socket socket = serverSocket.accept();
  10. //3.读取 客户端发送要下载的文件名
  11. // 这里老师使用了while读取文件名,时考虑将来客户端发送的数据较大的情况
  12. InputStream inputStream = socket.getInputStream();
  13. byte[] data = new byte[1024];
  14. int len = 0;
  15. String downLoadFileName = "";
  16. while ((len = inputStream.read(data)) != -1) {
  17. downLoadFileName += new String(data, 0 , len);
  18. }
  19. System.out.println("客户端希望下载文件名=" + downLoadFileName);
  20. //老师在服务器上有两个文件, 无名.mp3 高山流水.mp3
  21. //如果客户下载的是 高山流水 我们就返回该文件,否则一律返回 无名.mp3
  22. String resFileName = "";
  23. if("高山流水".equals(downLoadFileName)) {
  24. resFileName = "chapter21\\src\\高山流水.mp3";
  25. } else {
  26. resFileName = "chapter21\\src\\无名.mp3";
  27. }
  28. //4. 创建一个输入流,读取文件
  29. BufferedInputStream bis =
  30. new BufferedInputStream(new FileInputStream(resFileName));
  31. //5. 使用工具类StreamUtils ,读取文件到一个字节数组
  32. byte[] bytes = StreamUtils.streamToByteArray(bis);
  33. //6. 得到Socket关联的输出流
  34. BufferedOutputStream bos =
  35. new BufferedOutputStream(socket.getOutputStream());
  36. //7. 写入到数据通道,返回给客户端
  37. bos.write(bytes);
  38. socket.shutdownOutput();//很关键.
  39. System.out.println("服务端退出...");
  40. } catch (Exception e) {
  41. e.printStackTrace();
  42. }
  43. }
  44. }

tips:服务端下载的是什么我们不知道,我们只能在客户端设置下载文件的名称,但是文件的内容是由服务端决定的