`
Donald_Draper
  • 浏览: 979782 次
社区版块
存档分类
最新评论

DatagramChannelImpl 解析一(初始化)

    博客分类:
  • NIO
nio 
阅读更多
Channel接口定义:http://donald-draper.iteye.com/blog/2369111
AbstractInterruptibleChannel接口定义:http://donald-draper.iteye.com/blog/2369238
SelectableChannel接口定义:http://donald-draper.iteye.com/blog/2369317
SelectionKey定义:http://donald-draper.iteye.com/blog/2369499
SelectorProvider定义:http://donald-draper.iteye.com/blog/2369615
AbstractSelectableChannel定义:http://donald-draper.iteye.com/blog/2369742
NetworkChannel接口定义:http://donald-draper.iteye.com/blog/2369773
Selector定义:http://donald-draper.iteye.com/blog/2370015
AbstractSelector定义:http://donald-draper.iteye.com/blog/2370138
SelectorImpl分析 :http://donald-draper.iteye.com/blog/2370519
WindowsSelectorImpl解析一(FdMap,PollArrayWrapper):
http://donald-draper.iteye.com/blog/2370811
WindowsSelectorImpl解析二(选择操作,通道注册,通道反注册,选择器关闭等):
http://donald-draper.iteye.com/blog/2370862
MembershipKey定义:http://donald-draper.iteye.com/blog/2372947
MulticastChanne接口定义:http://donald-draper.iteye.com/blog/2373009
MembershipKeyImpl 简介:http://donald-draper.iteye.com/blog/2373066
DatagramChannel定义:http://donald-draper.iteye.com/blog/2373046
引言:
    先来回顾一下报文通道相关的概念。MembershipKeyImpl内部有一个多播关系key关联的多播通道和多播分组地址,及多播报文源地址,及一个地址阻塞集。MembershipKeyImpl主要操作为drop关系key,直接委托个多播通道drop方法;block地址,首先判断多播关系key中的阻塞Set中是否包含对应的地址,有,则直接返回,否则委托给DatagramChannelImpl的block方法,完成实际的阻塞工作,然后添加地址的多播关系key阻塞set;unblock,首先判断多播关系key中的阻塞Set中是否包含对应的地址,无,则直接返回,有则委托给DatagramChannelImpl的unblock方法,完成实际的的解除阻塞工作,并从多播关系key中的阻塞Set移除对应的地址。
MulticastChanne定义一个通道加入多播组的接口方法join。DatagramChannel的send和receive方法是不需要进行网络连接的,而read和write方法有与不能接受和返回socket地址。
通道必须建立连接。
    在上面一篇DatagramChannel文章中我们看了一下报文通道抽象类的方法的定义,
今天这篇我们来看报文通道的具体实现我们需要关注的方法为drop,block,unblock,join,
send,receive,read和write。
我们从DatagramChannel的open方法看起
//DatagramChannel
  public static DatagramChannel open() throws IOException {
        return SelectorProvider.provider().openDatagramChannel();
    }

SelectorProvider.provider()这个过程我们就不详说了实际是加载系统默认的SelectorProvider实例,则个我们在SelectorProvider定义有提过,简单看一下:
//SelectorProviderImpl
public abstract class SelectorProviderImpl extends SelectorProvider
{
    public DatagramChannel openDatagramChannel()
        throws IOException
    {
        return new DatagramChannelImpl(this);
    }

    public DatagramChannel openDatagramChannel(ProtocolFamily protocolfamily)
        throws IOException
    {
        return new DatagramChannelImpl(this, protocolfamily);
    }
}

从上面可以看出报文通道的具体实现为DatagramChannelImpl,下面来看DatagramChannelImpl
//DatagramChannelImpl
package sun.nio.ch;
import java.io.FileDescriptor;
import java.io.IOException;
import java.net.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.channels.spi.SelectorProvider;
import java.util.*;
import sun.net.ResourceManager;

// Referenced classes of package sun.nio.ch:
//            DatagramDispatcher, DatagramSocketAdaptor, DirectBuffer, IOStatus, 
//            IOUtil, MembershipKeyImpl, MembershipRegistry, NativeDispatcher, 
//            NativeThread, Net, SelChImpl, SelectionKeyImpl, 
//            SelectorImpl, Util

class DatagramChannelImpl extends DatagramChannel
    implements SelChImpl
{
   private static NativeDispatcher nd = new DatagramDispatcher();//报文分发器
    private final FileDescriptor fd;//报文通道的文件描述
    private final int fdVal;//文件描述值
    private final ProtocolFamily family;//网络协议
    private volatile long readerThread;//读线程
    private volatile long writerThread;//写线程
    private InetAddress cachedSenderInetAddress;//缓存的发送者InetAddress
    private int cachedSenderPort;//缓存的发送者port
    private final Object readLock;//读锁
    private final Object writeLock;//写锁
    private final Object stateLock;//状态锁
    private static final int ST_UNINITIALIZED = -1;//未初始化
    private static final int ST_UNCONNECTED = 0;//未连接
    private static final int ST_CONNECTED = 1;//已连接
    private static final int ST_KILLED = 2;//关闭
    private int state;//状态
    private SocketAddress localAddress;//本地SocketAddress
    private SocketAddress remoteAddress;//远端SocketAddress
    private DatagramSocket socket;//报文Sockeet
    private MembershipRegistry registry;//多播关系注册器
    private SocketAddress sender;//报文发送者的SocketAddress
    static final boolean $assertionsDisabled = !sun/nio/ch/DatagramChannelImpl.desiredAssertionStatus();

    static 
    {
        //加载net和nio资源库
        Util.load();
        initIDs();
    }
}
//初始化ID
private static native void initIDs();

我们来看一下多播关系注册器
//MembershipRegistry
class MembershipRegistry
{
    private Map groups;//HashMap<InetAddress,LinkedList<MembershipKeyImpl>>
    MembershipRegistry()
    {
        groups = null;
    }
    //注册多播关系到注册器
     void add(MembershipKeyImpl membershipkeyimpl)
    {
        InetAddress inetaddress = membershipkeyimpl.group();
        Object obj;
	//从当前注册器获取多播地址对应的obj-LinkedList<membershipkeyimpl>
        if(groups == null)
        {
            groups = new HashMap();
            obj = null;
        } else
        {
            obj = (List)groups.get(inetaddress);
        }
	//如果obj为null,则创建LinkedList,并将多播地址与obj的映射关系添加到注册器的Group中
        if(obj == null)
        {
            obj = new LinkedList();
            groups.put(inetaddress, obj);
        }
	//添加多播关系key到多播地址对应的多播关系key集合中
        ((List) (obj)).add(membershipkeyimpl);
    }
    //移除多播关系key
     void remove(MembershipKeyImpl membershipkeyimpl)
    {
        //根据多播关系key的多播组地址,获取对应的多播关系key集合
        InetAddress inetaddress = membershipkeyimpl.group();
        List list = (List)groups.get(inetaddress);
        if(list != null)
        {
            Iterator iterator = list.iterator();
	    //遍历多播关系key集合,找多播关系key,则移除
            do
            {
                if(!iterator.hasNext())
                    break;
                if(iterator.next() != membershipkeyimpl)
                    continue;
                iterator.remove();
                break;
            } while(true);
	    //多播关系key集合为空,则从注册器中移除对应的多播组地址映射
            if(list.isEmpty())
                groups.remove(inetaddress);
        }
    }
    //使注册器中的所有多播组中的多播关系key无效
      void invalidateAll()
    {
        if(groups != null)
        {
	    //遍历多播组
            for(Iterator iterator = groups.keySet().iterator(); iterator.hasNext();)
            {
                InetAddress inetaddress = (InetAddress)iterator.next();
                Iterator iterator1 = ((List)groups.get(inetaddress)).iterator();
		//遍历多播关系key集合
                while(iterator1.hasNext()) 
                {
                    MembershipKeyImpl membershipkeyimpl = (MembershipKeyImpl)iterator1.next();
		    //使多播关系key无效
                    membershipkeyimpl.invalidate();
                }
            }

        }
    }
    //检查多播关系注册器中是否有,多播组地址为inetaddress,源地址为inetaddress1,网络接口为networkinterface的MembershipKey
     MembershipKey checkMembership(InetAddress inetaddress, NetworkInterface networkinterface, InetAddress inetaddress1)
    {
label0:
        {
            if(groups == null)
                break label0;
            //获取多播组对应的多播关系集合
            List list = (List)groups.get(inetaddress);
            if(list == null)
                break label0;
            Iterator iterator = list.iterator();
            MembershipKeyImpl membershipkeyimpl;
	    //遍历多播关系集合中源地址为inetaddress1的多播关系key
            do
            {
	        //遍历源地址为inetaddress1的多播关系key,找到网络接口为networkinterface的MembershipKeyImpl
                do
                {
                    if(!iterator.hasNext())
                        break label0;
                    membershipkeyimpl = (MembershipKeyImpl)iterator.next();
                } while(!membershipkeyimpl.networkInterface().equals(networkinterface));
                if(inetaddress1 == null)
                    if(membershipkeyimpl.sourceAddress() == null)
                        return membershipkeyimpl;
                    else
                        throw new IllegalStateException("Already a member to receive all packets");
                if(membershipkeyimpl.sourceAddress() == null)
                    throw new IllegalStateException("Already have source-specific membership");
            } while(!inetaddress1.equals(membershipkeyimpl.sourceAddress()));
            return membershipkeyimpl;
        }
        return null;
    }
}

从上可以看出多播关系注册器MembershipRegistry主要是通过一个Map-HashMap<InetAddress,LinkedList<MembershipKeyImpl>>来管理多播组和多播组成员关系key的映射(关系)。
再来看构造
//根据选择器提供者创建报文通道
public DatagramChannelImpl(SelectorProvider selectorprovider)
        throws IOException
    {
        super(selectorprovider);
        readerThread = 0L;
        writerThread = 0L;
        readLock = new Object();
        writeLock = new Object();
        stateLock = new Object();
        state = -1;
	//更新socket计数器,自增1
        ResourceManager.beforeUdpCreate();
        try
        {
	    //确定网络协议
            family = Net.isIPv6Available() ? ((ProtocolFamily) (StandardProtocolFamily.INET6)) : ((ProtocolFamily) (StandardProtocolFamily.INET));
            //获取文件描述
	    fd = Net.socket(family, false);
	    //获取文件描述的id
            fdVal = IOUtil.fdVal(fd);
            state = 0;
        }
        catch(IOException ioexception)
        {
	    //更新socket计数器,自减1
            ResourceManager.afterUdpClose();
            throw ioexception;
        }
    }

这个构造我们需要关注的是
//更新socket计数器,自增1
ResourceManager.beforeUdpCreate();
//更新socket计数器,自减1
ResourceManager.afterUdpClose();

我们来看一下ResourceManager
//ResourceManager
package sun.net;

import java.net.SocketException;
import java.security.AccessController;
import java.util.concurrent.atomic.AtomicInteger;
import sun.security.action.GetPropertyAction;

public class ResourceManager
{
    private static final int DEFAULT_MAX_SOCKETS = 25;//默认最大socket数量
    private static final int maxSockets;//最大socket数量
    private static final AtomicInteger numSockets = new AtomicInteger(0);//当前socket数量

    static 
    {
        //获取虚拟机最大报文socket配置
        String s = (String)AccessController.doPrivileged(new GetPropertyAction("sun.net.maxDatagramSockets"));
        int i = 25;
        try
        {
            if(s != null)
                i = Integer.parseInt(s);
        }
        catch(NumberFormatException numberformatexception) { }
        maxSockets = i;
    } 
    public ResourceManager()
    {
    }

    public static void beforeUdpCreate()
        throws SocketException
    {
        //如果系统安全检查器不为空,同时当前socket数量自增后大于最大Socket数量,则抛出SocketException
        if(System.getSecurityManager() != null && numSockets.incrementAndGet() > maxSockets)
        {
            numSockets.decrementAndGet();
            throw new SocketException("maximum number of DatagramSockets reached");
        } else
        {
            return;
        }
    }
    //更新当前报文socket计数器,自减1
    public static void afterUdpClose()
    {
        if(System.getSecurityManager() != null)
            numSockets.decrementAndGet();
    }
}

我们再看其他两个构造方法:
  
 public DatagramChannelImpl(SelectorProvider selectorprovider, ProtocolFamily protocolfamily)
        throws IOException
    {
        super(selectorprovider);
        readerThread = 0L;
        writerThread = 0L;
        readLock = new Object();
        writeLock = new Object();
        stateLock = new Object();
        state = -1;
        if(protocolfamily != StandardProtocolFamily.INET && protocolfamily != StandardProtocolFamily.INET6)
            if(protocolfamily == null)
                throw new NullPointerException("'family' is null");
            else
                throw new UnsupportedOperationException("Protocol family not supported");
        if(protocolfamily == StandardProtocolFamily.INET6 && !Net.isIPv6Available())
        {
            throw new UnsupportedOperationException("IPv6 not available");
        } else
        {
            family = protocolfamily;
            fd = Net.socket(protocolfamily, false);
            fdVal = IOUtil.fdVal(fd);
            state = 0;
            return;
        }
    }

    public DatagramChannelImpl(SelectorProvider selectorprovider, FileDescriptor filedescriptor)
        throws IOException
    {
        super(selectorprovider);
        readerThread = 0L;
        writerThread = 0L;
        readLock = new Object();
        writeLock = new Object();
        stateLock = new Object();
        state = -1;
        family = Net.isIPv6Available() ? ((ProtocolFamily) (StandardProtocolFamily.INET6)) : ((ProtocolFamily) (StandardProtocolFamily.INET));
        fd = filedescriptor;
        fdVal = IOUtil.fdVal(filedescriptor);
        state = 0;
	//初始化报文socket本地地址
        localAddress = Net.localAddress(filedescriptor);
    }

从上面三个构造方法可以看出,主要是初始化读写线程,及读写锁和状态锁,初始化网络协议family,及报文通道描述符和文件描述id。
总结:
DatagramChannelImpl主要成员有报文socket分发器,这个与SocketChannleImpl中的socket分发器原理基本相同,报文socket分发器可以理解为报文通道的静态代理;网络协议family表示当前报文通道的网络协议family;多播关系注册器MembershipRegistry,
主要是通过一个Map-HashMap<InetAddress,LinkedList<MembershipKeyImpl>>来管理多播组和多播组成员关系key的映射(关系);通道本地读写线程记录器,及读写锁控制通道读写,一个状态锁,当通道状态改变时,需要获取状态锁。DatagramChannelImpl构造方法,主要是初始化读写线程,及读写锁和状态锁,初始化网络协议family,及报文通道描述符和文件描述id。DatagramChannelImpl(SelectorProvider selectorprovider)与其他两个不同的是构造时更新当前报文socket的数量。

DatagramChannelImpl 解析二(报文发送与接收):http://donald-draper.iteye.com/blog/2373281
0
1
分享到:
评论

相关推荐

    vpp初始化代码解析.pdf

    - **堆内存初始化**:主线程申请堆内存,使用clib_mem_init_thread_safe初始化一个内存区域,然后创建mspace结构来管理内存,并通过clib_mem_set_heap将其设置为全局变量clib_per_cpu_mheap的一部分。 - **vlib_...

    SIN初始化_混沌初始化_matlab_混沌映射_种群初始化_sin映射初始化粒子群_

    描述中提到的“混沌映射sin映射初始化种群”是指在PSO算法的初始阶段,通过SIN映射生成一组随机但分布均匀的粒子位置。与传统的随机数生成相比,混沌映射生成的序列具有更好的遍历性和无规律性,可以避免粒子群陷入...

    PCI设备BAR空间的初始化

    初始化BAR寄存器是建立这种映射关系的第一步。 3. **资源分配的必要条件**:通过初始化BAR寄存器,系统软件可以为每个PCI设备分配特定的地址空间,这是实现资源共享和避免地址冲突的重要步骤。 #### 三、初始化...

    C++全局变量初始化的一点总结

    C++中的全局变量初始化是一个重要的话题,涉及到程序的正确性和可预测性。全局变量,即具有静态存储期的变量,其生命周期始于程序开始,终于程序结束。本文将深入探讨全局变量初始化的时机、方式以及一些处理特殊...

    深入解析 ORACLE 数据库的初始化pdf

    《深入解析 ORACLE 数据库的初始化》是由知名数据库专家盖国强编著的一本专业书籍,专注于探讨 Oracle 数据库在启动、配置和管理过程中的各项技术细节。这本书以丰富的实例和深入的理论相结合,旨在帮助读者全面理解...

    java代码的初始化顺序demo

    这个过程包括加载、验证、准备、解析和初始化五个步骤。这里我们主要关注初始化阶段,也就是执行静态初始化块(如果有的话)。 2. **静态初始化**: 静态初始化块(也称为静态初始化器)是在类被加载并初始化时...

    Velocity初始化过程解析

    2. 初始化一个VelocityContext对象,并向其中添加键值对,这些值将在模板中用于替换占位符。 3. 使用VelocityEngine的getTemplate方法加载模板,并调用merge方法,将Context中的值与Template结合,生成最终输出。 ...

    新唐NUC977开发板uboot代码解析3-SD卡初始化及mmc相关命令

    ### 新唐NUC977开发板U-Boot代码解析:SD卡初始化及MMC相关命令 #### 一、概述 本文旨在深入分析新唐NUC977开发板上U-Boot启动过程中SD卡初始化的过程及其相关的MMC命令实现。SD卡在嵌入式系统中作为存储介质被...

    PMON 设备初始化代码分析,非常详细的资料说明

    PMON 设备初始化代码是 PMON 设备的核心组件之一,它负责初始化 PMON 设备的各个组件,包括 PCI 设备、内存、时钟频率、异常处理等。下面我们将对 PMON 设备初始化代码进行详细的分析。 1. PCI 设备初始化 PMON ...

    液晶显示器单片机初始化程序

    本文将深入解析标题、描述、标签以及部分内容中提及的知识点,帮助读者理解单片机控制的液晶显示器初始化过程。 ### 液晶显示器初始化的重要性 在启动或复位后,液晶显示器处于默认状态,其内部寄存器的配置可能不...

    组态王设备初始化失败安装可用

    在工业控制系统中,"设备初始化失败"是一个常见的问题,这可能由多种原因引起,例如驱动程序不兼容、系统设置错误、硬件故障或是缺少必要的组件。在本案例中,提到的“组态王设备初始化失败安装可用”指的是,当遇到...

    疯狂内核之——内核初始化

    - **BIOS时代**: BIOS(基本输入/输出系统)是一种位于ROM中的固件程序,用于在启动过程中初始化、测试系统硬件功能,并加载操作系统引导程序。BIOS通过一系列自检程序来验证硬件的状态,并提供最基本的硬件控制。 -...

    解决数码视讯Q5使用USB_Burning_Tool刷机时 提示 初始化DDR/读取初始化结果/USB错误

    解决数码视讯Q5使用USB_Burning_Tool刷机时 提示 初始化DDR/读取初始化结果/USB错误

    android2.3初始化过程

    Android 2.3的启动过程是一个复杂但有序的过程,涉及到日志系统初始化、配置文件解析、设备初始化、属性服务的启动等多个环节。通过对这些过程的深入了解,我们可以更好地理解Android系统是如何从底层硬件到顶层应用...

    vue初始化模板vue初始化模板

    vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue初始化模板vue...

    OV7725初始化代码

    本文将详细解析一段典型的 OV7725 初始化代码,帮助读者理解其工作原理及配置方法。 #### 二、OV7725简介 OV7725 图像传感器由OmniVision Technologies公司设计生产,具有以下特点: - 分辨率:最高支持 VGA (640 x...

    PIC18系列单片机各个功能模块的通用初始化代码

    标题"PIC18系列单片机各个功能模块的通用初始化代码"揭示了这个压缩包文件包含的是针对PIC18系列单片机的各种功能模块的初始化程序。PIC18系列是Microchip公司生产的8位微控制器,广泛应用于嵌入式系统设计。初始化...

    Java初始化块Java初始化块.doc

    Java 初始化块,也称为构造代码块,是Java编程语言中的一种特性,用于在对象创建时执行特定的初始化操作。初始化块在类的定义中以 `{}` 包裹的一段代码,没有返回值,也不需要任何参数。根据是否带有 `static` ...

    液晶屏幕lcd spi 初始化 代码

    液晶屏幕LCD SPI初始化是嵌入式系统中常见的一项任务,特别是在Windows CE(WinCE)操作系统中,用于驱动显示设备。SPI(Serial Peripheral Interface)是一种串行通信协议,它允许单主机与一个或多个设备进行全双工...

    hertzbeat部署使用到的sql初始化及初始yml

    `hertzbeat`是一个可能涉及监控、报警和日志收集的系统,其部署过程中需要用到SQL初始化脚本和配置YML文件。以下是对这些关键知识点的详细阐述: 1. **SQL初始化**:在部署`hertzbeat`时,SQL初始化通常指的是创建...

Global site tag (gtag.js) - Google Analytics