从0到1用java再造tcpip协议栈:架构重建,完整实现ping应用

2024-04-30 22:08

本文主要是介绍从0到1用java再造tcpip协议栈:架构重建,完整实现ping应用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在原先代码设计中,我们为了方便,喜欢在一个模块中组织数据包的协议头,然后将要发送的数据融合在一起,并调用网卡将数据发送出去,这种偷懒的做法将多种逻辑融合在一起。这种做法一旦遇到复杂的数据发送需求时,系统逻辑的复杂性会呈现出爆炸性的增长,最后超出我们的控制范围。

为了实现体系的层次化,将各种功能剥离成单独模块,实现系统的可理解性,我将体系结构改动为以下模式:

1.png

从上图看,所有的应用实例,也就是调用网络协议,实现数据收发功能的应用都继承IApplication接口和继承Application类,其内容如下:

package Application;import java.util.HashMap;public interface IApplication {public  int getPort();public boolean isClosed(); public  void handleData(HashMap<String, Object> data);
}package Application;import java.util.HashMap;public class Application implements IApplication{protected  int port = 0;private boolean closed = false;public Application() {ApplicationManager manager = ApplicationManager.getInstance();manager.addApplication(this);}@Overridepublic int getPort() {return port;}@Overridepublic void handleData(HashMap<String, Object> data) {// TODO Auto-generated method stub}@Overridepublic boolean isClosed() {return closed;}}

所有应用对象都要导出getPort()接口,每个port对应唯一一个应用对象,如果数据包到达后,协议会根据port寻找应该接受数据的应用对象。应用对象全部接受ApplicationManager的管理,当网络协议部分有数据需要提交给对应的应用时,需要通过ApplicationManager查询相应应用对象,它的代码如下:

package Application;import java.util.ArrayList;public class ApplicationManager  {private static ArrayList<IApplication> application_list = new ArrayList<IApplication>();private static ApplicationManager instance = null;private  ApplicationManager() {}public static  ApplicationManager getInstance() {if (instance == null) {instance = new ApplicationManager();}return instance;}public static void addApplication(IApplication app) {application_list.add(app);}public IApplication getApplicationByPort(int port) {for (int i = 0; i < application_list.size(); i++) {IApplication app = application_list.get(i);if (app.getPort() == port) {return app;}}return null;}}

实现网络协议的模块单独形成一个独立部分,实现具体网络协议的对象都继承统一的接口IProtocol:

package protocol;import java.util.HashMap;import jpcap.packet.Packet;public interface IProtocol {public byte[] createHeader(HashMap<String, Object> headerInfo);public HashMap<String, Object> handlePacket(Packet packet);
}

所有协议对象都接受ProtocolManager的统一管理,当应用对象需要调用某个协议对象创建包头时,需要经过ProtocolManager获取相应对象,同时它是唯一一个从网卡接收数据的对象,当网卡把数据包传递给它后,它通过解析网络包的以太太包头,决定把数据包转交给对应的网络协议对象解析,它的代码如下:

package protocol;import java.util.Arrays;
import java.util.HashMap;import Application.ApplicationManager;
import Application.IApplication;
import datalinklayer.DataLinkLayer;
import jpcap.PacketReceiver;
import jpcap.packet.EthernetPacket;
import jpcap.packet.IPPacket;
import jpcap.packet.Packet;public class ProtocolManager implements PacketReceiver{private static ProtocolManager instance = null;private static ARPProtocolLayer arpLayer = null;private static DataLinkLayer dataLinkInstance = null;private static HashMap<String , byte[] > ipToMacTable = null;private static HashMap<String, byte[]> dataWaitToSend = null;private static byte[] broadcast=new byte[]{(byte)255,(byte)255,(byte)255,(byte)255,(byte)255,(byte)255};private ProtocolManager() {}public static ProtocolManager getInstance() {if (instance == null) {instance = new ProtocolManager();dataLinkInstance = DataLinkLayer.getInstance();ipToMacTable = new HashMap<String, byte[]>();dataWaitToSend = new HashMap<String, byte[]>();dataLinkInstance.registerPacketReceiver(instance);arpLayer = new ARPProtocolLayer();}return instance;}public IProtocol getProtocol(String name) {switch (name.toLowerCase()) {case "icmp":return new ICMPProtocolLayer();case "ip":return new IPProtocolLayer();}return null;}public void sendData(byte[] data, byte[] ip) throws Exception {/** 发送数据前先检查给定ip的mac地址是否存在,如果没有则先让ARP协议获取mac地址*/byte[] mac = ipToMacTable.get(Arrays.toString(ip));if (mac == null) {HashMap<String, Object> headerInfo = new HashMap<String, Object>();headerInfo.put("sender_ip", ip);byte[] arpRequest = arpLayer.createHeader(headerInfo);if (arpRequest == null) {throw new Exception("Get mac adress header fail");}dataLinkInstance.sendData(arpRequest, broadcast, EthernetPacket.ETHERTYPE_ARP);//将要发送的数据存起,等待mac地址返回后再发送dataWaitToSend.put(Arrays.toString(ip), data);} else {//如果mac地址已经存在则直接发送数据dataLinkInstance.sendData(data, mac, IPPacket.IPPROTO_IP);}}@Overridepublic void receivePacket(Packet packet) {if (packet == null) {return;}//确保收到数据包是arp类型EthernetPacket etherHeader = (EthernetPacket)packet.datalink;/** 数据链路层在发送数据包时会添加一个802.3的以太网包头,格式如下* 0-7字节:[0-6]Preamble , [7]start fo frame delimiter* 8-22字节: [8-13] destination mac, [14-19]: source mac * 20-21字节: type* type == 0x0806表示数据包是arp包, 0x0800表示IP包,0x8035是RARP包*/if (etherHeader.frametype == EthernetPacket.ETHERTYPE_ARP) {//调用ARP协议解析数据包ARPProtocolLayer arpLayer = new ARPProtocolLayer();HashMap<String, Object> info = arpLayer.handlePacket(packet);byte[] senderIP = (byte[])info.get("sender_ip");byte[] senderMac = (byte[])info.get("sender_mac");ipToMacTable.put(Arrays.toString(senderIP), senderMac);//一旦有mac地址更新后,查看缓存表是否有等待发送的数据sendWaitingData(senderIP);}//处理IP包头if (etherHeader.frametype == EthernetPacket.ETHERTYPE_IP) {handleIPPacket(packet);}}private void handleIPPacket(Packet packet) {IProtocol ipProtocol = new IPProtocolLayer();HashMap<String, Object> info = ipProtocol.handlePacket(packet);if (info == null) {return ;}byte protocol = 0;if (info.get("protocol") != null) {protocol = (byte)info.get("protocol");//设置下一层协议的头部packet.header = (byte[])info.get("header");System.out.println("receive packet with protocol: " + protocol);}if (protocol != 0) {switch(protocol) {case IPPacket.IPPROTO_ICMP:handleICMPPacket(packet);break;default:return;}}}private void handleICMPPacket(Packet packet) {IProtocol icmpProtocol = new ICMPProtocolLayer();HashMap<String, Object> headerInfo = icmpProtocol.handlePacket(packet);short identifier = (short)headerInfo.get("identifier");IApplication app = ApplicationManager.getInstance().getApplicationByPort(identifier);if (app != null && app.isClosed() != true) {app.handleData(headerInfo);}}private void sendWaitingData(byte[] destIP) {byte[] data = dataWaitToSend.get(Arrays.toString(destIP));byte[] mac = ipToMacTable.get(Arrays.toString(destIP));if (data != null && mac != null) {dataLinkInstance.sendData(data, mac, EthernetPacket.ETHERTYPE_IP);}}
}

从代码我们看到,一旦数据包到来时,它的receivePacket接口会被调用,它通过嗅探以太包头判断数据包应该提交给哪种网络协议,在代码中目前我们只实现了对两种网络数据包的处理,一种是ARP包,一种是IP包。

它也负责发送数据,当应用或者协议需要把数据包发送出去时,需要调用它的sendData接口。它会先检查接收者IP对应的mac地址是否在缓存表中,如果没有,它会调用ARPProtocolLayer对象,通过ARP协议获取给定IP的mac地址。然后再调用其他协议对象,结合获得的mac地址去发送数据。

如果接收到的数据包是IP包,它会调用IPProtocolLayer对象解析协议包头,根据解析后返回的字段采取下一步行动,IP包头下面往往会跟着其他协议,由于我们本节实现ICMP ping应用,因此在代码中它监控IP处理后接下来是否要走ICMP协议,这些逻辑都在接口handleIPPacket中实现。如果所有协议处理完毕,需要把数据提交给对应的应用时,它会通过ApplicationManager把数据提交过去,这个逻辑在handleICMPPacket调用中有实现。

接下来我们看看ping应用的实现:

package Application;import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Random;import protocol.ICMPProtocolLayer;
import protocol.IProtocol;
import protocol.ProtocolManager;public class PingApp extends Application{private int echo_times = 0;private short identifier = 0;private short sequence = 0;private byte[] destIP = null;/** times: 连续发送多少次数据包* destIP: ping的对象*/public PingApp(int times, byte[] destIP ) {if (times > 0) {echo_times = times;} else {throw new IllegalArgumentException("ehoc times must > 0");}Random rand = new Random();identifier = (short) (rand.nextInt() & 0x0000FFFF);this.destIP = destIP;this.port = identifier;}public void startPing() {for (int i = 0; i < this.echo_times; i++) {try {byte[] packet = createPackage(null);ProtocolManager.getInstance().sendData(packet, destIP);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}private byte[] createPackage(byte[] data) throws Exception {byte[] icmpEchoHeader = this.createICMPEchoHeader();if (icmpEchoHeader == null) {throw new Exception("ICMP Header create fail");}		byte[] ipHeader = this.createIP4Header(icmpEchoHeader.length);//分别构建ip包头和icmp echo包头后,将两个包头结合在一起byte[] packet  = new byte[icmpEchoHeader.length + ipHeader.length];ByteBuffer packetBuffer = ByteBuffer.wrap(packet);packetBuffer.put(ipHeader);packetBuffer.put(icmpEchoHeader);return packetBuffer.array();}private byte[] createICMPEchoHeader() {IProtocol icmpProto = ProtocolManager.getInstance().getProtocol("icmp");if (icmpProto == null) {return null;}//构造icmp echo 包头HashMap<String, Object> headerInfo = new HashMap<String, Object>();headerInfo.put("header", "echo");headerInfo.put("identifier", identifier);headerInfo.put("sequence_number", sequence);sequence++;//附带当前时间long time = System.currentTimeMillis();ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);buffer.putLong(time);byte[] timeBuffer = buffer.array();headerInfo.put("data", timeBuffer);byte[] icmpEchoHeader = icmpProto.createHeader(headerInfo);return icmpEchoHeader;}private byte[] createIP4Header(int dataLength) {IProtocol ip4Proto = ProtocolManager.getInstance().getProtocol("ip");if (ip4Proto == null || dataLength <= 0) {return null;}//创建IP包头默认情况下只需要发送数据长度,下层协议号,接收方ip地址HashMap<String, Object> headerInfo = new HashMap<String, Object>();headerInfo.put("data_length", dataLength);ByteBuffer destIP = ByteBuffer.wrap(this.destIP);headerInfo.put("destination_ip", destIP.getInt());byte protocol = ICMPProtocolLayer.PROTOCL_ICMP;headerInfo.put("protocol", protocol);headerInfo.put("identification", (short)this.port);byte[] ipHeader = ip4Proto.createHeader(headerInfo);return ipHeader;}@Overridepublic void handleData(HashMap<String, Object> data) {long time = System.currentTimeMillis();short sequence = (short)data.get("sequence");byte[] time_buf = (byte[])data.get("data");ByteBuffer buf = ByteBuffer.wrap(time_buf);long send_time = buf.getLong();System.out.println("receive reply for ping request " + sequence + "for  " + (time - send_time) / 1000 + "secs");}}

它通过调用IPProtocoalLayer和ICMPProtocolLayer组装包头,以便发生ping数据包,它所做的工作就是组装出如下格式的数据包:

屏幕快照 2019-01-25 上午10.16.01.png

从上图看,ping数据包分成两部分,一部分是上面的IP包头,它有20字节,第二部分是下面的ICMP header,有8字节,最后是payload,这部分由程序自己附带,收到ping包的对方会原封不动的把payload转发回来。在Ping应用实现中,我们附带的payload是当前数据包的组建时间,当下次接收到回应时,我们把这个时间拿到,再结合当前时间就可以知道数据传递的一个来回需要多久。

在ping应用中,createIP4Header调用IPProtocolLayer组装IP包头,createICMPEchoHeader调用ICMPProtocolLayer组装ICMP header。当数据包返回后,它的handleData被调用,它在该接口里对返回数据进行操作。我们看看IPProtocolLayer的实现:

package protocol;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;import datalinklayer.DataLinkLayer;
import jpcap.packet.Packet;
import utils.Utility;public class IPProtocolLayer implements IProtocol{private static int ETHERNET_FRAME_HEADER_LENGTH = 14;private static byte IP_VERSION = 4;private static int CHECKSUM_OFFSET = 10;private static int HEADER_LENGTH_OFFSET = 0 + ETHERNET_FRAME_HEADER_LENGTH;private static int TOTAL_LENGTH_OFFSET = 2 + ETHERNET_FRAME_HEADER_LENGTH;private static int SOURCE_IP_OFFSET = 12 + ETHERNET_FRAME_HEADER_LENGTH;private static int DEST_IP_OFFSET = 16 + ETHERNET_FRAME_HEADER_LENGTH;private static int PROTOCOL_INDICATOR_OFFSET = 9 + ETHERNET_FRAME_HEADER_LENGTH;@Overridepublic byte[] createHeader(HashMap<String, Object> headerInfo) {byte version = (byte) (IP_VERSION & 0x0F);byte internetHeaderLength = 5;if (headerInfo.get("internet_header_length") != null) {internetHeaderLength = (byte)headerInfo.get("internet_header_length");}byte[] buffer = new byte[internetHeaderLength * 4];ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);byteBuffer.put((byte) (version << 4 | internetHeaderLength));byte b = byteBuffer.get(0);byte dscp = 0;if (headerInfo.get("dscp") != null) {dscp = (byte)headerInfo.get("dscp");}byte ecn = 0;if (headerInfo.get("ecn") != null) {ecn = (byte)headerInfo.get("ecn");}byteBuffer.put((byte)(dscp << 2 | ecn));if (headerInfo.get("data_length") == null) {return null;}/** 总长度等于IP数据包包头长度加上末尾option长度加上后续数据长度*/int optionLength = 0;byte[] options = null;if (headerInfo.get("options") != null) {options = (byte[])headerInfo.get("options");optionLength += options.length;}short totalLength = (short) ((int)headerInfo.get("data_length") + optionLength + internetHeaderLength*4);byteBuffer.order(ByteOrder.BIG_ENDIAN);byteBuffer.putShort(totalLength);short identification = 0;if (headerInfo.get("identification") != null) {identification = (short)headerInfo.get("identification");}byteBuffer.order(ByteOrder.BIG_ENDIAN);byteBuffer.putShort(identification);short flagAndOffset = 0;if (headerInfo.get("flag") != null) {flagAndOffset =  (short) (((short)headerInfo.get("flag")) << 13);}if (headerInfo.get("fragment_offset") != null) {flagAndOffset |= ((short)headerInfo.get("fragment_offset"));}byteBuffer.order(ByteOrder.BIG_ENDIAN);byteBuffer.putShort(flagAndOffset);byte timeToLive = 64;if (headerInfo.get("time_to_live") != null) {timeToLive = (byte)headerInfo.get("time_to_live");}byteBuffer.put(timeToLive);byte protocol = 0;if (headerInfo.get("protocol") == null) {return null;}protocol = (byte)headerInfo.get("protocol");byteBuffer.put(protocol);short checkSum = 0;byteBuffer.order(ByteOrder.BIG_ENDIAN);byteBuffer.putShort(checkSum);//设置source ipbyte[] ipArr = DataLinkLayer.getInstance().deviceIPAddress();ByteBuffer ip = ByteBuffer.wrap(ipArr);int srcIP = ip.getInt();byteBuffer.order(ByteOrder.BIG_ENDIAN);byteBuffer.putInt(srcIP);int destIP = 0;if (headerInfo.get("destination_ip") == null) {return null;}byteBuffer.order(ByteOrder.BIG_ENDIAN);destIP = (int)headerInfo.get("destination_ip");byteBuffer.putInt(destIP);if (headerInfo.get("options") != null) {byteBuffer.put(options);}checkSum = (short) Utility.checksum(byteBuffer.array(), byteBuffer.array().length);byteBuffer.order(ByteOrder.BIG_ENDIAN);byteBuffer.putShort(CHECKSUM_OFFSET, checkSum);return byteBuffer.array();}@Overridepublic HashMap<String, Object> handlePacket(Packet packet) {/** 解析收到数据包的IP包头,暂时不做校验和检测,默认网络发送的数据包不会出错,* 暂时忽略对option段的处理*/byte[] ip_data = new byte[packet.header.length +  packet.data.length];ByteBuffer buffer = ByteBuffer.wrap(ip_data);buffer.put(packet.header);buffer.put(packet.data);HashMap<String, Object> headerInfo = new HashMap<String, Object>();//获取发送者IPbyte[] src_ip = new byte[4];buffer.position(SOURCE_IP_OFFSET);buffer.get(src_ip, 0, 4);headerInfo.put("source_ip", src_ip);//获取接受者IPbyte[] dest_ip = new byte[4];buffer.position(DEST_IP_OFFSET);buffer.get(dest_ip, 0, 4);headerInfo.put("dest_ip", dest_ip);//确保接受者是我们自己byte[] ip = DataLinkLayer.getInstance().deviceIPAddress();for (int i = 0; i < ip.length; i++) {if (ip[i] != dest_ip[i]) {return null;}}//获得下一层协议编号buffer.position(0);byte protocol = buffer.get(PROTOCOL_INDICATOR_OFFSET);headerInfo.put("protocol", protocol);int k = 0;if (protocol == 1) {k = 2;System.out.println("receive protocol 2");}byte headerLength = buffer.get(HEADER_LENGTH_OFFSET);headerLength &= 0x0F;//*4得到包头字节长度headerLength *= 4; short totalLength = buffer.getShort(TOTAL_LENGTH_OFFSET);int dataLength = totalLength - headerLength;;byte[] data = new byte[dataLength];buffer.position(headerLength + ETHERNET_FRAME_HEADER_LENGTH);buffer.get(data, 0, dataLength);headerInfo.put("header", data);return headerInfo;}}

它的目的很简单,就是根据上图包头的字段组装协议包头,如果有对应的数据包抵达,它根据协议包头字段对数据进行解析。我们再看看ICMPProtocolLayer的实现:

package protocol;import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;import jpcap.PacketReceiver;
import jpcap.packet.EthernetPacket;
import jpcap.packet.Packet;public class ICMPProtocolLayer implements IProtocol{public static byte PROTOCL_ICMP = 1;   private ArrayList<IProtocol> protocol_header_list = new ArrayList<IProtocol>();private Packet packet;public ICMPProtocolLayer() {//增加icmp echo 协议包头创建对象protocol_header_list.add(new ICMPEchoHeader());}//checkType针对的是IPV6private HashMap<String, Object> analyzeICMPMessage() {HashMap<String, Object> info = null;info = handleICMPInfoMsg(this.packet);return info;}private HashMap<String, Object> handleICMPInfoMsg(Packet packet) {for (int i = 0; i < protocol_header_list.size(); i++) {IProtocol handler = protocol_header_list.get(i);HashMap<String, Object> info = handler.handlePacket(packet);if (info != null) {return info;}}return null;}@Overridepublic byte[] createHeader(HashMap<String, Object> headerInfo) {for (int i = 0; i < protocol_header_list.size(); i++) {byte[] buff = protocol_header_list.get(i).createHeader(headerInfo);if (buff != null) {return buff;}}return null;}@Overridepublic HashMap<String, Object> handlePacket(Packet packet) {this.packet = packet;return analyzeICMPMessage();}}

ICMPProtocolLayer 很简单,它只是一个框架,因为ICMP具体数据包的形式多样,因此我们依旧使用责任链模式把具体工作分发给具体对象,例如我们要组装ping数据包对应的echo包头,据需要下面具体的实现实例:

package protocol;import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Random;import jpcap.packet.Packet;
import utils.Utility;public class ICMPEchoHeader implements IProtocol{private static int ICMP_EOCH_HEADER_LENGTH = 8;private static byte ICMP_ECHO_TYPE = 8;private static byte ICMP_ECHO_REPLY_TYPE = 0;private static short ICMP_ECHO_IDENTIFIER_OFFSET = 4;private static short ICMP_ECHO_SEQUENCE_NUM_OFFSET = 6;private static short ICMP_ECHO_OPTIONAL_DATA_OFFSET = 8;private static short ICMP_ECHO_ONLY_HEADER_LENGTH = 8;@Overridepublic byte[] createHeader(HashMap<String, Object> headerInfo) {String headerName = (String)headerInfo.get("header");if (headerName != "echo" && headerName != "echo_reply") {return null;}int bufferLen = ICMP_EOCH_HEADER_LENGTH;int dataLen = ((byte[])headerInfo.get("data")).length;if (headerInfo.get("data") != null) {bufferLen += ((byte[])headerInfo.get("data")).length;}byte[] buffer = new byte[bufferLen ];ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);byte type = ICMP_ECHO_TYPE;if (headerName == "echo_reply") {type = ICMP_ECHO_REPLY_TYPE;}byteBuffer.put(type);byte code = 0;byteBuffer.put(code);short checkSum = 0;byteBuffer.order(ByteOrder.BIG_ENDIAN);byteBuffer.putShort(checkSum);short identifier = 0;if (headerInfo.get("identifier") == null) {Random ran = new Random();identifier = (short) ran.nextInt();headerInfo.put("identifier", identifier);}identifier = (short) headerInfo.get("identifier");byteBuffer.order(ByteOrder.BIG_ENDIAN);byteBuffer.putShort(identifier);System.out.println("ICMP echo header, identifier: " + String.format("0x%08x", identifier));short sequenceNumber = 0;if (headerInfo.get("sequence_number") != null) {sequenceNumber = (short) headerInfo.get("sequence_number");}headerInfo.put("sequence_number", sequenceNumber);byteBuffer.order(ByteOrder.BIG_ENDIAN);byteBuffer.putShort(sequenceNumber);System.out.println("ICMP echo header, sequence: " + String.format("0x%08x", sequenceNumber));if (headerInfo.get("data") != null) {byte[] data = (byte[])headerInfo.get("data");byteBuffer.put(data, 0, data.length);}checkSum = (short) Utility.checksum(byteBuffer.array(), byteBuffer.array().length);byteBuffer.order(ByteOrder.BIG_ENDIAN);byteBuffer.putShort(2, checkSum);System.out.println("ICMP echo header, checksum: " + String.format("0x%08x", checkSum));return byteBuffer.array();}@Overridepublic HashMap<String, Object> handlePacket(Packet packet) {ByteBuffer buffer = ByteBuffer.wrap(packet.header);if (buffer.get(0) != ICMP_ECHO_REPLY_TYPE) {return null;}HashMap<String, Object> header = new HashMap<String, Object>();header.put("identifier", buffer.getShort(ICMP_ECHO_IDENTIFIER_OFFSET));header.put("sequence", buffer.getShort(ICMP_ECHO_SEQUENCE_NUM_OFFSET));;if (packet.header.length > ICMP_ECHO_ONLY_HEADER_LENGTH) {header.put("data", packet.data);}return header;}}

上面协议对象负责组装ping协议包头,如果ping数据包返回,它也会根据相应的包头字段进行解读,解读后获得的数据就会提交给对应的应用对象。更加详细的代码讲解和调试演示请观看视频。

上面代码运行后,情况如下:

屏幕快照 2019-01-25 上午10.37.31.png

我们构造了一个ping数据包,发送给路由器,路由器收到后返回数据包给Ping应用,这一来回用时15秒,之所以那么久是因为我在代码中设置断点调试所致。

更详实的讲解以及抓包演示,请通过下面链接观看视频:
更详细的讲解和代码调试演示过程,请点击链接

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:
这里写图片描述

这篇关于从0到1用java再造tcpip协议栈:架构重建,完整实现ping应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/950045

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

Spring Security 基于表达式的权限控制

前言 spring security 3.0已经可以使用spring el表达式来控制授权,允许在表达式中使用复杂的布尔逻辑来控制访问的权限。 常见的表达式 Spring Security可用表达式对象的基类是SecurityExpressionRoot。 表达式描述hasRole([role])用户拥有制定的角色时返回true (Spring security默认会带有ROLE_前缀),去

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

Spring Security--Architecture Overview

1 核心组件 这一节主要介绍一些在Spring Security中常见且核心的Java类,它们之间的依赖,构建起了整个框架。想要理解整个架构,最起码得对这些类眼熟。 1.1 SecurityContextHolder SecurityContextHolder用于存储安全上下文(security context)的信息。当前操作的用户是谁,该用户是否已经被认证,他拥有哪些角色权限…这些都被保

Spring Security基于数据库验证流程详解

Spring Security 校验流程图 相关解释说明(认真看哦) AbstractAuthenticationProcessingFilter 抽象类 /*** 调用 #requiresAuthentication(HttpServletRequest, HttpServletResponse) 决定是否需要进行验证操作。* 如果需要验证,则会调用 #attemptAuthentica

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

Java架构师知识体认识

源码分析 常用设计模式 Proxy代理模式Factory工厂模式Singleton单例模式Delegate委派模式Strategy策略模式Prototype原型模式Template模板模式 Spring5 beans 接口实例化代理Bean操作 Context Ioc容器设计原理及高级特性Aop设计原理Factorybean与Beanfactory Transaction 声明式事物

mybatis的整体架构

mybatis的整体架构分为三层: 1.基础支持层 该层包括:数据源模块、事务管理模块、缓存模块、Binding模块、反射模块、类型转换模块、日志模块、资源加载模块、解析器模块 2.核心处理层 该层包括:配置解析、参数映射、SQL解析、SQL执行、结果集映射、插件 3.接口层 该层包括:SqlSession 基础支持层 该层保护mybatis的基础模块,它们为核心处理层提供了良好的支撑。

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

百度/小米/滴滴/京东,中台架构比较

小米中台建设实践 01 小米的三大中台建设:业务+数据+技术 业务中台--从业务说起 在中台建设中,需要规范化的服务接口、一致整合化的数据、容器化的技术组件以及弹性的基础设施。并结合业务情况,判定是否真的需要中台。 小米参考了业界优秀的案例包括移动中台、数据中台、业务中台、技术中台等,再结合其业务发展历程及业务现状,整理了中台架构的核心方法论,一是企业如何共享服务,二是如何为业务提供便利。