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

相关文章

Java设计模式---迭代器模式(Iterator)解读

《Java设计模式---迭代器模式(Iterator)解读》:本文主要介绍Java设计模式---迭代器模式(Iterator),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录1、迭代器(Iterator)1.1、结构1.2、常用方法1.3、本质1、解耦集合与遍历逻辑2、统一

Java内存分配与JVM参数详解(推荐)

《Java内存分配与JVM参数详解(推荐)》本文详解JVM内存结构与参数调整,涵盖堆分代、元空间、GC选择及优化策略,帮助开发者提升性能、避免内存泄漏,本文给大家介绍Java内存分配与JVM参数详解,... 目录引言JVM内存结构JVM参数概述堆内存分配年轻代与老年代调整堆内存大小调整年轻代与老年代比例元空

深度解析Java DTO(最新推荐)

《深度解析JavaDTO(最新推荐)》DTO(DataTransferObject)是一种用于在不同层(如Controller层、Service层)之间传输数据的对象设计模式,其核心目的是封装数据,... 目录一、什么是DTO?DTO的核心特点:二、为什么需要DTO?(对比Entity)三、实际应用场景解析

Java 线程安全与 volatile与单例模式问题及解决方案

《Java线程安全与volatile与单例模式问题及解决方案》文章主要讲解线程安全问题的五个成因(调度随机、变量修改、非原子操作、内存可见性、指令重排序)及解决方案,强调使用volatile关键字... 目录什么是线程安全线程安全问题的产生与解决方案线程的调度是随机的多个线程对同一个变量进行修改线程的修改操

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

深度解析Java项目中包和包之间的联系

《深度解析Java项目中包和包之间的联系》文章浏览阅读850次,点赞13次,收藏8次。本文详细介绍了Java分层架构中的几个关键包:DTO、Controller、Service和Mapper。_jav... 目录前言一、各大包1.DTO1.1、DTO的核心用途1.2. DTO与实体类(Entity)的区别1

Java中的雪花算法Snowflake解析与实践技巧

《Java中的雪花算法Snowflake解析与实践技巧》本文解析了雪花算法的原理、Java实现及生产实践,涵盖ID结构、位运算技巧、时钟回拨处理、WorkerId分配等关键点,并探讨了百度UidGen... 目录一、雪花算法核心原理1.1 算法起源1.2 ID结构详解1.3 核心特性二、Java实现解析2.

SpringBoot整合liteflow的详细过程

《SpringBoot整合liteflow的详细过程》:本文主要介绍SpringBoot整合liteflow的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋...  liteflow 是什么? 能做什么?总之一句话:能帮你规范写代码逻辑 ,编排并解耦业务逻辑,代码

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

Spring Security中用户名和密码的验证完整流程

《SpringSecurity中用户名和密码的验证完整流程》本文给大家介绍SpringSecurity中用户名和密码的验证完整流程,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定... 首先创建了一个UsernamePasswordAuthenticationTChina编程oken对象,这是S