精华内容
下载资源
问答
  • DHCP获取IP地址的过程

    万次阅读 2012-02-28 16:34:48
    DHCP服务允许工作站连接到网络并且自动获取一个IP地址。配置DHCP服务服务器可以为每一个网络客户提供一个IP地址、子网掩码、缺省网关、一个WINS服务器的IP地址,以及一个DNS服务器的IP地址。  下面就对此做简单...
     DHCP称为动态主机配置协议。DHCP服务允许工作站连接到网络并且自动获取一个IP地址。配置DHCP服务的服务器可以为每一个网络客户提供一个IP地址、子网掩码、缺省网关、一个WINS服务器的IP地址,以及一个DNS服务器的IP地址。
    

        下面就对此做简单介绍:DHCP是一个基于广播的协议,它的操作可以归结为四个阶段,这些阶段是IP租用请求、IP租用提供、IP租用选择、IP租用确认。

    1、IP租用请求:在任何时候,客户计算机如果设置为自动获取IP地址,那么在它开机时,就会检查自己当前是否租用了一个IP地址,如果没有,它就向DCHP请求一个租用,由于该客户计算机并不知道DHCP服务器的地址,所以会用255.255.255.255作为目标地址,源地址使用0.0.0.0,在网络上广播一个DHCPDISCOVER消息,消息包含客户计算机的媒体访问控制(MAC)地址(网卡上内建的硬件地址)以及它的NetBIOS名字。

    2、IP租用提供:当DHCP服务器接收到一个来自客户的IP租用请求时,它会根据自己的作用域地址池为该客户保留一个IP地址并且在网络上广播一个来实现,该消息包含客户的MAC地址、服务器所能提供的IP地址、子网掩码、租用期限,以及提供该租用的DHCP服务器本身的IP地址。

    3、IP租用选择:如果子网还存在其它DHCP服务器,那么客户机在接受了某个DHCP服务器的DHCPOFFER消息后,它会广播一条包含提供租用的服务器的IP地址的DHCPREQUEST消息,在该子网中通告所有其它DHCP服务器它已经接受了一个地址的提供,其他DHCP服务器在接收到这条消息后,就会撤销为该客户提供的租用。然后把为该客户分配的租用地址返回到地址池中,该地址将可以重新作为一个有效地址提供给别的计算机使用。

    4、IP租用确认: DHCP服务器接收到来自客户的DHCPREQUEST消息,它就开始配置过程的最后一个阶段,这个确认阶段由DHCP服务器发送一个DHCPACK包给客户,该包包括一个租用期限和客户所请求的所有其它配置信息,至此,完成TCP/IP配置。

    展开全文
  • 安卓通过DHCP协议获取IP地址的过程

    千次阅读 2017-12-24 20:13:08
    安卓通过DHCP协议的DORA Discovery发现 Offer提供 Request请求 Ack确认 获取IP地址的过程

    安卓通过DHCP协议的DORA Discovery发现 Offer提供 Request请求 Ack确认 获取IP地址的过程
    安卓N之前 5.0 6.0通过 android_net_utils_runDhcp 方法运行 /system/bin/dhcpcd 获取ip地址
    安卓N之后 N不要了runDhcpcd(),而是通过DhcpClient
    DhcpClient是通过framework发送dhcpcd协议的UDP请求包直接去拿IP,不再使用开源的dhcpcd
    google还用了一个状态机 IpManager 来管理dhcpcd成功还是失败等状态,
    将ip赋值给IpConfiguration和LinkProperties传递到上层的framework

    /frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiStateMachine.java

    // 当WifiStateMachine状态机进入状态ObtainingIpState时 获取IP地址
    class ObtainingIpState extends State {
    @Override
    public void enter() {
    mIpManager.startProvisioning(IpManager.ProvisioningConfiguration prov); //启动获取IP地址
    }
    }
    ObtainingIpState所在状态机

    ObtainingIpState所在状态机

    /frameworks/base/services/net/java/android/net/ip/IpManager.java

    class StartedState extends State {} IpManager中的三个状态机
    class StoppedState extends State {}
    class StoppingState extends State {}

    public void startProvisioning(ProvisioningConfiguration req) {
        getNetworkInterface();
    
        mCallback.setNeighborDiscoveryOffload(true);
        // 给初始化的状态机 StoppedState 发送消息CMD_START
        sendMessage(CMD_START, new ProvisioningConfiguration(req));
    }
    

    ========================================================================
    class StoppedState extends State {
    @Override
    public boolean processMessage(Message msg) {
    switch (msg.what) {
    …..
    case CMD_START:
    mConfiguration = (ProvisioningConfiguration) msg.obj;
    // 接收到 CMD_START 会进行状态的切换,调用 StartedState的enter()方法
    transitionTo(mStartedState);
    break;
    …..
    }

    ==========================================

    class StartedState extends State {
    void enter(){
    if(startIPv4()) // 状态 StartedState 的enter 进入方法,调用startIPv4()函数
    }
    }

    ==========================================

    private boolean startIPv4() {
        // If we have a StaticIpConfiguration attempt to apply it and handle the result accordingly.
        if (mConfiguration.mStaticIpConfig != null) { // 如果有静态IP
            if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
                handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
            } else {
                if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
                recordMetric(IpManagerEvent.PROVISIONING_FAIL);
                mCallback.onProvisioningFailure(new LinkProperties(mLinkProperties));
                return false;
            }
        } else {
            // Start DHCPv4.  创建DhcpClient
            mDhcpClient = DhcpClient.makeDhcpClient(mContext, IpManager.this, mInterfaceName);
            mDhcpClient.registerForPreDhcpNotification(); // mRegisteredForPreDhcpNotification = true
    
            //接收前面发过来的mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);跟着跳转到DhcpInitState:
            mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); // 发送CMD_START_DHCP消息
    
            if (mConfiguration.mProvisioningTimeoutMs > 0) {
                final long alarmTime = SystemClock.elapsedRealtime() +
                        mConfiguration.mProvisioningTimeoutMs;
                mProvisioningTimeoutAlarm.schedule(alarmTime); // 在36秒后启动timeout超时操作
            }
        }
    
        return true;
    }
    
    
    private boolean setIPv4Address(LinkAddress address) {
        final InterfaceConfiguration ifcg = new InterfaceConfiguration();
        ifcg.setLinkAddress(address);
        try {
        final INetworkManagementService mNwService.setInterfaceConfig(mInterfaceName, ifcg);
            if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
        } catch (IllegalStateException | RemoteException e) {
            Log.e(mTag, "IPv4 configuration failed: ", e);
            return false;
        }
        return true;
    }
    

    ==========================================
    //接收前面发过来的mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP);跟着跳转到 DhcpInitState:
    mDhcpClient.sendMessage(DhcpClient.CMD_START_DHCP); // 发送CMD_START_DHCP消息

    /frameworks/base/services/net/java/android/net/dhcp/DhcpClient.java

    class StoppedState extends LoggingState {
        @Override
        public boolean processMessage(Message message) {
            super.processMessage(message);
            switch (message.what) {
                case CMD_START_DHCP:
                    if (mRegisteredForPreDhcpNotification) {
                        transitionTo(mWaitBeforeStartState);
                    } else {
                        transitionTo(mDhcpInitState);  // 状态跳转到 DhcpInitState
                    }
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
        }
    

    }

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

    //进入状态时启动一次startNewTransaction
    //DORA Discover发现 Offer提供 Request请求 ACK确认 开始Discovery 发现?
    @Override
    public void enter() {
    super.enter(); // 调用父类的 enter 方法中
    startNewTransaction();
    }

        protected boolean sendPacket() {
            return sendDiscoverPacket(); // 发送  DiscoverPacket 发现包
        }
    
        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); // 接收到了 Offer包  接下来发送 Request 请求包
            }
        }
    }
    
        private void startNewTransaction() {
        mTransactionId = mRandom.nextInt();  // 传输包的id号?
        mTransactionStartMillis = SystemClock.elapsedRealtime();
    }
    ==================================================
    PacketRetransmittingState
    
       abstract class PacketRetransmittingState extends LoggingState {
    
        private int mTimer;
        protected int mTimeout = 0;
    
        @Override
        public void enter() {
            super.enter();
            initTimer();
            maybeInitTimeout();
            sendMessage(CMD_KICK);  // 发送消息  CMD_KICK
        }
    
        @Override
        public boolean processMessage(Message message) {
            super.processMessage(message);
            switch (message.what) {
                case CMD_KICK:
    

    // 调用了 sendPacket()抽象方法 所以就是之前的DhcpInitState的具体的实现 sendPacket()
    sendPacket();
    scheduleKick();
    return HANDLED;
    case CMD_RECEIVED_PACKET:
    receivePacket((DhcpPacket) message.obj);
    return HANDLED;
    case CMD_TIMEOUT:
    timeout();
    return HANDLED;
    default:
    return NOT_HANDLED;
    }
    }

        public void exit() {
            mKickAlarm.cancel();
            mTimeoutAlarm.cancel();
        }
    
        }
        abstract protected boolean sendPacket();
    

    abstract protected void receivePacket(DhcpPacket packet);

    class DhcpInitState extends PacketRetransmittingState {
    
        protected boolean sendPacket() {
            return sendDiscoverPacket(); // 发送  DiscoverPacket 发现包
        }
    
    
    private boolean sendDiscoverPacket() {
        ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
                DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr,
                DO_UNICAST, REQUESTED_PARAMS);  // 创建 ByteBuffer的UDP包 
    

    return transmitPacket(packet, “DHCPDISCOVER”, DhcpPacket.ENCAP_L2, INADDR_BROADCAST); // 发送
    }

    }

     private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
        try {
            if (encap == DhcpPacket.ENCAP_L2) {
                if (DBG) Log.d(TAG, "Broadcasting " + description);
                // 送这里发送出去
                Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
            } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
                if (DBG) Log.d(TAG, "Broadcasting " + description);
                // N.B.: We only need this codepath because DhcpRequestPacket
                // hardcodes the source IP address to 0.0.0.0. We could reuse
                // the packet socket if this ever changes.
                Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
            } else {
                if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",description, Os.getpeername(mUdpSock)));
                Os.write(mUdpSock, buf);
            }
        } catch(ErrnoException|IOException e) {
            Log.e(TAG, "Can't send packet: ", e);
            return false;
        }
        return true;
    }
    

    ==================================================================
    另外一边有一个接收线程ReceiveThread run(),一直在收dhcp server的的数据包。

    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 {
    
                    //读取DHCP服务发出的OFFER提交包
                    length = Os.read(mPacketSock 【FileDescriptor】, 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); // 发送接收到消息 CMD_RECEIVED_PACKET
                } catch (IOException|ErrnoException e) {
                    if (!mStopped) {
                        Log.e(TAG, "Read error", e);
                        DhcpErrorEvent.logReceiveError(mIfaceName);
                    }
                } catch (DhcpPacket.ParseException e) {
                    Log.e(TAG, "Can't parse packet: " + e.getMessage());
                    if (PACKET_DBG) {
                        Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
                    }
                    DhcpErrorEvent.logParseError(mIfaceName, e.errorCode);
                }
            }
            if (DBG) Log.d(TAG, "Receive thread stopped");
        }
    

    }

    abstract class PacketRetransmittingState extends LoggingState {
    
        @Override
        public boolean processMessage(Message message) {
            super.processMessage(message);
            switch (message.what) {
                case CMD_RECEIVED_PACKET:
                    receivePacket((DhcpPacket) message.obj); // 调用子类具体实现的receivePacket 方法
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
        }
    }
    

    class DhcpInitState extends PacketRetransmittingState {

    class DhcpInitState extends PacketRetransmittingState {
    
        protected void receivePacket(DhcpPacket packet) {   // 完成 DORA 中的 Offer的阶段 
            if (!isValidPacket(packet)) return;
            if (!(packet instanceof DhcpOfferPacket)) return;
            mOffer = packet.toDhcpResults();
            if (mOffer != null) {
                Log.d(TAG, "Got pending lease: " + mOffer);
        // 接收到了来自DHCP服务器的OFFER包,切换状态到  DhcpRequestingState  并进入到 enter() 方法
                transitionTo(mDhcpRequestingState); 
            }
        }
    }
    

    }

    ===================================================================

    class DhcpRequestingState extends PacketRetransmittingState { // 进入到父类的enter 方法
        public DhcpRequestingState() {
            mTimeout = DHCP_TIMEOUT_MS / 2;  
        }
    
    
        public void enter() {// 进入到父类的enter 方法
            super.enter();
            initTimer();
            maybeInitTimeout();
            sendMessage(CMD_KICK); // 再次发送 CMD_KICK 消息
        }
    
                public boolean processMessage(Message message) {
            super.processMessage(message);
            switch (message.what) {
                case CMD_KICK:   //  收到 CMD_KICK 消息 
                    sendPacket();  // 发送包方法,此时调用的是具体子类 DhcpRequestingState的发送方法
                    scheduleKick();
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
        }
    
    
         // 发送请求   完成  DORA中的  Request的阶段
         // 此时 继续在接收线程中等待接收来自服务器的ACK DHCP数据包
        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);
            }
        }
    
    
    
    private boolean sendRequestPacket(
            Inet4Address clientAddress, Inet4Address requestedAddress,
            Inet4Address serverAddress, Inet4Address to) {
        // TODO: should we use the transaction ID from the server?
        final int encap = INADDR_ANY.equals(clientAddress)
                ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
    
        ByteBuffer packet = DhcpPacket.buildRequestPacket(
                encap, mTransactionId, getSecs(), clientAddress,
                DO_UNICAST, mHwAddr, requestedAddress,
                serverAddress, REQUESTED_PARAMS, null);
        String serverStr = (serverAddress != null) ? serverAddress.getHostAddress() : null;
        String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
                             " request=" + requestedAddress.getHostAddress() +
                             " serverid=" + serverStr;
        return transmitPacket(packet, description, encap, to);  // 发送数据包
    }
    
    
    
    return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); // 发送
    }
    

    }

     private boolean transmitPacket(ByteBuffer buf, String description, int encap, Inet4Address to) {
        try {
            if (encap == DhcpPacket.ENCAP_L2) {
                if (DBG) Log.d(TAG, "Broadcasting " + description);
                // 送这里发送出去  真正发送
                Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
            } else if (encap == DhcpPacket.ENCAP_BOOTP && to.equals(INADDR_BROADCAST)) {
                if (DBG) Log.d(TAG, "Broadcasting " + description);
                // N.B.: We only need this codepath because DhcpRequestPacket
                // hardcodes the source IP address to 0.0.0.0. We could reuse
                // the packet socket if this ever changes.
                Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
            } else {
                if (DBG) Log.d(TAG, String.format("Unicasting %s to %s",description, Os.getpeername(mUdpSock)));
                Os.write(mUdpSock, buf);
            }
        } catch(ErrnoException|IOException e) {
            Log.e(TAG, "Can't send packet: ", e);
            return false;
        }
        return true;
    }
    

    ===========================================================

    另外一边有一个接收线程ReceiveThread run(),一直在收dhcp server的的数据包。

    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 {
    
                    //读取DHCP服务发出的ACK确认包
                    length = Os.read(mPacketSock 【FileDescriptor】, mPacket, 0, mPacket.length); 
                    DhcpPacket packet = null;
                    packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
                    if (DBG) Log.d(TAG, "Received packet: " + packet);
    
                    // 发送接收到消息 CMD_RECEIVED_PACKET  此时处理的状态是 DhcpRequestingState
                    sendMessage(CMD_RECEIVED_PACKET, packet); 
                } catch (IOException|ErrnoException e) {
                    if (!mStopped) {
                        Log.e(TAG, "Read error", e);
                        DhcpErrorEvent.logReceiveError(mIfaceName);
                    }
                } catch (DhcpPacket.ParseException e) {
                    Log.e(TAG, "Can't parse packet: " + e.getMessage());
                    if (PACKET_DBG) {
                        Log.d(TAG, HexDump.dumpHexString(mPacket, 0, length));
                    }
                    DhcpErrorEvent.logParseError(mIfaceName, e.errorCode);
                }
            }
            if (DBG) Log.d(TAG, "Receive thread stopped");
        }
    

    }

    // 处理消息 CMD_RECEIVED_PACKET 对应的数据包是 ACK数据包 UDP
    class DhcpRequestingState extends PacketRetransmittingState {

            public boolean processMessage(Message message) {
            super.processMessage(message);
            switch (message.what) {
                case CMD_RECEIVED_PACKET:
                    receivePacket((DhcpPacket) message.obj);  //DhcpRequestingState处理接收到的数据包
                    return HANDLED;
                default:
                    return NOT_HANDLED;
            }
        }
    
    
            protected void receivePacket(DhcpPacket packet) {
            if (!isValidPacket(packet)) return;
            if ((packet instanceof DhcpAckPacket)) {
                DhcpResults results = packet.toDhcpResults();
                if (results != null) {
                    setDhcpLeaseExpiry(packet);
    //这里会调用notifySuccess,发送CMD去通知IpManager说,IP拿到了 分析 acceptDhcpResults
                    acceptDhcpResults(results, "Confirmed");  // 分叉
    
    //进入新的状态 ConfiguringInterfaceState enter方法  
                    transitionTo(mConfiguringInterfaceState); 
                }
            } else if (packet instanceof DhcpNakPacket) { // 收到的是NACK 解决数据包的话
                // TODO: Wait a while before returning into INIT state.
                Log.d(TAG, "Received NAK, returning to INIT");
                mOffer = null;
                transitionTo(mDhcpInitState);
            }
        }
    

    }

    //这里会调用notifySuccess,发送CMD去通知IpManager说,IP拿到了 分析 acceptDhcpResults
    acceptDhcpResults(results, “Confirmed”);

        private void acceptDhcpResults(DhcpResults results, String msg) {
        mDhcpLease = results;
        mOffer = null;
        Log.d(TAG, msg + " lease: " + mDhcpLease);
        notifySuccess(); // 通知成功拿到IP地址了
    }
    
    private void notifySuccess() {
        mController【StateMachine】.sendMessage( CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, new DhcpResults(mDhcpLease));
    }
    
    
    
      class StartedState extends State {
    
        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                  case DhcpClient.CMD_POST_DHCP_ACTION:
                    stopDhcpAction();
    
                    switch (msg.arg1) {
                        case DhcpClient.DHCP_SUCCESS:
                            handleIPv4Success((DhcpResults) msg.obj); // 处理 handleIPv4Success IPV4地址
                            break;
                        case DhcpClient.DHCP_FAILURE:
                            handleIPv4Failure();
                            break;
                        default:
                            Log.e(mTag, "Unknown CMD_POST_DHCP_ACTION status:" + msg.arg1);
                    }
                    break;
      }
      }
    

    }

    private void handleIPv4Success(DhcpResults dhcpResults) {
        mDhcpResults = new DhcpResults(dhcpResults);
        final LinkProperties newLp = assembleLinkProperties();
        final ProvisioningChange delta = setLinkProperties(newLp);
    
        if (VDBG) {
            Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
        }
        mCallback.onNewDhcpResults(dhcpResults);
        dispatchCallback(delta, newLp);   //这里分发dhcpResults 把IPv4地址发出去
    }
    
    
    
    
        private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
        switch (delta) {
            case GAINED_PROVISIONING:
                if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
                recordMetric(IpManagerEvent.PROVISIONING_OK);
                mCallback.onProvisioningSuccess(newLp); // 回调
                break;
    
        }
    }
    
    dispatchCallback 的结果最后会作用到 WaitForProvisioningCallback 
    
    
    public static class WaitForProvisioningCallback extends Callback {
        private LinkProperties mCallbackLinkProperties;
    
        public LinkProperties waitForProvisioning() {
            synchronized (this) {
                try {
                    wait();
                } catch (InterruptedException e) {}
                return mCallbackLinkProperties;
            }
        }
    
        @Override
        public void onProvisioningSuccess(LinkProperties newLp) {   // 回调
            synchronized (this) {
                mCallbackLinkProperties = newLp;  // 把当前的IPv4保存起来
                notify();
            }
        }
    
        @Override
        public void onProvisioningFailure(LinkProperties newLp) {
            synchronized (this) {
                mCallbackLinkProperties = null;
                notify();
            }
        }
    

    } acceptDhcpResults 分析到此为止 开始分析新的状态

    //进入新的状态 ConfiguringInterfaceState enter方法
    ConfiguringInterfaceState

    class ConfiguringInterfaceState extends LoggingState {
        @Override
        public void enter() {
            super.enter();
    

    // 发送消息 CMD_CONFIGURE_LINKADDRESS 被StartedState 处理
    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;
            }
        }
    }
    
    
    ========================
    
        class StartedState extends State {
        @Override
        public boolean processMessage(Message msg) {
            switch (msg.what) {
                       case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
                    final LinkAddress ipAddress = (LinkAddress) msg.obj;
                    if (setIPv4Address(ipAddress)) {  // 设置 IPv4地址
                        mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
                    } else {
                        Log.e(mTag, "Failed to set IPv4 address!");
                        dispatchCallback(ProvisioningChange.LOST_PROVISIONING,
                                new LinkProperties(mLinkProperties));
                        transitionTo(mStoppingState);
                    }
                    break;
        }
        }
    

    =========================================
    // 回到开头的 setIPv4Address 至此获得了IP地址
    private boolean setIPv4Address(LinkAddress address) {
    final InterfaceConfiguration ifcg = new InterfaceConfiguration();
    ifcg.setLinkAddress(address);
    try {
    final INetworkManagementService mNwService.setInterfaceConfig(mInterfaceName, ifcg);
    if (VDBG) Log.d(mTag, “IPv4 configuration succeeded”);
    } catch (IllegalStateException | RemoteException e) {
    Log.e(mTag, “IPv4 configuration failed: “, e);
    return false;
    }
    return true;
    }

    NetworkManagementService.java
    @Override
    public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
        LinkAddress linkAddr = cfg.getLinkAddress();
        if (linkAddr == null || linkAddr.getAddress() == null) {
            throw new IllegalStateException("Null LinkAddress given");
        }
    
        final Command cmd = new Command("interface", "setcfg", iface,
                linkAddr.getAddress().getHostAddress(),
                linkAddr.getPrefixLength());
        for (String flag : cfg.getFlags()) {
            cmd.appendArg(flag);
        }
    
        try {
            mConnector.execute(cmd);  //  执行命令
        } catch (NativeDaemonConnectorException e) {
            throw e.rethrowAsParcelableException();
        }
    }
    

    http://m.blog.csdn.net/xiaoxiangyuhai/article/details/75219357

    展开全文
  • DHCP方式获取IP地址过程 终端以广播形式向网络内广播DISCOVER发现报文,报文中携带相关参数列表。 网络内服务器,从可用地址池中分配一个ip地址给客户机,向这个地址广播offer报文,报文中携带终端请求参数。 ...

    网络知识点

    DHCP方式获取IP地址过程

    • 终端以广播的形式向网络内广播DISCOVER发现报文,报文中携带相关参数列表。
    • 网络内的服务器,从可用地址池中分配一个ip地址给客户机,向这个地址广播offer报文,报文中携带终端请求参数。
    • 终端从响应offer的服务器中选择一个,并在request报文中携带选择的服务器的ip和服务器分配给自己的ip地址。并进行广播,把终端选择的地址和服务器通知网络内所有主机服务器。
    • 网络内的服务器收到终端的request请求后,如果是终端选择的服务器,根据的是上面request报文中是否携带自身的信息,比如IP地址。则发送一个广播ACK,确定分配地址等信息;如果不是终端选择的服务,则不响应,并收回分配的地址到可用地址池中。
    • 终端收到ack后就把ip地址等信息跟网卡绑定。
    展开全文
  • 数据在网络上传输时候要自顶向下逐层封装,典型的DHCP过程是这样:1:客户机向服务器发送DHCP_DISCOVER报文,申请IP。2:服务器向客户机返会DHCP_OFFER报文,指定一个将要分配的IP。3:客户机向服务器发送DHCP_...

    DHCP是应用层协议,UDP是传输层协议,IP是网络层协议,以太网是链路层协议。数据在网络上传输的时候要自顶向下逐层封装的,典型的DHCP过程是这样的:
    1:客户机向服务器发送DHCP_DISCOVER报文,申请IP。
    2:服务器向客户机返会DHCP_OFFER报文,指定一个将要分配的IP。
    3:客户机向服务器发送DHCP_REQUEST报文,请求这个IP。
    4:服务器PING几次(一般是3次)这个IP,如果没得到响应的话,就说明这个IP现在空闲,可以分配给客户机,
    所以向客户机发送DHCP_ACK报文,确认可以分配。如果得到相应,则发送DHCP_NAK报文,拒绝分配。
    5:如果客户端收到DHCP_ACK,责发送一次免费ARP,判断这个IP是否已经被用。没有被用的话则绑定这个服务
    器分配来的IP,否则向服务器发送DHCP_DECLINE报文,拒绝这次分配。并重新执行第一步。
    如果收到的是DHCP_NAK,则直接重新执行第一步。
    客户机申请IP之前确实没有IP,DHCP_DISCOVER报文都是以广播形式发送的,IP头里的目的地址是255.255.255.255,源地址是0.0.0.0。链路层目的地址是FF-FF-FF-FF-FF-FF,源地址是自己的mac地址。这样服务器收到客户端发来的DISCOVER报文之后,会根据源mac地址向客户机发送单播的DHCP_OFFER报文。
    在看一下需要抓取的DHCP报文:
    客户发出的IP租用请求报文
    DHCP客户机初始化TCP/IP,通过UDP端口67向网络中发送一个DHCPDISCOVER广播包,请求租用IP地址。该 广播包中的源IP地址为0.0.0.0,目标IP地址为255.255.255.255;包中还包含客户机的MAC地址和计算机名。

    DHCP回应的IP租用提供报文
    任何接收到DHCPDISCOVER广播包并且能够提供IP地址的DHCP服务器,都会通过UDP端口68给客户机回应一个DHCPOFFER广播包,提供一个IP地址。该广播包的源IP地址为DCHP服务器IP,目标IP地址为255.255.255.255;包中还包含提供的IP地址、子网掩码及租期等信息。

    客户选择IP租用报文
    客户机从不止一台DHCP服务器接收到提供之后,会选择第一个收到的DHCPOFFER包,并向网络中广播一个 DHCPREQUEST消息包,表明自己已经接受了一个DHCP服务器提供的IP地址。该广播包中包含所接受的IP地址和服务器的IP地址。 所有其他的DHCP服务器撤消它们的提供以便将IP地址提供给下一次IP租用请求。

    DHCP服务器发出IP租用确认报文
    被客户机选择的DHCP服务器在收到DHCPREQUEST广播后,会广播返回给客户机一个DHCPACK消息包,表明已经接受客户机的选择,并将这一IP地址的合法租用以及其他的配置信息都放入该广播包发给客户机。
    客户配置成功后发出的公告报文
    客户机在收到DHCPACK包,会使用该广播包中的信息来配置自己的TCP/IP,则租用过程完成,客户机可以在网络中通信。
    至此一个客户获取IP的DHCP服务过程基本结束,不过客户获取的IP一般是用租期,到期前需要更新租期,这个过程是通过租用更新数据包来完成的。

    客户IP租用更新报文
    (1)在当前租期已过去50%时,DHCP客户机直接向为其提供IP地址的DHCP服务器发送DHCPREQUEST消息包。如果客户机接收到该服务器回应的DHCPACK消息包,客户机就根据包中所提供的新的租期以及其它已经更新的TCP/IP参数,更新自己的配置,IP租用更新完成。如果没收到该服务器的回复,则客户机继续使用现有的
    IP地址,因为当前租期还有50%。
    (2)如果在租期过去50%时未能成功更新,则客户机将在当前租期过去87.5%时再次向为其提供IP地址的DHCP联系。如果联系不成功,则重新开始IP租用过程。
    (3)如果DHCP客户机重新启动时,它将尝试更新上次关机时拥有的IP租用。如果更新未能成功,客户机将尝试联系现有IP租用中列出的缺省网关。如果联系成功且租用尚未到期,客户机则认为自己仍然位于与它获得现有IP租用时相同的子网上(没有被移走)继续使用现有IP地址。 如果未能与缺省网关联系成功,客户机则认为自己已经被移到不同的子网上,将会开始新一轮的IP租用过程。

    DHCP客户机在发出IP租用请求的DHCPDISCOVER广播包后,将花费1秒钟的时间等待DHCP服务器的回应,如果1秒钟没有服务器的回应,它会将这一广播包重新广播四次(以2,4,8和16秒为间隔,加上1~1000毫秒之间随机长度的时间)。四次之后,如果仍未能收到服务器的回应,则运行Windows 2000的DHCP客户机将从169.254.0.0/16这个自动保留的私有IP地址(APIPA)中选用一个IP地址,而运行其他操作系统的DHCP客户机将无法获得IP地址。DHCP客户机仍然每隔5分钟重新广播一次,如果收到某个服务器的回应,则继续IP租用过程。
    对于DHCP在获取IP地址时的报文,简单的进行整理和解释,部分资料来源于网络,如有错误 欢迎指出!多谢

    转载于:https://www.cnblogs.com/zhnaghui/p/4354439.html

    展开全文
  • 上一篇文章主要说了一下知识点中的IP地址的考点,这一篇我打算说说DHCP获取IP地址过程中捕获的报文的这个考点,都是自己的理解,有错误欢迎指正。  DHCP是应用层协议,UDP是传输层协议,IP是网络层协议,以太网是...
  • 1.首先检查两个缓存:先检查浏览器是否有对应域名与ip地址的缓存,有的话解析结束;没有检查本地操作系统是否有对应域名和ip地址的缓存,有的话解析结束,没有进行下面正式的解析 2.主机向本地DNS服务器(LDNS)发起...
  • 而有的用户无法通过dhcp获取ip地址。 根据上述现象初步分析是dhcp服务器的问题,但是其他的网段却不存在此故障。由此我将问题所定在Lan的范围内。 根据sniffer的分析结果发现客户端在dhcp的过程中受到不明dhcp服务器...
  • DHCP分配IP地址过程描述

    万次阅读 多人点赞 2018-06-29 09:50:21
    原文:https://www.cnblogs.com/tlnshuju/p/7356311.html图解DHCP的4步租约过程 DHCP租约过程就是DHCP客户机动态获取IP地址的过程DHCP租约过程分为4步:①客户机请求IP(客户机发DHCPDISCOVER广播包);②server...
  • 由于在IP地址动态获取过程中采用广播方式发送报文,因此要求DHCP客户端和服务器位于同一个网段内。如果DHCP客户端和DHCP服务器位于不同的网段,则需要通过DHCP中继来中继转发DHCP报文。 通过DHCP中继完成动态配置...
  • DHCP动态获取IP过程抓包实验 1、实验拓扑 R1设置成DHCP服务器,G0/0/0和G0/0/1接口都开启DHCP功能; PC-1,PC-2,PC-3设置为动态获取地址;...DHCP获取ip地址是有四个过程,也就是DHCP Discover,DHCP offer
  • 实现lwip的DHCP自动获取ip地址

    千次阅读 2012-03-04 15:25:49
    实现lwip的DHCP自动获取ip地址 [转载]  上个星期,在LM3S系列移植lwip上,实现了自动获取ip功能。  实现过程是:1)在opt.h上使能#define LWIP_ARP 1和#define LWIP_DHCP 1;2)在lwipopts.h上使能#define ...
  • DHCP自动获取ip地址

    2020-05-29 22:48:02
    二:实验过程 2.1:配置设备 在ensp中,将PC机和路由器及交换机按图示做成拓扑图。 2.2 配置SW1交换机 [SW1]vlan batch 10 20** **[SW1]int e0/0/1** **[SW1-Ethernet0/0/1]p l a** **[SW1-Ethernet0/0/1]p d v ...
  • openwrt中的dnsmasq包含有dhcp server和dns的...dnsmasq脚本启动时会先生成dnsmasq的配置文件,当网络变化频繁时,可能会同时出现多个生成配置文件的过程,写同一个配置文件导致后面关于dhcp的配置丢失,从而导致dhcp
  • 文章目录一、了解DHCP1.1 DHCP的好处1.2 DHCP的分配方式1.3 客户端请求IP地址的过程1.4 DHCP地址分配的特点二、 Linux系统安装DHCP服务器实操2.1 DHCP服务器安装参数解析2.2 DHCP实验实验环境实验过程 一、了解DHCP ...
  • 经验案例:当配置为共享/静态WEP加密时,客户端无法从DHCP获取IP地址 一、问题描述 当配置为共享/静态WEP加密时,客户端无法从DHCP上获取IP地址。 二、过程分析 这种现象是正常,当使用共享式时,WX默认应该...
  • 动态 IP地址下放过程:1、主机发送DHCP discover包发现DHCP服务器2、DHCP服务器发送DHCP offer包发送IP地址通过逆向ARP将IP地址解析成MAC地址,进行广播,收到该MAC地址的回应证明IP地址已被占用,没有收到证明该IP...
  • 使用野火h750板子lwip例程,在实际测试过程中发现,如果在没有插入网线情况下,先启动程序,会出现无法通过DHCP获取ip的情况,调试发现程序一直在死循环获取ip地址,必须在插入网线情况下,程序才能成功获取...
  • DHCP获取过程

    千次阅读 2017-10-18 19:28:17
    当主机接入网络时,选取DHCP服务,从而从DHCP服务器获取IP。  客户端DHCP发现(DHCP Discovery)  客户端在网络内广播告知DHCP服务器需要获取一个IP地址,因为此时没有IP,则客户端以0.0.0.0为源地址进行...
  • 在上一篇博客中计算机之间是如何进行通信?;详解三次握手和四次挥手,我们对下面这个图片中参数进行了一些解释。...电脑操作系统安装了TCP/IP协议栈,这个协议栈包括了一个DHCP客户端进程,这个客户端会广播...
  • 作者:车小胖 ... NAT和DHCP 区别是什么? TCP/IP协议之所以最终...如果没有DHCP,TCP/IP协议在初始化过程中,需要用户提供IP地址、网络掩码、默认网关、DNS服务器,以此来完成各个协议模块初始化工作,对于广大...
  • dhcp 获取ip过程; 1、先由客户端发送discover消息,这个消息属于广播,网内的dhcp都会收到这个消息,一般在这个消息内增加option60选项 2、服务器如果能够提供给客户端一个ip地址,则会给客户端发送一个offer消息...
  • Linux系统DHCP原理与配置DHCP工作原理DHCP的服务使用DHCP的好处:DHCP的分配方式DHCP的租约过程使用DHCP动态配置主机地址安装DHCP服务器 DHCP工作原理 DHCP的服务 DHCP (Dynamic HostConfiguration Protocol,动态...
  • 单写一篇文章记录这些问题,因为有问题实在是困扰了我太久太久了,终于解决了!...2.DHCP获取IP地址一致失败、获取超时等问题,只能使用静态IP地址 解决方法:DHCP,又名动态主机配置服务,它作.

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 407
精华内容 162
关键字:

dhcp获取ip地址的过程