JAVA—网络通信

2024-09-05 18:20
文章标签 java 网络通信

本文主要是介绍JAVA—网络通信,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

        本文是学习网络通信入门和简单了解UDP协议和TCP协议,学习和了解CS架构和简单了解BS架构和HTTP协议(部分图片来自黑马程序员)

目录

1.网络通信三要素

(1)IP地址

(2)端口号

(3)协议

2.UDP通信—快速入门

3.UDP通信—多发多收

4.TCP通信—快速入门

5.TCP通信—多发多收

6.TCP通信—同时接收多个客户端

7.TCP通信—综合案例

(1)即时通信-群聊

(2)实现一个简易的BS架构

 *线程优化


网络编程

可以让设备中的程序与网络其他设备中的程序进行数据交互

基本通信框架

有两种形式:CS架构(CLient客户端/Server服务器)和 BS架构(Browser浏览器/Server服务器)

1.网络通信三要素

(1)IP地址

  • IP(InternetProtocol)全称“互联网协议地址”,是分配给上网设备的唯一标志
  • 有两种形式 IPV4 和 IPV6

    IPV4 由四个字节组成一般使用点分十进制表示法

    IPV6 由8段组成 一段每四位编码成一个十六进制数 一般使用冒分十六进制表示 

127.0.0.1/ localhost  代表本机IP,只会寻求当前所在的主机

InetAddress

 代表IP地址

名称说明
public static InetAddress getLocalHost()读取本机IP,会以一个inetAddress的对象返回
public static  InetAddress getByname(String host)

根据ip地址或者域名 返回一个inetAddress对象

public String getHostName()获取该IP地址对象对应的主机名
public String getHostAddress()获取该IP地址中的IP地址信息
public boolean isReachable(int timeout)在指定毫秒内,判断主机与该IP是否能连通

(2)端口号

  • 标记正在计算机设备上运行的应用程序 被固定为一个16位的二进制 范围是0~65535

        分类

  • 周知端口:0~1023 被预先定义的知名应用占用(如HTTP占用80,FTP占用21)
  • 注册端口:1024~49151 分配给用户或某些应用程序
  • 动态端口:49152~65535 一般不固定分配某种进程,而是动态分配

        注意:我们自己开发的程序一般选择使用注册端口,且一个设备不能出现两个程序的端口号一致,否则出错

(3)协议

        网络上通信的设备 事先固定的连接规则 以及传输数据的规则被称为网络通信协议

 UDP协议 (通信效率高 用于语音 视频通话)

  • 特点:无连接,不可靠通信
  • 不事先建立连接数据,按照包发一包数据包含自己的IP程序端口,目的地IP程序端口的数据限制在64kb内的。
  • 发送方不管对方是否在线树立在中间丢失也不管如果接受方式的数据也不返回确认故而不可靠

 TCP协议 (通信效率相对不高 网页 文件下载,支付)

  • 特点:面向连接 可靠通信
  • TCP的最终目的:要保障在不可信的信道上实现可靠的传输
  • 主要三个步骤实现可靠传输:三次握手建立连接,传输数据进行确认,四次挥手断开连接(确保双方数据的收发都已经完成)
  • 三次握手

2.UDP通信—快速入门

 实现单发单收

package IP_Study.upd01;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;public class Client {public static void main(String[] args) throws Exception {// 1.创建客户端DatagramSocket socket = new DatagramSocket();// 2.创建数据包// public DatagramPacket(byte buff[], int offset, int length,//                          InetAddress address, int port)//            参数一:封装要发出去的数据//            参数二:发送要发出去的数据大小(字节个数)//            参数三:服务端的IP地址//            参数四:服务端程序的端口String rs = "万水千山 我陪你去看";byte[] buff = rs.getBytes();DatagramPacket packet = new DatagramPacket(buff,buff.length, InetAddress.getLocalHost(),8088);// 3.正式发送这个数据包数据socket.send(packet);System.out.println("客户端数据发送完毕");socket.close(); //关闭流 释放资源}
}
package IP_Study.upd01;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class Server {public static void main(String[] args) throws Exception {// 1.创建一个服务端对象DatagramSocket socket = new DatagramSocket(8088);System.out.println("服务端启动");// 2.创建一个数据包对象,用于接收数据byte [] buffer = new byte[1024 *64]; //64KBDatagramPacket packet = new DatagramPacket(buffer, buffer.length);// 3.正式使用数据包来接收客户端发来的数据socket.receive(packet);// 4.从字节数组中 打印接收的数据int len = packet.getLength();String rs = new String(buffer,0, len);System.out.println(rs);System.out.println(packet.getAddress().getHostAddress()); //获取发起IP}
}

3.UDP通信—多发多收

改造UDP单发单收代码已实现多发多收

package IP_Study.upd01;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;public class Client {public static void main(String[] args) throws Exception {// 1.创建客户端DatagramSocket socket = new DatagramSocket();// 2.创建数据包// public DatagramPacket(byte buff[], int offset, int length,//                          InetAddress address, int port)//            参数一:封装要发出去的数据//            参数二:发送要发出去的数据大小(字节个数)//            参数三:服务端的IP地址//            参数四:服务端程序的端口Scanner sc = new Scanner(System.in);while (true) {System.out.println("请说:");String rs = sc.next();//一旦发现客户输入exit 则退出客户端if("exit".equals(rs)){System.out.println("退出成功");socket.close();break;}byte[] buff = rs.getBytes();DatagramPacket packet = new DatagramPacket(buff,buff.length, InetAddress.getLocalHost(),8088);// 3.正式发送这个数据包数据socket.send(packet);}}
}
package IP_Study.upd01;import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;public class Server {public static void main(String[] args) throws Exception {// 1.创建一个服务端对象DatagramSocket socket = new DatagramSocket(8088);System.out.println("服务端启动");// 2.创建一个数据包对象,用于接收数据byte [] buffer = new byte[1024 *64]; //64KBDatagramPacket packet = new DatagramPacket(buffer, buffer.length);while (true) {// 3.正式使用数据包来接收客户端发来的数据socket.receive(packet);// 4.从字节数组中 打印接收的数据int len = packet.getLength();String rs = new String(buffer,0, len);System.out.println(rs);System.out.println(packet.getAddress().getHostAddress()); //获取发起IPSystem.out.println(packet.getPort());System.out.println("---------------");}}
}

4.TCP通信—快速入门

package IP_Study.tcp01;import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;public class Client {public static void main(String[] args) throws Exception {// 1.创建socket对象 并同时请求与服务端程序的连接Socket socket = new Socket("127.0.0.1",8888);// 2.从Socket中得到一个字节输出流并包装成数据输出流OutputStream os = socket.getOutputStream();// 3.把低级的字节输出流包装成数据输出流DataOutputStream dos = new DataOutputStream(os);// 4.开始写出数据dos.writeUTF("万水千山");dos.close();socket.close(); //释放资源}
}
package IP_Study.tcp01;import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws Exception {// 1.创建ServerSocket对象,同时为服务器注册端口ServerSocket serverSocket = new ServerSocket(8888);System.out.println("服务端启动成功");// 2.使用ServerSocket对象 带哦用一个accept方法 等待客户端连接请求Socket socket = serverSocket.accept();// 3.从Socket中获得一个字节数入流管道InputStream is = socket.getInputStream();// 4.把原始的字节输入流包装成数据输入流DataInputStream dis = new DataInputStream(is);// 5.使用数据输入流读取客户端发送的消息String rs = dis.readUTF(); //信息格式要统一System.out.println(rs);// 获取客户端的IP地址System.out.println(socket.getRemoteSocketAddress());dis.close();socket.close();}
}


5.TCP通信—多发多收

package IP_Study.tcp01;import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class Client {public static void main(String[] args) throws Exception {// 1.创建socket对象 并同时请求与服务端程序的连接Socket socket = new Socket("127.0.0.1",8888);// 2.从Socket中得到一个字节输出流并包装成数据输出流OutputStream os = socket.getOutputStream();// 3.把低级的字节输出流包装成数据输出流DataOutputStream dos = new DataOutputStream(os);Scanner sc = new Scanner(System.in);while (true) {// 4.开始写出数据System.out.println("请说");String rs = sc.nextLine();// 一旦输入 exit 就退出客户端if("exit".equals(rs)){System.out.println("退出成功");dos.close();socket.close();break;}dos.writeUTF(rs);dos.flush(); //刷新数据流 以免数据没有发出去}}
}
package IP_Study.tcp01;import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws Exception {// 1.创建ServerSocket对象,同时为服务器注册端口ServerSocket serverSocket = new ServerSocket(8888);System.out.println("服务端启动成功");// 2.使用ServerSocket对象 带哦用一个accept方法 等待客户端连接请求Socket socket = serverSocket.accept();// 3.从Socket中获得一个字节数入流管道InputStream is = socket.getInputStream();// 4.把原始的字节输入流包装成数据输入流DataInputStream dis = new DataInputStream(is);while (true) {// 5.使用数据输入流读取客户端发送的消息try {String rs = dis.readUTF(); //信息格式要统一System.out.println(rs);// 获取客户端的IP地址System.out.println(socket.getRemoteSocketAddress());} catch (Exception e) {System.out.println(socket.getRemoteSocketAddress() + "离线了");dis.close();socket.close();break;}}}
}

         此时的代码是不能实现与多个客户端进行通信的 因为服务器只搭建一个通道


6.TCP通信—同时接收多个客户端

改造代码时 不能单纯依靠死循环 还需要使用线程技术进行处理

package IP_Study.tcp01;import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class Client {public static void main(String[] args) throws Exception {// 1.创建socket对象 并同时请求与服务端程序的连接Socket socket = new Socket("127.0.0.1",8888);// 2.从Socket中得到一个字节输出流并包装成数据输出流OutputStream os = socket.getOutputStream();// 3.把低级的字节输出流包装成数据输出流DataOutputStream dos = new DataOutputStream(os);Scanner sc = new Scanner(System.in);while (true) {// 4.开始写出数据System.out.println("请说");String rs = sc.nextLine();// 一旦输入 exit 就退出客户端if("exit".equals(rs)){System.out.println("退出成功");dos.close();socket.close();break;}dos.writeUTF(rs);dos.flush(); //刷新数据流 以免数据没有发出去}}
}
package IP_Study.tcp01;import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws Exception {// 1.创建ServerSocket对象,同时为服务器注册端口ServerSocket serverSocket = new ServerSocket(8888);System.out.println("服务端启动成功");while (true) {// 2.使用ServerSocket对象 带哦用一个accept方法 等待客户端连接请求Socket socket = serverSocket.accept();System.out.println("有人上线了"+socket.getRemoteSocketAddress());// 把这个客户端的Socket通信管道交给一个独立的线程去完成new SeverReadder(socket).start();}}
}

 使用一个线程类来实现功能

package IP_Study.tcp01;import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;public class SeverReadder extends Thread{private Socket socaket;public SeverReadder(Socket socket) {this.socket = socket;}@Overridepublic void run(){// 3.从Socket中获得一个字节数入流管道try {InputStream is = socket.getInputStream();// 4.把原始的字节输入流包装成数据输入流DataInputStream dis = new DataInputStream(is);while (true) {try {// 5.使用数据输入流读取客户端发送的消息String rs = dis.readUTF(); //信息格式要统一System.out.println(rs);// 获取客户端的IP地址System.out.println(socket.getRemoteSocketAddress());} catch (IOException e) {System.out.println(socket.getRemoteSocketAddress() + "离线了");dis.close();socket.close();break;}}} catch (Exception e) {e.printStackTrace();}}
}

7.TCP通信—综合案例

(1)即时通信-群聊

    TCP通信-端口转发 实现思路是将存储好当前在线的socket都进行一次信息发送

package IP_Study.tcp01;import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;public class Client {public static void main(String[] args) throws Exception {// 1.创建socket对象 并同时请求与服务端程序的连接Socket socket = new Socket("127.0.0.1",8888);// 创建一个独立线程 负责随机从socket中接收服务器发送的信息new ClientReader(socket);// 2.从Socket中得到一个字节输出流并包装成数据输出流OutputStream os = socket.getOutputStream();// 3.把低级的字节输出流包装成数据输出流DataOutputStream dos = new DataOutputStream(os);Scanner sc = new Scanner(System.in);while (true) {// 4.开始写出数据System.out.println("请说");String rs = sc.nextLine();// 一旦输入 exit 就退出客户端if("exit".equals(rs)){System.out.println("退出成功");dos.close();socket.close();break;}dos.writeUTF(rs);dos.flush(); //刷新数据流 以免数据没有发出去}}
}
package IP_Study.tcp01;import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;public class ClientReader extends Thread{private Socket socket;public ClientReader(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {InputStream is = socket.getInputStream();// 4.把原始的字节输入流包装成数据输入流DataInputStream dis = new DataInputStream(is);while (true) {try {// 5.使用数据输入流读取客户端发送的消息String rs = dis.readUTF(); //信息格式要统一System.out.println(rs);// 获取客户端的IP地址System.out.println(socket.getRemoteSocketAddress());} catch (IOException e) {System.out.println("自己离线了"+socket.getRemoteSocketAddress());dis.close();socket.close();break;}}} catch (Exception e) {e.printStackTrace();}}}
package IP_Study.tcp01;import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;public class Server {public static List<Socket> onLineSockets = new ArrayList<>();public static void main(String[] args) throws Exception {// 1.创建ServerSocket对象,同时为服务器注册端口ServerSocket serverSocket = new ServerSocket(8888);System.out.println("服务端启动成功");while (true) {// 2.使用ServerSocket对象 带哦用一个accept方法 等待客户端连接请求Socket socket = serverSocket.accept();onLineSockets.add(socket);System.out.println("有人上线了"+socket.getRemoteSocketAddress());// 把这个客户端的Socket通信管道交给一个独立的线程去完成new SeverReader(socket).start();}}
}
package IP_Study.tcp01;import java.io.*;
import java.net.Socket;public class SeverReader extends Thread{private Socket socket;public SeverReader(Socket socket) {this.socket = socket;}@Overridepublic void run(){// 3.从Socket中获得一个字节数入流管道try {InputStream is = socket.getInputStream();// 4.把原始的字节输入流包装成数据输入流DataInputStream dis = new DataInputStream(is);while (true) {try {// 5.使用数据输入流读取客户端发送的消息String rs = dis.readUTF(); //信息格式要统一System.out.println(rs);//把这个信息发给全部客户端进行接收SendMsgToAll(rs);// 获取客户端的IP地址System.out.println(socket.getRemoteSocketAddress());} catch (IOException e) {System.out.println(socket.getRemoteSocketAddress() + "离线了");Server.onLineSockets.remove(socket);dis.close();socket.close();break;}}} catch (Exception e) {e.printStackTrace();}}private void SendMsgToAll(String rs) throws Exception {// 发送给全部在线的socket管道接受for (Socket onLineSocket : Server.onLineSockets) {OutputStream os = onLineSocket.getOutputStream();DataOutputStream dos = new DataOutputStream(os);dos.writeUTF(rs);dos.flush();}}
}

 实现思路:

当客户端有信息传输到服务器中,先将信息在服务端调用,在向所有在线的通信管道传输信息

(2)实现一个简易的BS架构

package IP_Study.tcp02;import IP_Study.tcp01.SeverReader;import java.net.ServerSocket;
import java.net.Socket;public class Sever {public static void main(String[] args) throws Exception {// 1.创建ServerSocket对象,同时为服务器注册端口ServerSocket serverSocket = new ServerSocket(8888);System.out.println("服务端启动成功");while (true) {// 2.使用ServerSocket对象 带哦用一个accept方法 等待客户端连接请求Socket socket = serverSocket.accept();System.out.println("有人上线了"+socket.getRemoteSocketAddress());// 把这个客户端的Socket通信管道交给一个独立的线程去完成new SeverReader(socket).start();}}
}
package IP_Study.tcp02;import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;public class SeverReader extends Thread{private Socket socket;public SeverReader(Socket socket) {this.socket = socket;}@Overridepublic void run() {// 响应一个网页内容try {OutputStream os = socket.getOutputStream();PrintStream ps = new PrintStream(os);ps.println("HTTP/1.1 200 OK");ps.println("Content-Type:text/html;charset=UTF-8");ps.println();//必须换行ps.println("<div style='color:purple;font-size:101px;text-align:center' >万水千山 我陪你去看");ps.close();socket.close();} catch (IOException e) {e.printStackTrace();}}
}

 *线程优化

  在高并发下 处理任务容易宕机 所以使用线程池来处理线程(注意线程池的参数)

package IP_Study.tcp02;import IP_Study.tcp01.SeverReader;import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class Sever {public static void main(String[] args) throws Exception {// 1.创建ServerSocket对象,同时为服务器注册端口ServerSocket serverSocket = new ServerSocket(8888);System.out.println("服务端启动成功");// 创建线程池 负责处理通信管道任务ThreadPoolExecutor pool = new ThreadPoolExecutor(16 * 2, 16 * 2, 0, TimeUnit.SECONDS,new ArrayBlockingQueue<>(8), Executors.defaultThreadFactory(), new ThreadPoolExecutor.AbortPolicy());while (true) {// 2.使用ServerSocket对象 带哦用一个accept方法 等待客户端连接请求Socket socket = serverSocket.accept();pool.execute(new SeverReader(socket));System.out.println("有人上线了"+socket.getRemoteSocketAddress());// 把这个客户端的Socket通信管道交给一个独立的线程去完成new SeverReader(socket).start();}}
}

                                                                                                        学习时间:2024.09.04

这篇关于JAVA—网络通信的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1139713

相关文章

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

SpringBoot+Docker+Graylog 如何让错误自动报警

《SpringBoot+Docker+Graylog如何让错误自动报警》SpringBoot默认使用SLF4J与Logback,支持多日志级别和配置方式,可输出到控制台、文件及远程服务器,集成ELK... 目录01 Spring Boot 默认日志框架解析02 Spring Boot 日志级别详解03 Sp

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

java如何解压zip压缩包

《java如何解压zip压缩包》:本文主要介绍java如何解压zip压缩包问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解压zip压缩包实例代码结果如下总结java解压zip压缩包坐在旁边的小伙伴问我怎么用 java 将服务器上的压缩文件解压出来,

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Spring WebFlux 与 WebClient 使用指南及最佳实践

《SpringWebFlux与WebClient使用指南及最佳实践》WebClient是SpringWebFlux模块提供的非阻塞、响应式HTTP客户端,基于ProjectReactor实现,... 目录Spring WebFlux 与 WebClient 使用指南1. WebClient 概述2. 核心依

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.