IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Java 学习笔记(十五) -> 正文阅读

[网络协议]Java 学习笔记(十五)

InetAddress 类

public static void main(String[] args) throws UnknownHostException {
        // 获取本机的 InetAddress 对象
        InetAddress localhost = InetAddress.getLocalHost();
        System.out.println("localhost = " + localhost);

        // 根据主机名获取 InetAddress 对象
        InetAddress host1 = InetAddress.getByName("BLACK"); // 此处为我的主机名
        System.out.println("host1 = " + host1);

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

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

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

输出结果:

localhost = Black/192.168.199.99
host1 = Black/192.168.199.99
host2 = www.baidu.com/14.215.177.39
hostAddress = 14.215.177.39
hostName = www.baidu.com

Socket

Socket 套接字应用广泛,是事实上网络通信的标准

  • 通信的两端都要有 Socket,是两台机器通信的端点
  • 网络通信其实就是 Socket 的通信
  • Socket 允许程序把网络连接当成一个流,数据在两个 Socket直接通过 IO 传输
  • 一般主动发起通信的应用程序属于客户端,等待通信请求的为服务端

在这里插入图片描述

编程方式有两种

  • TCP 编程,可靠
  • UDP 编程,不可靠

TCP 网络通信编程

  • 基于客户端-服务端的网络通信
  • 底层使用 TCP/IP 协议
  • 是基于 Socket 的 TCP 编程在这里插入图片描述

例子1:
服务器监听 9999 端口,客户端连接服务端,向服务端发送字节流的消息,然后服务端显示请求内容
服务端:

public class SocketTCPServer01 {
    public static void main(String[] args) throws IOException {
        // 监听本机 9999 端口,等待连接
        // 注意,不能有其它服务也在监听 9999 端口
        // ServerSocket 流可以对应多个 Socket 流,就像多个客户端连接服务器
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在监听 9999 端口,等待连接");

        // 当没有客户端连接时,程序会阻塞
        // 如果有程序连接,则会返回 Socket 对象,程序继续
        Socket socket = serverSocket.accept();
        System.out.println("服务器端 socket = " + socket);

        // 连接成功后,获取输入流对象
        InputStream inputStream = socket.getInputStream();
        // IO 读取
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            // 根据读取到的长度显示内容
            System.out.println(new String(buf, 0, readLen));
        }
        // 关闭流
        inputStream.close();
        socket.close();
        // 服务器流也关闭
        serverSocket.close();
        System.out.println("服务器退出");
    }
}

客户端:

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

        // 连接上后,生成 Socket
        // 通过 getOutputStream() 获取输出流对象
        OutputStream outputStream = socket.getOutputStream();
        // 通过输出流写入数据到通道
        outputStream.write("杰瑞狗666".getBytes());
        // 使用完毕后关闭流
        outputStream.close();
        socket.close();
        System.out.println("客户端退出");
    }
}

例子2:
在上个例子的基础上,让服务端收到客户端的字节流信息后,给客户端回复字节流的信息,客户端将收到的内容打印出来。注意:需要在输出流之后加上结束标记,关闭TCP连接

服务端

public class SocketTCPServer02 {
    public static void main(String[] args) throws IOException {
        // 监听本机 9999 端口,等待连接
        // 注意,不能有其它服务也在监听 9999 端口
        // ServerSocket 流可以对应多个 Socket 流,就像多个客户端连接服务器
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在监听 9999 端口,等待连接");

        // 当没有客户端连接时,程序会阻塞
        // 如果有程序连接,则会返回 Socket 对象,程序继续
        Socket socket = serverSocket.accept();
        System.out.println("服务器端 socket = " + socket);

        // 连接成功后,获取输入流对象
        InputStream inputStream = socket.getInputStream();
        // IO 读取
        byte[] buf = new byte[1024];
        int readLen = 0;
        while ((readLen = inputStream.read(buf)) != -1) {
            // 根据读取到的长度显示内容
            System.out.println(new String(buf, 0, readLen));
        }
        // 服务端向客户端发送回复
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write("hello杰瑞狗".getBytes());
        // 设置结束标记
        socket.shutdownOutput();
        // 关闭流
        inputStream.close();
        outputStream.close();
        socket.close();
        // 服务器流也关闭
        serverSocket.close();
        System.out.println("服务器退出");
    }
}

客户端

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

        // 连接上后,生成 Socket
        // 通过 getOutputStream() 获取输出流对象
        OutputStream outputStream = socket.getOutputStream();
        // 通过输出流写入数据到通道
        outputStream.write("杰瑞狗666".getBytes());
        // 设置结束标记,防止程序陷入无限的等待
        socket.shutdownOutput();
        // 获取服务端的回复
        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));
        }
        // 使用完毕后关闭流
        outputStream.close();
        inputStream.close();
        socket.close();
        System.out.println("客户端退出");
    }
}

例子3:
在第二个例子的基础上,使用字符流完成消息的发送接收

服务端:

public class SocketTCPServer03 {
    public static void main(String[] args) throws IOException {
        // 监听本机 9999 端口,等待连接
        // 注意,不能有其它服务也在监听 9999 端口
        // ServerSocket 流可以对应多个 Socket 流,就像多个客户端连接服务器
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端在监听 9999 端口,等待连接");

        // 当没有客户端连接时,程序会阻塞
        // 如果有程序连接,则会返回 Socket 对象,程序继续
        Socket socket = serverSocket.accept();
        System.out.println("服务器端 socket = " + socket);

        // 连接成功后,获取输入流对象
        InputStream inputStream = socket.getInputStream();
        // 使用字符流读取
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println("s = " + s);

        // 服务端向客户端发送回复
        OutputStream outputStream = socket.getOutputStream();
        // 使用字符流回复信息
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("回复杰瑞狗777 字符流");
        bufferedWriter.newLine();// 插入换行符,表示写入内容结束
        // 需要手动刷新,否则数据不会写入通道
        bufferedWriter.flush();

        // 关闭流
        bufferedWriter.close();
        bufferedReader.close();
        socket.close();
        // 服务器流也关闭
        serverSocket.close();
        System.out.println("服务器退出");
    }
}

客户端:

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

        // 连接上后,生成 Socket
        // 通过 getOutputStream() 获取输出流对象
        OutputStream outputStream = socket.getOutputStream();
        // 通过输出流写入数据到通道
        // 使用字符流
        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream));
        bufferedWriter.write("杰瑞狗666 字符流");
        bufferedWriter.newLine();// 插入换行符,表示写入内容结束
        // 需要手动刷新,否则数据不会写入通道
        bufferedWriter.flush();


        // 获取服务端的回复
        InputStream inputStream = socket.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
        String s = bufferedReader.readLine();
        System.out.println("s = " + s);

        // 使用完毕后关闭流
        bufferedReader.close();
        bufferedWriter.close();
        socket.close();
        System.out.println("客户端退出");
    }
}

例子 4:
使用客户端读取图片发送给服务端,服务端收到后回复字符流的消息

服务端:

public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端在监听 8888 端口");

        // 等待连接
        Socket accept = serverSocket.accept();

        // 读取客户端发送的数据
        BufferedInputStream bufferedInputStream = new BufferedInputStream(accept.getInputStream());
        // 把数据转化为 byte 数组
        byte[] bytes = StreamUtils.streamToByteArray(bufferedInputStream);

        // 把 byte 数组写入到文件
        String filePath = "src\\cat.jpg";
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(filePath));
        bufferedOutputStream.write(bytes);

        BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
        bufferedWriter.write("服务器收到图片");
        bufferedWriter.newLine();
        bufferedWriter.flush();

        // 关闭流
        bufferedWriter.close();
        bufferedOutputStream.close();
        bufferedInputStream.close();
        accept.close();
        serverSocket.close();
    }

客户端:

public static void main(String[] args) throws Exception {
        // 客户端连接 8888 服务端
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);

        // 创建读取磁盘文件的输入流
        String filePath = "e:\\cat.jpg";
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(filePath));
        // 使用工具类方法,把文件输入流转为 byte 数组
        byte[] bytes = StreamUtils.streamToByteArray(bufferedInputStream);

        // 通过 socket 获取输出流,把 byte 数组发给服务端
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(socket.getOutputStream());
        // 把 byte 数组发给服务端
        bufferedOutputStream.write(bytes);
        // 结束标记
        socket.shutdownOutput();

        BufferedInputStream bufferedInputStream1 = new BufferedInputStream(socket.getInputStream());
        String s = StreamUtils.streamToString(bufferedInputStream1);
        System.out.println("客户端收到回复:" + s);
        // 关闭流
        bufferedInputStream.close();
        bufferedInputStream1.close();
        bufferedInputStream.close();
        socket.close();
    }

工具类 StreamUtils:

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){ //当读取到 null时,就表示结束
			builder.append(line+"\r\n");
		}
		return builder.toString();

	}

TCP 其它知识点

  • 客户端也是通过端口与服务器进行通信的,端口是 TCP/IP 协议分配的,不是确定的

比如在上面的例子 4 中,在获取客户端连接后加上一句输出语句,可以看到客户端的 socket 的信息,里面包含端口号:
在这里插入图片描述

UDP

  • 类 DatagramSocket 和 DatagramPacket 实现了基于 UDP 协议的网络程序
  • UDP 数据报通过数据报套接字 DatagramSocket 发送和接受,系统不保证一定能到达目的地,也不保证到达的时间
  • DatagramPacket 对象封装了 UDP 数据报,在数据报中包含了发送端和接收端的 IP 地址以及端口
  • UDP 协议中每个数据报都给出了完整的地址信息,所以无需建立连接

原理示意图:
在这里插入图片描述

例子 1:
发送端向接收到发送消息,接收端收到消息后发送回复后退出,发送端收到回复后退出

发送端:

public class UDPSender01 {
    public static void main(String[] args) throws IOException {
        // 创建一个 DatagramSocket 对象,在端口 7777 准备接收数据
        DatagramSocket datagramSocket = new DatagramSocket(7777);

        // 将需要发送的数据封装到 DatagramPacket 对象中
        byte[] data = "杰瑞狗7777".getBytes();
        // params: 内容字节数组 数组长度 目的IP地址 端口
        DatagramPacket datagramPacket = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.125.29"), 9999);
        // 发送
        datagramSocket.send(datagramPacket);

        // 设置获取回复的数据包
        byte[] replyData = new byte[1024];
        DatagramPacket packet = new DatagramPacket(replyData, replyData.length);
        // 获取回复
        datagramSocket.receive(packet);

        // 拆包
        int len = packet.getLength();
        byte[] data2 = packet.getData();
        String s = new String(data2, 0, len);
        System.out.println("s = " + s);

        datagramSocket.close();
        System.out.println("发送端退出");
    }
}

接收端:

public class UDPReciever01 {
    public static void main(String[] args) throws IOException {
        // 创建一个 DatagramSocket 准备在端口 9999 接收数据
        DatagramSocket socket = new DatagramSocket(9999);
        // 创建一个 DatagramPacket 准备接受数据包
        // UDP 报文最大为 64 k
        byte[] buf = new byte[1024];
        DatagramPacket packet = new DatagramPacket(buf, buf.length);

        // 调用接收方法,将网络上传输的 DatagramPacket 对象填充到 packet 对象
        // 如果没有数据包发送到 9999 端口。就会一直阻塞
        System.out.println("接收端等待接收数据");
        socket.receive(packet);

        // 拆包,取出数据并显示
        // 实际收到的数据长度
        int len = packet.getLength();
        // 获取数据
        byte[] data = packet.getData();
        String s = new String(data, 0, len);
        System.out.println("s = " + s);

        // 接收端发送回复
        byte[] replyData = "已收到杰瑞给777".getBytes();
        // 新建一个数据包
        DatagramPacket packet1 = new DatagramPacket(replyData, replyData.length, InetAddress.getByName("192.168.125.29"), 7777);
        socket.send(packet1);

        System.out.println("接收端退出");
        socket.close();


    }
}

TCP 文件下载

客户端发送要获取的文件名,服务端根据文件名发送文件

服务端:

public class TCPFileUploadServerHW03 {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("服务端在监听 8888 端口");

        // 等待连接
        Socket accept = serverSocket.accept();

        // 读取客户端发送的数据
        InputStream inputStream = accept.getInputStream();
        byte[] buf = new byte[1024];
        int len = 0;
        String downloadName = "";
        while ((len = inputStream.read(buf)) != -1) {
            downloadName += new String(buf, 0, len);
        }
        System.out.println("客户端请求下载的文件名:" + downloadName);

        // 选择返回的文件名
        String resFileName = downloadName.equals("高山流水") ? "src\\高山流水.mp3" : "src\\无名.mp3";

        // 创建输入流,读取磁盘文件
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(resFileName));
        // 转化为字节数组,用于传输
        byte[] bytes = StreamUtils.streamToByteArray(bufferedInputStream);

        // 获取 socket 对应的输出流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(accept.getOutputStream());
        // 发送给客户端
        bufferedOutputStream.write(bytes);
        // 设置结束标记
        accept.shutdownOutput();


        // 关闭流
        bufferedOutputStream.close();
        inputStream.close();
        bufferedInputStream.close();
        accept.close();
        serverSocket.close();
    }
}

客户端:

public class TCPFileUploadClientHW03 {
    public static void main(String[] args) throws Exception {
        // 客户端连接 8888 服务端
        Socket socket = new Socket(InetAddress.getLocalHost(), 8888);

        // 接受用户输入,指定下载的文件名
        System.out.println("输出要下载的文件名:");
        Scanner scanner = new Scanner(System.in);
        String msg = scanner.next();

        // 获取 socket 的输出流
        OutputStream outputStream = socket.getOutputStream();
        // 把消息发送给服务端
        outputStream.write(msg.getBytes());
        // 设置结束标记
        socket.shutdownOutput();

        // 读取服务端返回的文件
        BufferedInputStream bufferedInputStream = new BufferedInputStream(socket.getInputStream());
        // 输入流转化为字节数组
        byte[] bytes = StreamUtils.streamToByteArray(bufferedInputStream);

        // 获取一个输出流,把字节数组写到磁盘
        String desFilePath = "e:\\" + msg + ".mp3";
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(desFilePath));
        bufferedOutputStream.write(bytes);
        System.out.println("客户端成功保存!");

        // 关闭流
        bufferedInputStream.close();
        bufferedOutputStream.close();
        outputStream.close();
        socket.close();
    }
}
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-11-26 09:12:22  更:2021-11-26 09:13:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年7日历 -2024/7/5 14:05:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码