深入理解通讯协议
七层网络模型

应用层:专门为应用层服务的,比如说我们的浏览器
表示层:主要数据格式的转换,数据的加密,比如说服务器的编码格式,需要表示层做一个格式的转换;
会话层: 主要是建立回话的,确保回话的稳定和安全;
传输层:是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基于分布式思想下内容请搜索靠谱客的其他文章。
发表评论 取消回复