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

SocketChannelImpl 解析四(关闭通道等)

    博客分类:
  • NIO
nio 
阅读更多
SocketChannelImpl 解析一(通道连接,发送数据):http://donald-draper.iteye.com/blog/2372364
SocketChannelImpl 解析二(发送数据后续):http://donald-draper.iteye.com/blog/2372548
SocketChannelImpl 解析三(接收数据):http://donald-draper.iteye.com/blog/2372590
引言:
上一篇文章,我们看了SocketChannelImpl接收数据相关方法,具体为:
    读输入流到buffer,首先同步读写,确保通道,输入流打开,通道连接建立,
清除原始读线程,获取新的本地读线程,委托IOUtil读输入流到buffer;IOUtil读输入流到buffer,首先确保buffer是可写的,否则抛出IllegalArgumentException,然后判断buffer是否为Direct类型,是则委托给readIntoNativeBuffer,否则通过Util从当前线程缓冲区获取一个临时的DirectByteBuffer,然后通过readIntoNativeBuffer读输入流数据到临时的DirectByteBuffer,这一个过程是通过SocketDispatcher的read方法实现,读写数据到DirectByteBuffer中后,将DirectByteBuffer中数据,写到原始buffer中,并将
DirectByteBuffer添加到添加临时DirectByteBuffer到当前线程的缓冲区,以便重用,因为重新DirectByteBuffer为直接操作物理内存,频繁分配物理内存,将耗费过多的资源。
    从输入流读取数据,写到ByteBuffer数组的read方法,首先同步写锁,确保通道,连接建立,输入流打开,委托给IOUtil,从输入流读取数据写到ByteBuffer数组中;IOUtil首先获取存放i个字节缓冲区的IOVecWrapper,遍历ByteBuffer数组m,将buffer添加到iovecwrapper的字节缓冲区数组中,如果ByteBuffer非Direct类型,委托Util从当前线程的缓冲区获取容量为j2临时DirectByteBuffer,并将ByteBuffer写到DirectByteBuffer,并将DirectByteBuffer添加到iovecwrapper的字节缓冲区(Shadow-Direct)数组中,将字节缓冲区的起始地址写到iovecwrapper,字节缓冲区的实际容量写到iovecwrapper;遍历iovecwrapper的字节缓冲区(Shadow-Direct)数组,将Shadow数组中的DirectByteBuffer通过Util添加到本地线程的缓存区中,并清除DirectByteBuffer在iovecwrapper的相应数组中的信息;最后通过
SocketDispatcher,从filedescriptor对应的输入流读取数据,写到iovecwrapper的缓冲区中。
今天SocketChannelImpl的其他方法
//是否正在连接
 public boolean isConnectionPending()
    {
        //同步状态锁
        Object obj = stateLock;
        JVM INSTR monitorenter ;//进入同步
        return state == 1;
        Exception exception;
        exception;
        throw exception;//有异常,则抛出
    }

//完成连接
public boolean finishConnect()
        throws IOException
    {
       //同步读写锁及状态锁
        Object obj = readLock;
        JVM INSTR monitorenter ;//进入同步
        Object obj1 = writeLock;
        JVM INSTR monitorenter ;
        Object obj2 = stateLock;
        JVM INSTR monitorenter ;
        if(!isOpen())//通道关闭,则抛出ClosedChannelException
            throw new ClosedChannelException();
        if(state == 2)
	    //如果已经建立连接,则返回true
            return true;
        if(state != 1)
	    //如果连接不处于正在建立状态,则抛出NoConnectionPendingException
            throw new NoConnectionPendingException();
       ...
        int i = 0;
        begin();//与end方法,协调记录中断器,处理连接中断
        boolean flag;
        synchronized(blockingLock())
        {
            synchronized(stateLock)
            {
                if(isOpen())
                    break MISSING_BLOCK_LABEL_206;
                flag = false;
            }
        }
        synchronized(stateLock)
        {
            readerThread = 0L;
            if(state == 3)
            {
                kill();
                i = 0;
            }
        }
        end(i > 0 || i == -2);
        if(!$assertionsDisabled && !IOStatus.check(i))
            throw new AssertionError();
        obj1;
        JVM INSTR monitorexit ;
        obj;
        JVM INSTR monitorexit ;
        return flag;
        readerThread = NativeThread.current();
        obj7;
        JVM INSTR monitorexit ;
	//检查连接
        if(!isBlocking())
            do
                i = checkConnect(fd, false, readyToConnect);
            while(i == -3 && isOpen());
        else
            do
                i = checkConnect(fd, true, readyToConnect);
            while(i == 0 || i == -3 && isOpen());
        obj4;
        ...
    }

在完成连接方法中,我们需要关注的这下面这段
//检查连接
if(!isBlocking())
    do
        i = checkConnect(fd, false, readyToConnect);
    while(i == -3 && isOpen());
else
    do
        i = checkConnect(fd, true, readyToConnect);
    while(i == 0 || i == -3 && isOpen());

上面这段之所以,在一个循环内检查连接,主要是为了,在完成连接的过程中,如果由于某种原因 被中断,当中断位消除时,继续完成连接。
 private static native int checkConnect(FileDescriptor filedescriptor, boolean flag, boolean flag1)
        throws IOException;

再来看其他方法,
//配置阻塞模式
 protected void implConfigureBlocking(boolean flag)
        throws IOException
    {
        IOUtil.configureBlocking(fd, flag);
    }

//IOUtil
 
 static native void configureBlocking(FileDescriptor filedescriptor, boolean flag)
        throws IOException;

//socket通道支持的配置选项
public final Set supportedOptions()
    {
        return DefaultOptionsHolder.defaultOptions;
    }
//DefaultOptionsHolder
private static class DefaultOptionsHolder
    {

        private static Set defaultOptions()
        {
            HashSet hashset = new HashSet(8);
            hashset.add(StandardSocketOptions.SO_SNDBUF);//发送缓冲区size
            hashset.add(StandardSocketOptions.SO_RCVBUF);//接收缓冲区size
            hashset.add(StandardSocketOptions.SO_KEEPALIVE);//
            hashset.add(StandardSocketOptions.SO_REUSEADDR);//地址重用
            hashset.add(StandardSocketOptions.SO_LINGER);//
            hashset.add(StandardSocketOptions.TCP_NODELAY);//TCP
            hashset.add(StandardSocketOptions.IP_TOS);
            hashset.add(ExtendedSocketOption.SO_OOBINLINE);
	    //返回不可修改的HashSet
            return Collections.unmodifiableSet(hashset);
        }
        static final Set defaultOptions = defaultOptions();
        private DefaultOptionsHolder()
        {
        }
    }

//StandardSocketOptions
/**
 * The size of the socket send buffer.发送缓冲区大小
 * @see Socket#setSendBufferSize
 */
public static final SocketOption<Integer> SO_SNDBUF =
    new StdSocketOption<Integer>("SO_SNDBUF", Integer.class);
 /**
 * The size of the socket receive buffer.接收缓存区大小
 * @see Socket#setReceiveBufferSize
 * @see ServerSocket#setReceiveBufferSize
 */
 public static final SocketOption<Integer> SO_RCVBUF =
     new StdSocketOption<Integer>("SO_RCVBUF", Integer.class);
/**
* Keep connection alive.连接是否保活
*/
public static final SocketOption<Boolean> SO_KEEPALIVE =
    new StdSocketOption<Boolean>("SO_KEEPALIVE", Boolean.class);
/**
 * Re-use address.地址重用
 * @see ServerSocket#setReuseAddress
 */
public static final SocketOption<Boolean> SO_REUSEADDR =
    new StdSocketOption<Boolean>("SO_REUSEADDR", Boolean.class);

/**
 * Linger on close if data is present.如果通道中有数据,延时关闭时间
 * @see Socket#setSoLinger
 */
public static final SocketOption<Integer> SO_LINGER =
    new StdSocketOption<Integer>("SO_LINGER", Integer.class);
 /**
 * Disable the Nagle algorithm.TCP无延时
 * @see Socket#setTcpNoDelay
 */
public static final SocketOption<Boolean> TCP_NODELAY =
    new StdSocketOption<Boolean>("TCP_NODELAY", Boolean.class);
//下面两个配置选择,我们以后碰到再说
/**
 * The Type of Service (ToS) octet in the Internet Protocol (IP) header.
 * @see DatagramSocket#setTrafficClass
 */
public static final SocketOption<Integer> IP_TOS =
    new StdSocketOption<Integer>("IP_TOS", Integer.class);

//ExtendedSocketOption
package sun.nio.ch;
import java.net.SocketOption;
class ExtendedSocketOption
{
    private ExtendedSocketOption()
    {
    }
    static final SocketOption SO_OOBINLINE = new SocketOption() {
        public String name()
        {
            return "SO_OOBINLINE";
        }
        public Class type()
        {
            return java/lang/Boolean;
        }
        public String toString()
        {
            return name();
        }
    };
}

//设置配置选项
public SocketChannel setOption(SocketOption socketoption, Object obj)
        throws IOException
    {
        if(socketoption == null)
            throw new NullPointerException();
	//非支持配置选项,则抛出UnsupportedOperationException
        if(!supportedOptions().contains(socketoption))
            throw new UnsupportedOperationException((new StringBuilder()).append("'").append(socketoption).append("' not supported").toString());
        //同步状态锁,进入同步
	Object obj1 = stateLock;
        JVM INSTR monitorenter ;
        if(!isOpen())
	    //通道关闭,则抛出ClosedChannelException
            throw new ClosedChannelException();
        if(socketoption != StandardSocketOptions.IP_TOS)
            break MISSING_BLOCK_LABEL_108;
        if(!Net.isIPv6Available())
	    //iPv6不可用,则通过Net设置配置项
            Net.setSocketOption(fd, StandardProtocolFamily.INET, socketoption, obj);
        return this;
        ...
        JVM INSTR monitorexit ;
        return;
        Exception exception;
        exception;
        throw exception;
    }

设置配置项,需要关注的是
if(!Net.isIPv6Available())
     //iPv6不可用,则通过Net设置配置项
     Net.setSocketOption(fd, StandardProtocolFamily.INET, socketoption, obj);

//Net
//检查IP6是否可用
 static boolean isIPv6Available()
    {
        if(!checkedIPv6)
        {
            isIPv6Available = isIPv6Available0();
            checkedIPv6 = true;
        }
        return isIPv6Available;
    }
static void setSocketOption(FileDescriptor filedescriptor, ProtocolFamily protocolfamily, SocketOption socketoption, Object obj)
        throws IOException
    {
        if(obj == null)
            throw new IllegalArgumentException("Invalid option value");
        Class class1 = socketoption.type();
	//非整形和布尔型,则抛出断言错误
        if(class1 != java/lang/Integer && class1 != java/lang/Boolean)
            throw new AssertionError("Should not reach here");
        if(socketoption == StandardSocketOptions.SO_RCVBUF || socketoption == StandardSocketOptions.SO_SNDBUF)
        {    //判断接收和发送缓冲区大小
            int i = ((Integer)obj).intValue();
            if(i < 0)
                throw new IllegalArgumentException("Invalid send/receive buffer size");
        }
	//缓冲区有数据,延迟关闭socket的的时间
        if(socketoption == StandardSocketOptions.SO_LINGER)
        {
            int j = ((Integer)obj).intValue();
            if(j < 0)
                obj = Integer.valueOf(-1);
            if(j > 65535)
                obj = Integer.valueOf(65535);
        }
	//UDP单播
        if(socketoption == StandardSocketOptions.IP_TOS)
        {
            int k = ((Integer)obj).intValue();
            if(k < 0 || k > 255)
                throw new IllegalArgumentException("Invalid IP_TOS value");
        }
	//UDP多播
        if(socketoption == StandardSocketOptions.IP_MULTICAST_TTL)
        {
            int l = ((Integer)obj).intValue();
            if(l < 0 || l > 255)
                throw new IllegalArgumentException("Invalid TTL/hop value");
        }
        OptionKey optionkey = SocketOptionRegistry.findOption(socketoption, protocolfamily);
        if(optionkey == null)
            throw new AssertionError("Option not found");
        int i1;
	//转换配置参数值
        if(class1 == java/lang/Integer)
        {
            i1 = ((Integer)obj).intValue();
        } else
        {
            boolean flag = ((Boolean)obj).booleanValue();
            i1 = flag ? 1 : 0;
        }
        boolean flag1 = protocolfamily == UNSPEC;
	//设置文件描述符的值
        setIntOption0(filedescriptor, flag1, optionkey.level(), optionkey.name(), i1);
    }

    private static native void setIntOption0(FileDescriptor filedescriptor, boolean flag, int i, int j, int k)
        throws IOException;

//获取配置选项
 public Object getOption(SocketOption socketoption)
        throws IOException
    {
        if(socketoption == null)
            throw new NullPointerException();
	//检查配置
        if(!supportedOptions().contains(socketoption))
            throw new UnsupportedOperationException((new StringBuilder()).append("'").append(socketoption).append("' not supported").toString());
        Object obj = stateLock;
        JVM INSTR monitorenter ;
	//检查通道打开状态
        if(!isOpen())
            throw new ClosedChannelException();
	//IP_TOS配置项返回值,如果iP6可用,返回0,否则委托给Net
        if(socketoption == StandardSocketOptions.IP_TOS)
            return Net.isIPv6Available() ? Integer.valueOf(0) : Net.getSocketOption(fd, StandardProtocolFamily.INET, socketoption);
        //获取配置项
	Net.getSocketOption(fd, Net.UNSPEC, socketoption);
        obj;
        JVM INSTR monitorexit ;
        return;
        Exception exception;
        exception;
        throw exception;
    }

//Net
static Object getSocketOption(FileDescriptor filedescriptor, ProtocolFamily protocolfamily, SocketOption socketoption)
        throws IOException
    {
        Class class1 = socketoption.type();
	//非整形和布尔型,则抛出断言错误
        if(class1 != java/lang/Integer && class1 != java/lang/Boolean)
            throw new AssertionError("Should not reach here");
        OptionKey optionkey = SocketOptionRegistry.findOption(socketoption, protocolfamily);
        if(optionkey == null)
            throw new AssertionError("Option not found");
        boolean flag = protocolfamily == UNSPEC;
	//获取文件描述的选项配置
        int i = getIntOption0(filedescriptor, flag, optionkey.level(), optionkey.name());
        if(class1 == java/lang/Integer)
            return Integer.valueOf(i);
        else
            return i != 0 ? Boolean.TRUE : Boolean.FALSE;
    }
    private static native int getIntOption0(FileDescriptor filedescriptor, boolean flag, int i, int j)
        throws IOException;

//获取通道Socket
  public Socket socket()
    {
        Object obj = stateLock;
        JVM INSTR monitorenter ;
        if(socket == null)
	    //创建Socket适配器
            socket = SocketAdaptor.create(this);
        return socket;
        Exception exception;
        exception;
        throw exception;
    }

//SocketAdaptor,可简单理解为SocketChannelImpl的代理
public class SocketAdaptor extends Socket
{
    private final SocketChannelImpl sc;
    private volatile int timeout;
    private InputStream socketInputStream;//输入流
    static final boolean $assertionsDisabled = !sun/nio/ch/SocketAdaptor.desiredAssertionStatus();
     //创建socket适配器
     public static Socket create(SocketChannelImpl socketchannelimpl)
    {
        return new SocketAdaptor(socketchannelimpl);
        SocketException socketexception;
        socketexception;
        throw new InternalError("Should not reach here");
    }
    //构造SocketAdaptor
      private SocketAdaptor(SocketChannelImpl socketchannelimpl)
        throws SocketException
    {
        super((SocketImpl)null);
        timeout = 0;
        socketInputStream = null;
        sc = socketchannelimpl;
    }
     public SocketChannel getChannel()
    {
        return sc;
    }

    public void connect(SocketAddress socketaddress)
        throws IOException
    {
        connect(socketaddress, 0);
    }
    public void connect(SocketAddress socketaddress, int i)
        throws IOException
    {
    ...
    sc.configureBlocking(false);
        if(!sc.connect(socketaddress))
    ...
    }
    //绑定地址
    public void bind(SocketAddress socketaddress)
        throws IOException
    {
        try
        {
            sc.bind(socketaddress);
        }
        catch(Exception exception)
        {
            Net.translateException(exception);
        }
    }
    //获取远端socket地址
      public InetAddress getInetAddress()
    {
        SocketAddress socketaddress = sc.remoteAddress();
        if(socketaddress == null)
            return null;
        else
            return ((InetSocketAddress)socketaddress).getAddress();
    }
    //获取本地地址
    public InetAddress getLocalAddress()
    {
        if(sc.isOpen())
        {
            SocketAddress socketaddress = sc.localAddress();
            if(socketaddress != null)
                return ((InetSocketAddress)socketaddress).getAddress();
        }
        return (new InetSocketAddress(0)).getAddress();
    }
   //获取远端socket端口
    public int getPort()
    {
        SocketAddress socketaddress = sc.remoteAddress();
        if(socketaddress == null)
            return 0;
        else
            return ((InetSocketAddress)socketaddress).getPort();
    }
    还有一些方法,我们这里就不一一列出了,相关方法都是通过内部
    socketChannelImpl实例的相应方法实现,所有SocketAdaptor,可简单理解为SocketChannelImpl的代理
}

//SocketAdaptor结构图:





//获取本地socket地址
    public SocketAddress getLocalAddress()
        throws IOException
    {
        Object obj = stateLock;
        JVM INSTR monitorenter ;
        if(!isOpen())
            throw new ClosedChannelException();
        return localAddress;
        Exception exception;
        exception;
        throw exception;
    }
//获取远端Socket地址
    public SocketAddress getRemoteAddress()
        throws IOException
    {
        Object obj = stateLock;
        JVM INSTR monitorenter ;
        if(!isOpen())
            throw new ClosedChannelException();
        return remoteAddress;
        Exception exception;
        exception;
        throw exception;
    }
//关闭输入流
public SocketChannel shutdownInput()
        throws IOException
    {
        Object obj = stateLock;
        JVM INSTR monitorenter ;
        if(!isOpen())
            throw new ClosedChannelException();
        if(!isConnected())
            throw new NotYetConnectedException();
        if(isInputOpen)
        {
	    //为Net关闭fd对应的输入流
            Net.shutdown(fd, 0);
            if(readerThread != 0L)
	        //通知读线程,输入流关闭
                NativeThread.signal(readerThread);
            isInputOpen = false;
        }
        return this;
        Exception exception;
        exception;
        throw exception;
    }
//关闭输出流
    public SocketChannel shutdownOutput()
        throws IOException
    {
        Object obj = stateLock;
        JVM INSTR monitorenter ;
        if(!isOpen())
            throw new ClosedChannelException();
        if(!isConnected())
            throw new NotYetConnectedException();
        if(isOutputOpen)
        {    
	    //为Net关闭fd对应的输出流
            Net.shutdown(fd, 1);
            if(writerThread != 0L)
	        //通知写线程,输出流关闭
                NativeThread.signal(writerThread);
            isOutputOpen = false;
        }
        return this;
        Exception exception;
        exception;
        throw exception;
    }

//Net
static native void shutdown(FileDescriptor filedescriptor, int i)
        throws IOException;

//NativeThread
package sun.nio.ch;
class NativeThread
{
    NativeThread()
    {
    }
    static long current()
    {
        return 0L;
    }
    static void signal(long l)
    {
    }
}

//输出流是否关闭
 public boolean isInputOpen()
{
    Object obj = stateLock;
    JVM INSTR monitorenter ;
    return isInputOpen;
    Exception exception;
    exception;
    throw exception;
}
//输入流是否关闭
public boolean isOutputOpen()
{
    Object obj = stateLock;
    JVM INSTR monitorenter ;
    return isOutputOpen;
    Exception exception;
    exception;
    throw exception;
}
//是否连接
 public boolean isConnected()
{
    Object obj = stateLock;
    JVM INSTR monitorenter ;
    return state == 2;
    Exception exception;
    exception;
    throw exception;
}

//关闭选择通道
protected void implCloseSelectableChannel()
        throws IOException
    {
        synchronized(stateLock)//同步状态锁
        {
	    //置输入流和输出流打开状态为false
            isInputOpen = false;
            isOutputOpen = false;
            if(state != 4)
	        //如果通道没有关闭,则预先关闭fd
                nd.preClose(fd);
            if(readerThread != 0L)
	        //通知读线程,关闭输入流
                NativeThread.signal(readerThread);
            if(writerThread != 0L)
	        //通知写线程,输出流关闭
                NativeThread.signal(writerThread);
            if(!isRegistered())
	        //如果当前没有注册到任何选择器,则调用kill完成实际关闭工作
                kill();
        }
    }

关闭选择通道有两点需要关注
1.
 
//如果通道没有关闭,则预先关闭fd
nd.preClose(fd);

//SocketDispatcher
void preClose(FileDescriptor filedescriptor)
     throws IOException
 {
     preClose0(filedescriptor);
 }
 static native void preClose0(FileDescriptor filedescriptor)
        throws IOException;

2.
 
//如果当前没有注册到任何选择器,则调用kill完成实际关闭工作
kill();

  
 public void kill()
        throws IOException
    {
label0:
        {
            synchronized(stateLock)
            {
                if(state != 4)
                    break label0;
            }
            return;
        }
        if(state != -1)
            break MISSING_BLOCK_LABEL_34;
        state = 4;
        obj;
        JVM INSTR monitorexit ;
        return;
        if(!$assertionsDisabled && (isOpen() || isRegistered()))
            throw new AssertionError();
        if(readerThread == 0L && writerThread == 0L)
        {
	    //委托SocketDispatcher关闭通道
            nd.close(fd);
            state = 4;//已经关闭
        } else
        {
	    //正在关闭
            state = 3;
        }
        obj;
        JVM INSTR monitorexit ;
          goto _L1
        exception;
        throw exception;
_L1:
    }

来看
//委托SocketDispatcher关闭通道
 nd.close(fd);

//SocketDispatcher
void close(FileDescriptor filedescriptor)
      throws IOException
  {
      close0(filedescriptor);
  }
static native void close0(FileDescriptor filedescriptor)
      throws IOException;

从上面可以看出:
实际关闭通道,首先同步状态锁,置输入流和输出流打开状态为false,
如果通道没有关闭,则通过SocketDispatcher预先关闭fd,通知读线程,关闭输入流,
通知写线程,输出流关闭,如果当前没有注册到任何选择器,则调用kill完成实际关闭工作,
即SocketDispatcher关闭fd。
//设置通道兴趣事件
 public void translateAndSetInterestOps(int i, SelectionKeyImpl selectionkeyimpl)
    {
        int j = 0;
        if((i & 1) != 0)
            j |= 1;//读事件
        if((i & 4) != 0)
            j |= 4;//写事件
        if((i & 8) != 0)
            j |= 2;//连接事件
        selectionkeyimpl.selector.putEventOps(selectionkeyimpl, j);
    }
//设置就绪事件
public boolean translateAndSetReadyOps(int i, SelectionKeyImpl selectionkeyimpl)
{
    return translateReadyOps(i, 0, selectionkeyimpl);
}
//更新就绪事件
public boolean translateAndUpdateReadyOps(int i, SelectionKeyImpl selectionkeyimpl)
{
    return translateReadyOps(i, selectionkeyimpl.nioReadyOps(), selectionkeyimpl);
}
public boolean translateReadyOps(int i, int j, SelectionKeyImpl selectionkeyimpl)
{
    int k = selectionkeyimpl.nioInterestOps();
    int l = selectionkeyimpl.nioReadyOps();
    int i1 = j;
    //就绪事件为读1写4连接8,接受连接事件16,不是这四种事件,则返回false
    if((i & 32) != 0)
        return false;
    //下面的这段24,16不是很明白,理解的网友可以给我留言,一起探讨,
    //莫非为8+16,接受连接,并建立连接
    if((i & 24) != 0)
    {
        i1 = k;
        selectionkeyimpl.nioReadyOps(i1);
        readyToConnect = true;//准备连接
        return (i1 & ~l) != 0;
    }
    if((i & 1) != 0 && (k & 1) != 0 && state == 2)
        i1 |= 1;//读事件,已连接
    if((i & 2) != 0 && (k & 8) != 0 && (state == 0 || state == 1))
    {
        i1 |= 8;//连接事件,正在连接
        readyToConnect = true;
    }
    if((i & 4) != 0 && (k & 4) != 0 && state == 2)
        i1 |= 4;//写事件
    selectionkeyimpl.nioReadyOps(i1);
    return (i1 & ~l) != 0;
}
//获取通道文件描述
 public FileDescriptor getFD()
{
    return fd;
}
//获取通道文件描述值
public int getFDVal()
{
    return fdVal;
}


总结:
实际关闭通道,同步状态锁,置输入流和输出流打开状态为false,如果通道没有关闭,则通过SocketDispatcher预先关闭fd,通知读线程,关闭输入流,通知写线程,输出流关闭,如果当前没有注册到任何选择器,则调用kill完成实际关闭工作,
即SocketDispatcher关闭fd。
  • 大小: 81.6 KB
  • 大小: 50.7 KB
分享到:
评论

相关推荐

    Ice-3.7.4.msi for windows版

    6 连接突然断开(比如对方因为段错误而程序停止,或者在连接过程中正常关闭) : 发生在使用代理调用接口的时候(连接丢失) 原因: 已经建立连接的对方突然断开 错误信息: warning: connection exception: ...

    nodejs-x64-0.10.21.tgz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    node-v4.1.1-linux-armv6l.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    node-v4.1.0-linux-arm64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    matlab S-Function 混合系统仿真

    matlab绘制函数图像 MATLAB (Matrix Laboratory) 是一种用于数值计算的高级编程语言和交互式环境,由 MathWorks 公司开发。它广泛用于算法开发、数据可视化、数据分析以及数值计算的高级技术计算语言和交互式环境。以下是一些 MATLAB 的基本特性和使用方式: 1. 基本语法 变量:MATLAB 中的变量不需要预先声明,直接赋值即可。 数组:MATLAB 使用方括号 [] 创建数组,数组索引从 1 开始。 运算符:包括加、减、乘、除、乘方等。 函数:MATLAB 有大量内置函数,也可以编写自定义函数。 2. 绘图 MATLAB 提供了丰富的绘图功能,如绘制线图、散点图、柱状图、饼图等。 matlab x = 0:0.01:2*pi; y = sin(x); plot(x, y); title('Sine Function'); xlabel('x'); ylabel('y'); 3. 数据分析 MATLAB 可以处理各种类型的数据,包括矩阵、向量、数组等,并提供了许多数据分析函数,如统计函数、信号处理函数等。 4. 脚本和函数

    智慧交通规划方案.pptx

    智慧交通规划方案.pptx

    基于单片机的智能鱼缸监控系统设计+全部资料+详细文档(高分项目).zip

    【资源说明】 基于单片机的智能鱼缸监控系统设计+全部资料+详细文档(高分项目).zip基于单片机的智能鱼缸监控系统设计+全部资料+详细文档(高分项目).zip基于单片机的智能鱼缸监控系统设计+全部资料+详细文档(高分项目).zip 【备注】 1、该项目是个人高分项目源码,已获导师指导认可通过,答辩评审分达到95分 2、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 3、本项目适合计算机相关专业(人工智能、通信工程、自动化、电子信息、物联网等)的在校学生、老师或者企业员工下载使用,也可作为毕业设计、课程设计、作业、项目初期立项演示等,当然也适合小白学习进阶。 4、如果基础还行,可以在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!

    FT232驱动串口驱动FT232工业串口

    FT232串口驱动win10亲测可用

    数据分析-电商数据分析

    # 订单数据分析代码说明文档 ## 概述 该代码用于对订单数据进行统计分析,并生成相应的可视化图表。主要功能包括每日订单趋势、每小时订单趋势以及各城市订单数量统计。 ## 文件结构 - `main.py`: 主程序文件,包含了数据处理函数的调用和结果展示。 - `cosmetic.py`: 包含了与化妆品相关的数据分析函数。 - `table.html`: 表格图表的 HTML 文件。 - `citys.html`: 各城市订单数量统计的 HTML 文件。 - `days.html`: 每日订单趋势图表的 HTML 文件。 - `hours.html`: 每小时订单趋势图表的 HTML 文件。 ## 数据处理函数 1. `get_table(data)`: 生成订单数据统计表格,并将结果保存为 HTML 文件。 2. `get_citys(data)`: 生成各城市订单数量统计柱状图,并将结果保存为 HTML 文件。 3. `get_days(data)`: 生成每日订单趋势折线图,并将结果保存为 HTML 文件。 4. `get_hours(data)`: 生成每小时订单趋势折

    node-v5.9.0-linux-arm64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    css 新增2d,3d变换特性完成的vr场景开发.zip

    VR(Virtual Reality)即虚拟现实,是一种可以创建和体验虚拟世界的计算机技术。它利用计算机生成一种模拟环境,是一种多源信息融合的、交互式的三维动态视景和实体行为的系统仿真,使用户沉浸到该环境中。VR技术通过模拟人的视觉、听觉、触觉等感觉器官功能,使人能够沉浸在计算机生成的虚拟境界中,并能够通过语言、手势等自然的方式与之进行实时交互,创建了一种适人化的多维信息空间。 VR技术具有以下主要特点: 沉浸感:用户感到作为主角存在于模拟环境中的真实程度。理想的模拟环境应该使用户难以分辨真假,使用户全身心地投入到计算机创建的三维虚拟环境中,该环境中的一切看上去是真的,听上去是真的,动起来是真的,甚至闻起来、尝起来等一切感觉都是真的,如同在现实世界中的感觉一样。 交互性:用户对模拟环境内物体的可操作程度和从环境得到反馈的自然程度(包括实时性)。例如,用户可以用手去直接抓取模拟环境中虚拟的物体,这时手有握着东西的感觉,并可以感觉物体的重量,视野中被抓的物体也能立刻随着手的移动而移动。 构想性:也称想象性,指用户沉浸在多维信息空间中,依靠自己的感知和认知能力获取知识,发挥主观能动性,寻求解答,形成新的概念。此概念不仅是指观念上或语言上的创意,而且可以是指对某些客观存在事物的创造性设想和安排。 VR技术可以应用于各个领域,如游戏、娱乐、教育、医疗、军事、房地产、工业仿真等。随着VR技术的不断发展,它正在改变人们的生活和工作方式,为人们带来全新的体验。

    Vue从0搭建后台管理系统框架

    里面有md文档介绍,也有源码,自己可根据md文档进行一步一步的框架搭建。 这里说一句为什么要出这个,首先也是争对我自己,我自己vue逻辑层代码也写了很多,网页端也搭建过很多。还有很多案例,但唯独没有搭建过这种后台管理的,不知道也怎么入手。看很多大佬写的源码也看得懂。但是就是不知道该怎么去搭建这个框架。 所以我也是特意花了一点时间去网上找了一些资源然后进行学习模仿。后面也是终于理清它这个单页面的实现。也希望这份文档资源可以帮到大家。

    node-v0.12.11-sunos-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    数据结构和算法必知必会的50个代码实现.zip

    【数据结构课程设计】 数组 实现一个支持动态扩容的数组 实现一个大小固定的有序数组,支持动态增删改操作 实现两个有序数组合并为一个有序数组 链表 实现单链表、循环链表、双向链表,支持增删操作 实现单链表反转 实现两个有序的链表合并为一个有序链表 实现求链表的中间结点 栈 用数组实现一个顺序栈 用链表实现一个链式栈 编程模拟实现一个浏览器的前进、后退功能 队列 用数组实现一个顺序队列 用链表实现一个链式队列 实现一个循环队列 ......

    基于puma560机械臂RRT路径规划matlab仿真源码(高分课设).zip

    基于puma560机械臂RRT路径规划matlab仿真源码(高分课设).zip个人经导师指导并认可通过的98分大作业设计项目,主要针对计算机相关专业的正在做课程设计、期末大作业的学生和需要项目实战练习的学习者。 基于puma560机械臂RRT路径规划matlab仿真源码(高分课设).zip个人经导师指导并认可通过的98分大作业设计项目,主要针对计算机相关专业的正在做课程设计、期末大作业的学生和需要项目实战练习的学习者。 基于puma560机械臂RRT路径规划matlab仿真源码(高分课设).zip个人经导师指导并认可通过的98分大作业设计项目,主要针对计算机相关专业的正在做课程设计、期末大作业的学生和需要项目实战练习的学习者。 基于puma560机械臂RRT路径规划matlab仿真源码(高分课设).zip个人经导师指导并认可通过的98分大作业设计项目,主要针对计算机相关专业的正在做课程设计、期末大作业的学生和需要项目实战练习的学习者。 基于puma560机械臂RRT路径规划matlab仿真源码(高分课设).zip个人经导师指导并认可通过的98分大作业设计项目,主要针对计算机相关

    node-v5.10.1-darwin-x64.tar.xz

    Node.js,简称Node,是一个开源且跨平台的JavaScript运行时环境,它允许在浏览器外运行JavaScript代码。Node.js于2009年由Ryan Dahl创立,旨在创建高性能的Web服务器和网络应用程序。它基于Google Chrome的V8 JavaScript引擎,可以在Windows、Linux、Unix、Mac OS X等操作系统上运行。 Node.js的特点之一是事件驱动和非阻塞I/O模型,这使得它非常适合处理大量并发连接,从而在构建实时应用程序如在线游戏、聊天应用以及实时通讯服务时表现卓越。此外,Node.js使用了模块化的架构,通过npm(Node package manager,Node包管理器),社区成员可以共享和复用代码,极大地促进了Node.js生态系统的发展和扩张。 Node.js不仅用于服务器端开发。随着技术的发展,它也被用于构建工具链、开发桌面应用程序、物联网设备等。Node.js能够处理文件系统、操作数据库、处理网络请求等,因此,开发者可以用JavaScript编写全栈应用程序,这一点大大提高了开发效率和便捷性。 在实践中,许多大型企业和组织已经采用Node.js作为其Web应用程序的开发平台,如Netflix、PayPal和Walmart等。它们利用Node.js提高了应用性能,简化了开发流程,并且能更快地响应市场需求。

    基于GPT及个人习惯的comfyui节点.zip

    GPT, 通常指的是“Generative Pre-trained Transformer”(生成式预训练转换器),是一个在自然语言处理(NLP)领域非常流行的深度学习模型架构。GPT模型由OpenAI公司开发,并在多个NLP任务上取得了显著的性能提升。 GPT模型的核心是一个多层Transformer解码器结构,它通过在海量的文本数据上进行预训练来学习语言的规律。这种预训练方式使得GPT模型能够捕捉到丰富的上下文信息,并生成流畅、自然的文本。 GPT模型的训练过程可以分为两个阶段: 预训练阶段:在这个阶段,模型会接触到大量的文本数据,并通过无监督学习的方式学习语言的结构和规律。具体来说,模型会尝试预测文本序列中的下一个词或短语,从而学习到语言的语法、语义和上下文信息。 微调阶段(也称为下游任务训练):在预训练完成后,模型会被应用到具体的NLP任务中,如文本分类、机器翻译、问答系统等。在这个阶段,模型会使用有标签的数据进行微调,以适应特定任务的需求。通过微调,模型能够学习到与任务相关的特定知识,并进一步提高在该任务上的性能。 GPT模型的优势在于其强大的生成能力和对上下文信息的捕捉能力。这使得GPT模型在自然语言生成、文本摘要、对话系统等领域具有广泛的应用前景。同时,GPT模型也面临一些挑战,如计算资源消耗大、训练时间长等问题。为了解决这些问题,研究人员不断提出新的优化方法和扩展模型架构,如GPT-2、GPT-3等,以进一步提高模型的性能和效率。

    KylinGPT 是一个基于OpenAI的GPT模型的应用,它包括文本润色、翻译、图片搜索以及图片生成等功能.zip

    GPT, 通常指的是“Generative Pre-trained Transformer”(生成式预训练转换器),是一个在自然语言处理(NLP)领域非常流行的深度学习模型架构。GPT模型由OpenAI公司开发,并在多个NLP任务上取得了显著的性能提升。 GPT模型的核心是一个多层Transformer解码器结构,它通过在海量的文本数据上进行预训练来学习语言的规律。这种预训练方式使得GPT模型能够捕捉到丰富的上下文信息,并生成流畅、自然的文本。 GPT模型的训练过程可以分为两个阶段: 预训练阶段:在这个阶段,模型会接触到大量的文本数据,并通过无监督学习的方式学习语言的结构和规律。具体来说,模型会尝试预测文本序列中的下一个词或短语,从而学习到语言的语法、语义和上下文信息。 微调阶段(也称为下游任务训练):在预训练完成后,模型会被应用到具体的NLP任务中,如文本分类、机器翻译、问答系统等。在这个阶段,模型会使用有标签的数据进行微调,以适应特定任务的需求。通过微调,模型能够学习到与任务相关的特定知识,并进一步提高在该任务上的性能。 GPT模型的优势在于其强大的生成能力和对上下文信息的捕捉能力。这使得GPT模型在自然语言生成、文本摘要、对话系统等领域具有广泛的应用前景。同时,GPT模型也面临一些挑战,如计算资源消耗大、训练时间长等问题。为了解决这些问题,研究人员不断提出新的优化方法和扩展模型架构,如GPT-2、GPT-3等,以进一步提高模型的性能和效率。

    仿北京时间Btime V.1.4.0新闻WordPress主题模板源码.rar

    仿北京时间Btime V.1.4.0新闻WordPress主题模板源码.rar

    TFTLCD-ST7789驱动屏幕彩条显示

    TFTLCD屏幕彩条显示,采用的是8080串行显示,使用的是ST7789驱动芯片

Global site tag (gtag.js) - Google Analytics