概述
深入理解通讯协议
七层网络模型
应用层:专门为应用层服务的,比如说我们的浏览器
表示层:主要数据格式的转换,数据的加密,比如说服务器的编码格式,需要表示层做一个格式的转换;
会话层: 主要是建立回话的,确保回话的稳定和安全;
传输层:是OSI最重要的关键一环;确保端对端的可靠性传输;
网络层:主要是IP和路由的选择
链路成:主要负责链路的管理,比如说我们的交换机,就是在链路层的
物理层:就是物理传输的介质,比如说网线
高一层是建立在低一层的基础之上的;
TCP/IP系列协议
TCP/IP 是互联网架构的一个基础;
TCP传输以后到网络层的IP 主要是目的地和原地址的一个交互
ping 命令可以测试两台主机是不是通的
TCP的三次握手协议
TCP的三次握手的漏洞
三种解决方案
1,监控达到一个阈值都没有连接次数,就把所有的连接都释放;
2,有第三次连接之后我才确认连接
3,防火墙,确认你的连接都是有效的之后我才会让你请求后端的服务;
TCP的四次挥手
客户端和服务端是可以相互发消息和接收消息;
抓包工具 wireshark
世界上最流行的网络协议分析器
TCP的通讯原理
tcp 是使用主机的ip地址和端口作为TCP连接的端点;
当我的TCP接收端里面没有内容的时候回发生阻塞。当我的发送端缓冲区写满了之后也会阻塞;
TCP的可靠性传输
解决这个问题就会引入一个概念 ,就是滑动窗口
滑动窗口,是一个接收方,和发送放都维护的一个序列;这个顺序被称为窗口;
只有在窗口里面的数字才会被真正的发送;
在线演示滑动窗口:
https://media.pearsoncmg.com/aw/ecs_kurose_compnetwork_7/cw/content/interactiveanimations/selective-repeat-protocol/index.html
滑动窗口: 确保数据不会丢失,如果丢失会从发;
限制发送的速度;
如果多次发送失败,就会断开连接;
有效的控制一个阻塞;
HTTP协议
、
http 协议是一个超文本的协议;
uri 是定义资源的唯一标识,
url 是网络中唯一的路径;
HTTP报文的组成
请求行: 比如说他是get还是post 请求
3xx状态 重定向,我需要附加的操作才能完成;
深入理解通讯协议2
UDP协议
UDP 面向的是一个无连接的状态;
所以他是相对不可靠的;
优点: 传输快
缺点:可能会数据丢失
UDP 的组成
源端口: 源端口和端口号,不需要时可为0
目的端口号: 目的端口号必须填;
长度:UDP用户数据的长度,最小值是8
校验和: 用来校验UDP的数据是不是可靠;如果这个传输校验和不对,我就直接丢弃
为什么使用UDP,因为效率高;
tcp实战
package enjoy.protocol.tcp; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.ServerSocket; import java.net.Socket; /** * TCP 服务器端 */ public class TcpServer { public static void main(String[] args) throws Exception { // 第一部分 创建一个ServerSocket监听一个端口 : 8888 ServerSocket serverSocket = new ServerSocket(8888); System.out.println("TCP服务器已经启动,端口是 8888"); while (true){ //无线循环等待客户端的请求,如果有就分配一个请求 Socket socket = serverSocket.accept(); // 根据标准输入构造一个bufferReader BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String buffer=null; //循环读取输入的每一行; while((buffer=reader.readLine())!=null&& !buffer.equals("")){ //输出每一行 System.out.println(buffer); //输出完之后 服务器给客户端有些东西; } // 通过Socket 对象得到输出流,构造BufferedWrite BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); //模拟http的请求头信息 writer.write("HTTP/1.1 200 OK rn Content-Type:text/html rn charset=UTF-8rnrn "); // 写http的请求body 信息 writer.write("<html><head><title>http请求</title></head><body><h1>这是一个HTTP请求</h1></body></html>"); // 刷新这个输出流使得数据立马就发送 writer.flush(); // 关闭 reader.close(); writer.close(); socket.close(); } } }
package enjoy.protocol.tcp; import java.io.PrintWriter; import java.net.Socket; /** * TCP 客户端 */ public class TcpClient { public static void main(String[] args) throws Exception{ String msg="hello 13"; //穿件一个Socket ,跟本机的8888 端口进行连接 Socket socket = new Socket("127.0.0.1", 8888); // 使用socket 创建一个printWrite 进行写数据 PrintWriter printWriter = new PrintWriter(socket.getOutputStream()); // 发送数据 printWriter.println(msg); //刷新一下,使得服务器立马收到请求信息 printWriter.flush(); printWriter.close(); socket.close(); } }
实现UDP
package enjoy.protocol.udp; import java.net.DatagramPacket; import java.net.DatagramSocket; public class ReciveDeom { public static void main(String[] args) throws Exception { // 创建一个DatagramDocket 实例,并且绑定到本机地址,端口10005 DatagramSocket datagramSocket = new DatagramSocket(10005); byte[] bytes = new byte[1024]; // 以一个空的数组来床间距 DatagramPacket ,这个对象作用是接收DatagramDocket 中的数据 DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length); // 因为我不知道你什么时候发送数据过来,所以我在这里无限循环等待 while (true){ // 接收到数据包 datagramSocket.receive(datagramPacket); // 获取接收的数据 byte[] data =datagramPacket.getData(); //把数组转成字符 String str= new String(data,0,datagramPacket.getLength()); // 如果数据包中的数据是88的信息, 则跳出且关闭 if("88".equals(str)){ break; } System.out.println("接收到的数据为"+str); } // 关闭 datagramSocket.close(); } }
package enjoy.protocol.udp; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * UDP 发送端 */ public class SendDemo { public static void main(String[] args) throws Exception{ // 创建一个DatagramSocket 实例 DatagramSocket datagramSocket = new DatagramSocket(); // 使用键盘输入构建一个BufferedReader BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in)); String line=null; while ((line=bufferedReader.readLine())!=null){ //转成byte byte [] bytes = line.getBytes(); //创建一个用于发送DatagramPacket对象 DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("127.0.0.1"), 10005); // 发送数据包 datagramSocket.send(datagramPacket); // 当输入88时结束发送 if("88".equals(line)){ break; } } // 关闭 datagramSocket.close(); } }
总结
基于分布式思想下的RPC解决方案
手写RPC 方案
第一步, 定义服务的接口,并且注册服务的接口;
第二步,进行查询服务在哪个地方;去注册中心去查;
第三步, 作为客户端调用需要使用一个动态代理;
第四步,对动态代理做一个增强;
第五步,进行通讯
第六步,反序列化得出他的对象,方法,参数;
第七步,通过反射的机制完成调用
实现技术方案
Socket通讯,动态代理与反射,Java序列化
动态代理
代理模式三要素
1,接口,
2,接口真实对象,一定是继承接口的实现,他是真正的实现
3,还有一个代理
真实对象和代理对象必须同事继承同一个接口;
代理的对象他必须包含真实的对象在里面;
InvocationHandler 增强代理
Proxy 就是个调度器
ROC实现细节
序列化是把对象转化为01串
反序列化,就是把在网络上传输的01串转化为java的bean对象;
反射就是: 在运行的时候,你给我一个名称,你给我一个方法名,给我一个方法的参数,我就可以把这个类完整的构造出来,并且去调用他对应的方法;
实现代码demo
客户端
public interface TechInterface { String XJ(String name); }
package enjoyedu.rpc; import jdk.internal.org.objectweb.asm.tree.TryCatchBlockNode; import javax.lang.model.element.VariableElement; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.InetSocketAddress; import java.net.Socket; /** * 客户端的动态代理 */ public class RpcClientFrame { public static <T> T getRemotProxyObj(final Class<?> serviceInterface) throws Exception{ InetSocketAddress serviceAddr=new InetSocketAddress("127.0.0.1",8888); return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(),new Class<?>[]{serviceInterface},new DynProxy(serviceInterface,serviceAddr)); } /** * 实现动态代理类,实现了对远程服务的访问 * */ private static class DynProxy implements InvocationHandler{ //接口 private final Class<?> serviceInterface; //远程调用地址 private final InetSocketAddress addr; public DynProxy(Class<?> serviceInterface,InetSocketAddress addr){ this.serviceInterface=serviceInterface; this.addr=addr; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { /** * 网络增强部分 */ // 使用socket Socket socket =null; //因为传递的大部分是方法, 参数, 所以我们使用Object 流对象 ObjectInputStream objectInputStream =null; ObjectOutputStream objectOutputStream=null; try{ socket=new Socket(); //连接到远程的地址和端口 socket.connect(addr); // 往远端 发送数据,按照顺序发送数据: 类名,方法名,参数类型,参数值 // 拿到输出的流 objectOutputStream =new ObjectOutputStream(socket.getOutputStream()); // 发送 调用方法的类名 使用UTF-8 避免乱码 objectOutputStream.writeUTF(serviceInterface.getName()); // 发送 方法名 objectOutputStream.writeUTF(method.getName()); // 发送 调用方法的类名 使用Object objectOutputStream.writeObject(method.getParameterTypes()); // 发送 参数的值 objectOutputStream.writeObject(args); objectOutputStream.flush(); // 立马拿到远程的执行结果 objectInputStream =new ObjectInputStream(socket.getInputStream()); // 把调用的细节打印出来 System.out.println("远程调用成功!"+serviceInterface.getName()); //最后网络的请求返回给返回结果 return objectInputStream.readObject(); }catch (Exception e){ e.printStackTrace(); } finally { // 关闭 socket.close(); objectInputStream.close(); objectOutputStream.close(); return null; } } } }
package enjoyedu; import enjoyedu.rpc.RpcClientFrame; import enjoyedu.service.TechInterface; public class Client { public static void main(String[] args) throws Exception { TechInterface techInterface = RpcClientFrame.getRemotProxyObj(TechInterface.class); System.out.println(techInterface.XJ("king")); } }
服务server端
package enjoyedu.service; public interface TeachInterface { // 服务员的接口 String XJ(String name); }
package enjoyedu.service.impl; import enjoyedu.service.TeachInterface; public class TechImpl implements TeachInterface { @Override public String XJ(String name) { return "您好,13号为您服务"+name; } }
package enjoyedu; import enjoyedu.register.RegisterCenter; import enjoyedu.service.TeachInterface; import enjoyedu.service.impl.TechImpl; import java.io.IOException; public class Server { public static void main(String[] args) throws Exception{ new Thread(new Runnable() { @Override public void run() { try { // 起一个注册中心 RegisterCenter serviceServer = new RegisterCenter(8888); // 注册中心的服务至注册中心 serviceServer.register(TeachInterface.class, TechImpl.class); // 运行我们的服务 serviceServer.start(); } catch (IOException e) { throw new RuntimeException(e); } } }).start(); } }
注册中心
package enjoyedu.register; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 注册中心 */ public class RegisterCenter { //线程池 private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); private static final HashMap<String,Class> serviceRegistry=new HashMap<String,Class>(); private static boolean isRunning =false; private static int port; public RegisterCenter(int port){ this.port=port; } // 启动支持中心 启动 public void start() throws IOException{ ServerSocket server = new ServerSocket(); server.bind(new InetSocketAddress(port)); System.out.println("start server"); try { while (true){ // 1 监听客户端的TCP 连接,连接到TCP连接以后将其封装成task,由线程池执行, executor.execute(new ServiceTask(server.accept())); } }finally { server.close(); } } // 注册服务: socket 通讯+ 反射 public void register(Class serviceInterface,Class impl){ serviceRegistry.put(serviceInterface.getName(),impl); } // 服务端获取运行 private static class ServiceTask implements Runnable{ Socket clent=null; public ServiceTask(Socket client){ this.clent=client; } public void run(){ // 反射 // 同样使用object 流 ObjectInputStream inputStream =null; ObjectOutputStream outputStream=null; try{ //1 发送的object 对象拿到, 2,在使用反射的机制进行调用,3,最后返回结果 inputStream=new ObjectInputStream(clent.getInputStream()); // 拿到类名 String serviceName = inputStream.readUTF(); String methodName = inputStream.readUTF(); //拿到参数类型 Class<?>[] prarmTypes= ( Class<?>[])inputStream.readObject(); // 拿到参数值 Object[] arguments = ( Object[] )inputStream.readObject(); // 到注册中心根据 接口名,获取实现类 Class serviceClass =serviceRegistry.get(serviceName); // 使用反射的机制进行调用 Method method=serviceClass.getMethod(methodName,prarmTypes); // 反射调用方法 ,类的实例化,反射参数值,把结果拿到 Object result = method.invoke(serviceClass.newInstance(), arguments); // 通过执行socket 返回给客户端 outputStream=new ObjectOutputStream(clent.getOutputStream()); outputStream.writeObject(result); // 关闭 outputStream.close(); inputStream.close(); clent.close(); }catch(Exception e){ e.printStackTrace(); } } } }
最后
以上就是平常微笑为你收集整理的深入理解通讯协议-手写RMI和RPC深入理解通讯协议深入理解通讯协议2基于分布式思想下的RPC解决方案的全部内容,希望文章能够帮你解决深入理解通讯协议-手写RMI和RPC深入理解通讯协议深入理解通讯协议2基于分布式思想下的RPC解决方案所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复