前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android11 DHCP初识

Android11 DHCP初识

作者头像
用户7557625
发布2021-09-09 16:50:58
1.8K0
发布2021-09-09 16:50:58
举报

先简单说一下DHCP过程,对于代码理解会有帮助。 1、客户端发起Discover,来寻找DHCP服务器 2、DHCPServer给Clinet发送offer 3、DHCPClient接受Offer以后发送DHCPRequest 4、DHCPServer发送packet ack表示可以用此IP,结束。

客户端代码主要在DhcpClient.java中。里面有一个状态机。 frameworks/base/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java

在这里插入图片描述
在这里插入图片描述

DHCP客户端日志

09-07 11:17:32.758  1695  7348 D DhcpClient: Receive thread started
09-07 11:17:32.774  1695  3991 D DhcpClient: Broadcasting DHCPDISCOVER
09-07 11:17:32.783  1695  7348 D DhcpClient: Received packet: 78:8e:33:2a:57:9c OFFER, ip /192.168.1.171, mask /255.255.255.0, DNS servers: /218.2.135.1 /114.114.114.114 , gateways [/192.168.1.1] lease time 6912, domain null
09-07 11:17:32.784  1695  3991 D DhcpClient: Got pending lease: android.net.networkstack.DhcpResults@c1d00c7 DHCP server /192.168.1.1 Vendor info null lease 6912 seconds Servername 
09-07 11:17:32.787  1695  3991 D DhcpClient: Broadcasting DHCPREQUEST ciaddr=0.0.0.0 request=192.168.1.171 serverid=192.168.1.1
09-07 11:17:32.799  1695  7348 D DhcpClient: Received packet: 78:8e:33:2a:57:9c ACK: your new IP /192.168.1.171, netmask /255.255.255.0, gateways [/192.168.1.1] DNS servers: /218.2.135.1 /114.114.114.114 , lease time 7200
09-07 11:17:32.801  1695  3991 D DhcpClient: Confirmed lease: android.net.networkstack.DhcpResults@a271e1d DHCP server /192.168.1.1 Vendor info null lease 7200 seconds Servername 
09-07 11:17:32.819  1695  3991 D DhcpClient: Scheduling renewal in 3599s
09-07 11:17:32.820  1695  3991 D DhcpClient: Scheduling rebind in 6299s
09-07 11:17:32.820  1695  3991 D DhcpClient: Scheduling expiry in 7199s

在DHCP客户端,有一个接收线程,用于接收DHCP的packet。

class ReceiveThread extends Thread {

    private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
    private volatile boolean mStopped = false;

    public void halt() {
        mStopped = true;
        closeSockets();  // Interrupts the read() call the thread is blocked in.
    }

    @Override
    public void run() {
        if (DBG) Log.d(TAG, "Receive thread started");
        while (!mStopped) {
            int length = 0;  // Or compiler can't tell it's initialized if a parse error occurs.
            try {
                length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
                DhcpPacket packet = null;
                packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
                if (DBG) Log.d(TAG, "Received packet: " + packet);
                sendMessage(CMD_RECEIVED_PACKET, packet);
            } catch (IOException|ErrnoException e) {
                if (!mStopped) {
                    Log.e(TAG, "Read error", e);
                    logError(DhcpErrorEvent.RECEIVE_ERROR);
                }
            } catch (DhcpPacket.ParseException e) {
                Log.e(TAG, "Can't parse packet: " + e.getMessage());
                if (PACKET_DBG) {
                    Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
                }
                if (e.errorCode == DhcpErrorEvent.DHCP_NO_COOKIE) {
                    int snetTagId = 0x534e4554;
                    String bugId = "31850211";
                    int uid = -1;
                    String data = DhcpPacket.ParseException.class.getName();
                    EventLog.writeEvent(snetTagId, bugId, uid, data);
                }
                logError(e.errorCode);
            }
        }
        if (DBG) Log.d(TAG, "Receive thread stopped");
    }
}

初始状态是StoppedState

setInitialState(mStoppedState);

IPClient会发送CMD_START_DHCP,然后DHCPClient接收到以后会开启DHCP,进入DhcpInitState

class StoppedState extends State {
    @Override
    public boolean processMessage(Message message) {
        switch (message.what) {
            case CMD_START_DHCP:
                if (mRegisteredForPreDhcpNotification) {
                    transitionTo(mWaitBeforeStartState);
                } else {
                    transitionTo(mDhcpInitState);
                }
                return HANDLED;

DhcpInitState 会接收packet包,如果数据包合法,则发送请求数据包。

class DhcpInitState extends PacketRetransmittingState {
    public DhcpInitState() {
        super();
    }

    @Override
    public void enter() {
        super.enter();
        startNewTransaction();
        mLastInitEnterTime = SystemClock.elapsedRealtime();
    }

    protected boolean sendPacket() {
        return sendDiscoverPacket();
    }

    protected void receivePacket(DhcpPacket packet) {
        if (!isValidPacket(packet)) return;
        if (!(packet instanceof DhcpOfferPacket)) return;
        mOffer = packet.toDhcpResults();
        if (mOffer != null) {
            Log.d(TAG, "Got pending lease: " + mOffer);
            transitionTo(mDhcpRequestingState);
        }
    }
}

DhcpRequestingState 中可以看到,如果超时,会返回到DhcpInitState 。如果receive到DHCP结果,并且确认没问题,则进入ConfiguringInterfaceState

class DhcpRequestingState extends PacketRetransmittingState {
    public DhcpRequestingState() {
        mTimeout = DHCP_TIMEOUT_MS / 2;
    }

    protected boolean sendPacket() {
        return sendRequestPacket(
                INADDR_ANY,                                    // ciaddr
                (Inet4Address) mOffer.ipAddress.getAddress(),  // DHCP_REQUESTED_IP
                (Inet4Address) mOffer.serverAddress,           // DHCP_SERVER_IDENTIFIER
                INADDR_BROADCAST);                             // packet destination address
    }

    protected void receivePacket(DhcpPacket packet) {
        if (!isValidPacket(packet)) return;
        if ((packet instanceof DhcpAckPacket)) {
            DhcpResults results = packet.toDhcpResults();
            if (results != null) {
                setDhcpLeaseExpiry(packet);
                acceptDhcpResults(results, "Confirmed");
                transitionTo(mConfiguringInterfaceState);
            }
        } else if (packet instanceof DhcpNakPacket) {
            // TODO: Wait a while before returning into INIT state.
            Log.d(TAG, "Received NAK, returning to INIT");
            mOffer = null;
            transitionTo(mDhcpInitState);
        }
    }

    @Override
    protected void timeout() {
        // After sending REQUESTs unsuccessfully for a while, go back to init.
        transitionTo(mDhcpInitState);
    }
}

ConfiguringInterfaceState 这里就很简单了,直接跳转到DhcpBoundState

class ConfiguringInterfaceState extends LoggingState {
    @Override
    public void enter() {
        super.enter();
        mController.sendMessage(CMD_CONFIGURE_LINKADDRESS, mDhcpLease.ipAddress);
    }

    @Override
    public boolean processMessage(Message message) {
        super.processMessage(message);
        switch (message.what) {
            case EVENT_LINKADDRESS_CONFIGURED:
                transitionTo(mDhcpBoundState);
                return HANDLED;
            default:
                return NOT_HANDLED;
        }
    }
}

正常的一次DHCP流程就是这样,比较简单。 StoppedState->DhcpInitState ->DhcpRequestingState ->ConfiguringInterfaceState ->DhcpBoundState 但是DHCPClient实际比较复杂,有很多状态,以后慢慢再看。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-09-07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档