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实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

SpringBoot日志级别与日志分组详解

《SpringBoot日志级别与日志分组详解》文章介绍了日志级别(ALL至OFF)及其作用,说明SpringBoot默认日志级别为INFO,可通过application.properties调整全局或... 目录日志级别1、级别内容2、调整日志级别调整默认日志级别调整指定类的日志级别项目开发过程中,利用日志

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有