当前位置: 首页 > news >正文

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 项目文章|PNAS:中国农大田见晖教授团队揭示DNA甲基化保护早期胚胎线粒体基因组稳定性
  • 使用FFmpeg的AVFilter转换YUV到RGB
  • 深度学习示例2-多输入多输出的神经网络模型
  • 20.弹窗组件封装
  • Linux【4】拷贝移动 文件内容
  • 云计算的成本:您需要了解的 AWS 定价信息
  • stm32 8080时序驱动lcd屏幕
  • PDM系统详细介绍
  • Spring Cloud全解析:负载均衡之Ribbon简介
  • <计算机网络>笔记1: TCP/IP五层协议
  • Android 存储之 SharedPreferences 框架体系编码模板
  • 【ZYNQ MPSoC开发】lwIP TCP发送用于数据缓存的软件FIFO设计
  • 【C++11】深入理解与应用右值引用
  • GPT教我学【这天学的物理知识】速度的相对性
  • DAY14:一条SQL查询语句是如何执行的?| 事务的四大特性有哪些?| 数据库的事物隔离级别有哪些
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • 2017 年终总结 —— 在路上
  • co模块的前端实现
  • iOS筛选菜单、分段选择器、导航栏、悬浮窗、转场动画、启动视频等源码
  • Java 23种设计模式 之单例模式 7种实现方式
  • Js基础知识(一) - 变量
  • Promise面试题2实现异步串行执行
  • uva 10370 Above Average
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • 第2章 网络文档
  • 基于遗传算法的优化问题求解
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 前端工程化(Gulp、Webpack)-webpack
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • 如何正确理解,内页权重高于首页?
  • ​DB-Engines 12月数据库排名: PostgreSQL有望获得「2020年度数据库」荣誉?
  • (02)Unity使用在线AI大模型(调用Python)
  • (6)【Python/机器学习/深度学习】Machine-Learning模型与算法应用—使用Adaboost建模及工作环境下的数据分析整理
  • (javascript)再说document.body.scrollTop的使用问题
  • (八)Flask之app.route装饰器函数的参数
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (亲测有效)推荐2024最新的免费漫画软件app,无广告,聚合全网资源!
  • (算法)前K大的和
  • (一)Mocha源码阅读: 项目结构及命令行启动
  • (一)SvelteKit教程:hello world
  • (已解决)Bootstrap精美弹出框模态框modal,实现js向modal传递数据
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转)ORM
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • (转载)PyTorch代码规范最佳实践和样式指南
  • .equals()到底是什么意思?
  • .java 9 找不到符号_java找不到符号
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地中转一个自定义的弱事件(可让任意 CLR 事件成为弱事件)
  • .NET开源项目介绍及资源推荐:数据持久层
  • .net与java建立WebService再互相调用
  • :如何用SQL脚本保存存储过程返回的结果集
  • @Autowired多个相同类型bean装配问题
  • @Valid和@NotNull字段校验使用
  • [20160807][系统设计的三次迭代]