关于java:Java-网络编程详解

47次阅读

共计 23334 个字符,预计需要花费 59 分钟才能阅读完成。

文章和代码曾经归档至【Github 仓库:https://github.com/timerring/java-tutorial】或者公众号【AIShareLab】回复 java 也可获取。

网络的相干概念

网络通信

  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 地址分类

非凡的: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

网络通信协定

协定(tcp/ip)

TCP/IP (Transmission Control Protocol/Internet Protocol) 的简写, 中文译名为传输控制协议 / 因特网互联协定, 又叫网络通讯协定, 这个协定是 lnternet 最根本的协定、Internet 国际互联网络的根底, 简略地说, 就是由网络层的 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

利用案例

编写代码,获取计算机的主机名和 IP 地址相干 API

package com.hspedu.api;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 演示 InetAddress 类的应用
 */
public class API_ {public static void main(String[] args) throws UnknownHostException {

        //1. 获取本机的 InetAddress 对象
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost);//DESKTOP-S4MP84S/192.168.12.1

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

        //3. 依据域名返回 InetAddress 对象, 比方 www.baidu.com 对应
        InetAddress host2 = InetAddress.getByName("www.baidu.com");
        System.out.println("host2=" + host2);//www.baidu.com / 110.242.68.4

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

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

Socket

根本介绍

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

示意图

TCP 网络通信编程

根本介绍

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

最初须要敞开 socket,不然链接太多会呈现问题。

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

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

ServerSocket 能够通过 accept() 返回多个 Socket[多个客户端连贯服务器的并发]

package com.hspedu.socket;

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

/**
 * 客户端,发送 "hello, server" 给服务端
 */
public class SocketTCP01Client {public static void main(String[] args) throws IOException {
        // 思路
        //1. 连贯服务端 (ip , 端口)// 解读: 连贯本机的 9999 端口, 如果连贯胜利,返回 Socket 对象
        // 如果链接网络,第一个参数能够改为 IP
        Socket socket = new Socket(InetAddress.getLocalHost(), 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("客户端退出.....");
    }
}
package com.hspedu.socket;

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

/**
 * 服务端
 */
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[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {System.out.println(new String(buf, 0, readLen));// 依据读取到的理论长度,显示内容.
        }
        //5. 敞开流和 socket
        inputStream.close();
        socket.close();
        serverSocket.close();// 最初也须要敞开}
}

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

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

留神:设置完结标记。确保输入完结。

package com.hspedu.socket;

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

/**
 * 服务端
 */
@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[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {System.out.println(new String(buf, 0, readLen));// 依据读取到的理论长度,显示内容.
        }
        //5. 获取 socket 相关联的输入流
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello, client".getBytes());
        // 设置完结标记
        socket.shutdownOutput();

        //6. 敞开流和 socket
        outputStream.close();
        inputStream.close();
        socket.close();
        serverSocket.close();// 敞开}
}
package com.hspedu.socket;

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

/**
 * 客户端,发送 "hello, server" 给服务端
 */
@SuppressWarnings({"all"})
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.getOutputStream()
        //   失去 和 socket 对象关联的输入流对象
        OutputStream outputStream = socket.getOutputStream();
        //3. 通过输入流,写入数据到 数据通道
        outputStream.write("hello, server".getBytes());
        //   设置完结标记
        socket.shutdownOutput();

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

        //5. 敞开流对象和 socket, 必须敞开
        inputStream.close();
        outputStream.close();
        socket.close();
        System.out.println("客户端退出.....");
    }
}

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

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

这里完结标记也能够采纳 writer.newLine();,然而这要求对方读取必须应用 readLine()

package com.hspedu.socket;

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

/**
 * 客户端,发送 "hello, server" 给服务端,应用字符流
 */
@SuppressWarnings({"all"})
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.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();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);

        //5. 敞开流对象和 socket, 必须敞开
        bufferedReader.close();// 敞开外层流
        bufferedWriter.close();
        socket.close();
        System.out.println("客户端退出.....");
    }
}
package com.hspedu.socket;

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

/**
 * 服务端, 应用字符流形式读写
 */
@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
        bufferedWriter.close();
        bufferedReader.close();
        socket.close();
        serverSocket.close();// 敞开}
}

利用案例 4

  1. 编写一个服务端, 和一个客户端
  2. 服务器端在 8888 端口监听
  3. 客户端连贯到服务端, 发送一张图片 e:llqie.png
  4. 服务器端接管到客户端发送的图片,保留到 src 下, 发送“收到图片 ” 再退出
  5. 客户端接管到服务端发送的“收到图片”,再退出
  6. 该程序要求应用 StreamUtils.java, 咱们间接应用其中封装好的办法。
package com.hspedu.upload;

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


/**
 * 文件上传的客户端
 */
public class TCPFileUploadClient {public static void main(String[] args) throws Exception {

        // 客户端连贯服务端 8888,失去 Socket 对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
        // 创立读取磁盘文件的输出流
        //String filePath = "e:\\qie.png";
        String filePath = "e:\\abc.mp4";
        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();}
}
package com.hspedu.upload;

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

/**
 * 文件上传的服务端
 */
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 = "src\\abc.mp4";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath));
        bos.write(bytes);
        bos.close();

        // 向客户端回复 "收到图片"
        // 通过 socket 获取到输入流(字符)
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        writer.write("收到图片");
        writer.flush();// 把内容刷新到数据通道
        socket.shutdownOutput();// 设置写入完结标记

        // 敞开其余资源
        writer.close();
        bis.close();
        socket.close();
        serverSocket.close();}
}
package com.hspedu.upload;

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

/**
 * 此类用于演示对于流的读写办法
 *
 */
public class StreamUtils {
   /**
    * 性能:将输出流转换成 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;
   }
   /**
    * 性能:将 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();}

}

netstat 指令

  1. netstat -an 能够查看以后主机网络状况,包含项口监听状况和网络连接状况
  2. netstat -an | more 能够分页显示
  3. netstat -anb(在管理员状态下运行)能够查看是哪些利用监听该端口。
  4. 内部地址就是 连贯 该本地地址和端口号的客户端的 IP 和端口号。

阐明:

(1) Listening 示意某个端口在监听 Established 示意连贯曾经建设

(2)如果有一个内部程序 (客户端) 连贯到该端口,就会显示一条连贯信息

TCP 网络通讯

  1. 当客户端连贯到服务端后,实际上客户端也是通过一个端口和服务端进行通信的,这个端口是 TCP/IP 来调配的,是不确定的,是随机的。(客户端的端口
  2. 程序验证 +netstat

UDP 网络通信编程

根本介绍

  1. DatagramSocketDatagramPacket[数据包 / 数据报]实现了基于 UDP
    协定网络程序。
  2. UDP 数据报通过数据报套接字DatagramSocket 发送和接管,零碎不保障 UDP 数据报肯定可能平安送到目的地,也不能确定什么时候能够到达。
  3. DatagramPacket 对象封装了 UDP 数据报,在数据报中蕴含了发送端的 IP 地址和
    端口号以及接收端的 IP 地址和端口号。
  4. UDP 协定中每个数据报都给出了残缺的地址信息,因而毋庸建设发送方和接管方
    的连贯

根本流程

  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. 发送端接管回复的数据, 再退出
package com.hspedu.udp;

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

/**
 * UDP 接收端
 */
public class UDPReceiverA {public static void main(String[] args) throws IOException {
        //1. 创立一个 DatagramSocket 对象,筹备在 9999 接收数据
        DatagramSocket socket = new DatagramSocket(9999);
        //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);


        //=== 回复信息给 B 端
        // 将须要发送的数据,封装到 DatagramPacket 对象
        data = "好的, 今天见".getBytes();
        // 阐明: 封装的 DatagramPacket 对象 data 内容字节数组 , data.length , 主机(IP) , 端口
        packet =
                new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9998);

        socket.send(packet);// 发送

        //5. 敞开资源
        socket.close();
        System.out.println("A 端退出...");

    }
}
package com.hspedu.udp;

import java.io.IOException;
import java.net.*;

/**
 * 发送端 B ====> 也能够接收数据
 */
@SuppressWarnings({"all"})
public class UDPSenderB {public static void main(String[] args) throws IOException {

        //1. 创立 DatagramSocket 对象,筹备在 9998 端口 接收数据
        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.12.1"), 9999);

        socket.send(packet);

        //3.=== 接管从 A 端回复的信息
        //(1)   构建一个 DatagramPacket 对象,筹备接收数据
        //   在后面解说 UDP 协定时,老师说过一个数据包最大 64k
        byte[] buf = new byte[1024];
        packet = new DatagramPacket(buf, buf.length);
        //(2)    调用 接管办法, 将通过网络传输的 DatagramPacket 对象
        //   填充到 packet 对象
        // 老师提醒: 当有数据包发送到 本机的 9998 端口时,就会接管到数据
        //   如果没有数据包发送到 本机的 9998 端口, 就会阻塞期待.
        socket.receive(packet);

        //(3)  能够把 packet 进行拆包,取出数据,并显示.
        int length = packet.getLength();// 理论接管到的数据字节长度
        data = packet.getData();// 接管到数据
        String s = new String(data, 0, length);
        System.out.println(s);

        // 敞开资源
        socket.close();
        System.out.println("B 端退出");
    }
}

本章作业

1. 编程题

(1)应用字符流的形式, 编写一个客户端程序和服务器端程序,

(2)客户端发送“name” 服务器端接管到后,返回“我是 nova “, nova 是你本人的名字.

(3)客户端发送 ”hobby”,服务器端接管到后,返回“编写 java 程序”

(4)不是这两个问题,回复“你说啥呢 ”

问题: 目前,咱们只能问一次,就退出了,怎么能够问屡次?->while -> 聊天

package com.hspedu.homework;

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

/**
 * 客户端,发送 "hello, server" 给服务端,应用字符流
 */
@SuppressWarnings({"all"})
public class Homework01Client {public static void main(String[] args) throws IOException {
        // 思路
        //1. 连贯服务端 (ip , 端口)// 解读: 连贯本机的 9999 端口, 如果连贯胜利,返回 Socket 对象
        Socket socket = new Socket(InetAddress.getLocalHost(), 9999);

        //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();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println(s);

        //5. 敞开流对象和 socket, 必须敞开
        bufferedReader.close();// 敞开外层流
        bufferedWriter.close();
        socket.close();
        System.out.println("客户端退出.....");
    }
}
package com.hspedu.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. 在本机 的 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 读取, 应用字符流, 老师应用 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
        bufferedWriter.close();
        bufferedReader.close();
        socket.close();
        serverSocket.close();// 敞开}
}

2. 编程题

(1)编写一个接收端 A, 和一个发送端 B, 应用 UDP 协定实现

(2)接收端在 8888 端口期待接收数据(receive)

(3)发送端向接收端发送数据“四大名著是哪些”

(4)接收端接管到发送端发送的问题后,返回“四大名著是 << 红楼梦 >>.….”,否则返回 what?

(5)接收端和发送端程序退出

package com.hspedu.homework;

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

/**
 * 发送端 B ====> 也能够接收数据
 */
@SuppressWarnings({"all"})
public class Homework02SenderB {public static void main(String[] args) throws IOException {

        //1. 创立 DatagramSocket 对象,筹备在 9998 端口 接收数据
        DatagramSocket socket = new DatagramSocket(9998);

        //2. 将须要发送的数据,封装到 DatagramPacket 对象
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输出你的问题:");
        String question = scanner.next();
        byte[] data = question.getBytes(); //

        // 阐明: 封装的 DatagramPacket 对象 data 内容字节数组 , data.length , 主机(IP) , 端口
        DatagramPacket packet =
                new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 8888);

        socket.send(packet);

        //3.=== 接管从 A 端回复的信息
        //(1)   构建一个 DatagramPacket 对象,筹备接收数据
        //   在后面解说 UDP 协定时,老师说过一个数据包最大 64k
        byte[] buf = new byte[1024];
        packet = new DatagramPacket(buf, buf.length);
        //(2)    调用 接管办法, 将通过网络传输的 DatagramPacket 对象
        //   填充到 packet 对象
        // 老师提醒: 当有数据包发送到 本机的 9998 端口时,就会接管到数据
        //   如果没有数据包发送到 本机的 9998 端口, 就会阻塞期待.
        socket.receive(packet);

        //(3)  能够把 packet 进行拆包,取出数据,并显示.
        int length = packet.getLength();// 理论接管到的数据字节长度
        data = packet.getData();// 接管到数据
        String s = new String(data, 0, length);
        System.out.println(s);

        // 敞开资源
        socket.close();
        System.out.println("B 端退出");
    }
}
package com.hspedu.homework;

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

/**
 * UDP 接收端
 */
@SuppressWarnings({"all"})
public class Homework02ReceiverA {public static void main(String[] args) throws IOException {
        //1. 创立一个 DatagramSocket 对象,筹备在 8888 接收数据
        DatagramSocket socket = new DatagramSocket(8888);
        //2. 构建一个 DatagramPacket 对象,筹备接收数据
        //   在后面解说 UDP 协定时,老师说过一个数据包最大 64k
        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);
        //3. 调用 接管办法, 将通过网络传输的 DatagramPacket 对象
        //   填充到 packet 对象
        System.out.println("接收端 期待接管问题");
        socket.receive(packet);

        //4. 能够把 packet 进行拆包,取出数据,并显示.
        int length = packet.getLength();// 理论接管到的数据字节长度
        byte[] data = packet.getData();// 接管到数据
        String s = new String(data, 0, length); // 将字节数组转为 string!!!!
        // 判断接管到的信息是什么
        String answer = "";
        if("四大名著是哪些".equals(s)) {answer = "四大名著 << 红楼梦 >> << 三国演示 >> << 西游记 >> << 水浒传 >>";} else {answer = "what?";}


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

        socket.send(packet);// 发送

        //5. 敞开资源
        socket.close();
        System.out.println("A 端退出...");

    }
}

3. 编程题

(1)编写客户端程序和服务器端程序

(2)客户端能够输出一个音乐文件名,比方高山流水, 服务端收到音乐名后,能够给客户端返回这个音乐文件,如果服务器没有这个文件,返回一个默认的音乐即可。

(3)客户端收到文件后,保留到本地 e:\\

(4)提醒: 该程序能够应用 StreamUtils.java

package com.hspedu.homework;

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

/**
 * 文件下载的客户端
 */
public class Homework03Client {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 bis = new BufferedInputStream(socket.getInputStream());
        byte[] bytes = StreamUtils.streamToByteArray(bis);
        //5. 失去一个输入流,筹备将 bytes 写入到磁盘文件
        // 比方你下载的是 高山流水 => 下载的就是 高山流水.mp3
        //    你下载的是 无名 => 下载的就是 无名.mp3
        String filePath = "e:\\" + downloadFileName + ".mp3";
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
        bos.write(bytes);

        //6. 敞开相干的资源
        bos.close();
        bis.close();
        outputStream.close();
        socket.close();

        System.out.println("客户端下载结束,正确退出..");


    }
}
package com.hspedu.homework;

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

/**
 * 先写文件下载的服务端
 */
public class Homework03Server {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[] b = new byte[1024];
        int len = 0;
        String downLoadFileName = "";
        while ((len = inputStream.read(b)) != -1) {downLoadFileName += new String(b, 0 , len);
        }
        System.out.println("客户端心愿下载文件名 =" + downLoadFileName);

        // 老师在服务器上有两个文件, 无名.mp3 高山流水.mp3
        // 如果客户下载的是 高山流水 咱们就返回该文件,否则一律返回 无名.mp3

        String resFileName = "";
        if("高山流水".equals(downLoadFileName)) {resFileName = "src\\ 高山流水.mp3";} else {resFileName = "src\\ 无名.mp3";}

        //4. 创立一个输出流,读取文件
        BufferedInputStream bis =
                new BufferedInputStream(new FileInputStream(resFileName));

        //5. 应用工具类 StreamUtils,读取文件到一个字节数组

        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.hspedu.homework;

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

/**
 * 此类用于演示对于流的读写办法
 *
 */
public class StreamUtils {
   /**
    * 性能:将输出流转换成 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);  
      }
      byte[] array = bos.toByteArray();
      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){builder.append(line+"\r\n");
      }
      return builder.toString();}
}

正文完
 0