美文网首页
socket及socket java api

socket及socket java api

作者: miky_zheng | 来源:发表于2019-01-27 11:38 被阅读0次

    wiki上对于soket的定义

    1.起源:
    起源于https://tools.ietf.org/html/rfc147
    2.套接字主要是在Internet模型传输层中使用的概念。路由器交换机等网络设备一般不需要传输层的实现,因为它们一般在链路层(交换机)或互联网层(路由器)上运行。
    3.类型

    广义:socket有两种场景。

    本篇重点:网络套接字以及Java关于socket api

    首先回顾7层网络模型

    10dfa9ec8a1363270baa4fcb9b8fa0ec09fac787.jpg

    由上面socket类型所知,socket主要是传输层的概念,主要也就是udp,和tcp两种情况。(另外一种暂不讨论)

    先看下api
    实例代码:

    try {
                new Socket("127.0.0.1",80);
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
    

    1.常用构造器

       public Socket(InetAddress address, int port) throws IOException {
            this(address != null ? new InetSocketAddress(address, port) : null,
                 (SocketAddress) null, true);
        }
    
    private Socket(SocketAddress address, SocketAddress localAddr,
                       boolean stream) throws IOException {
            setImpl();
    
            // backward compatibility
            if (address == null)
                throw new NullPointerException();
    
            try {
                createImpl(stream);
                if (localAddr != null)
                    bind(localAddr);
                connect(address);
            } catch (IOException | IllegalArgumentException | SecurityException e) {
                try {
                    close();
                } catch (IOException ce) {
                    e.addSuppressed(ce);
                }
                throw e;
            }
        }
    

    1.以上构造器创建了一个IP Socket Address ,基于ip协议的SocketAddress(IP address + port number)

      public InetSocketAddress(InetAddress addr, int port) {
            holder = new InetSocketAddressHolder(
                            null,
                            addr == null ? InetAddress.anyLocalAddress() : addr,
                            checkPort(port));
        }
    

    2.创建SocksSocketImpl

     void setImpl() {
            if (factory != null) {
                impl = factory.createSocketImpl();
                checkOldImpl();
            } else {
                // No need to do a checkOldImpl() here, we know it's an up to date
                // SocketImpl!
                impl = new SocksSocketImpl();
            }
            if (impl != null)
                impl.setSocket(this);
        }
    
    1. createImpl 即SocksSocketImpl::createImpl
      SocksSocketImpl是AbstractPlainSocketImpl的子类,最终看AbstractPlainSocketImpl的createImpl。由代码可知,最终的socket创建是由AbstractPlainSocketImpl创建的。如果是tcp,create(boolean stream)参数为true;如果是udp,参数为false。
     /**
         * Creates a socket with a boolean that specifies whether this
         * is a stream socket (true) or an unconnected UDP socket (false).
         */
        protected synchronized void create(boolean stream) throws IOException {
            this.stream = stream;
            if (!stream) {
                ResourceManager.beforeUdpCreate();
                // only create the fd after we know we will be able to create the socket
                fd = new FileDescriptor();
                try {
                    socketCreate(false);
                } catch (IOException ioe) {
                    ResourceManager.afterUdpClose();
                    fd = null;
                    throw ioe;
                }
            } else {
                fd = new FileDescriptor();
                socketCreate(true);//子类PlainSocketImpl调用native void socketCreate(boolean isServer) throws IOException;
            }
            if (socket != null)
                socket.setCreated();
    //这里的socket,即开始new 的socket(“127.0.0.1”,80)对象
            if (serverSocket != null)
                serverSocket.setCreated();
        }
    

    3.在接着回到开始创建socket的代码:创建完socket,接着校验地址是否是IP4或ip6格式,校验端口是否有权限(如果端口不允许访问,会抛异常SecurityException),接着就开始bind远程地址,并开始connect。

          if (localAddr != null)
                    bind(localAddr);
                connect(address);
    

    bind最终调用的是SocksSocketImpl,即PlainSocketImpl的本地方法

    getImpl().bind (addr, port);
    
        protected synchronized void bind(InetAddress address, int lport)
            throws IOException
        {
           synchronized (fdLock) {
                if (!closePending && (socket == null || !socket.isBound())) {
                    NetHooks.beforeTcpBind(fd, address, lport);
                }
            }
            socketBind(address, lport);//PlainSocketImpl 的native void socketBind(InetAddress address, int port)
            throws IOException;
            if (socket != null)
                socket.setBound();
            if (serverSocket != null)
                serverSocket.setBound();
        }
    

    接着看connect

     public void connect(SocketAddress endpoint) throws IOException {
            connect(endpoint, 0);
        }
    //关键代码
      //再次校验ip地址等
         .....
            else if (timeout == 0) {
                if (epoint.isUnresolved())
                    impl.connect(addr.getHostName(), port);
                else
                    impl.connect(addr, port);
            } else
                throw new UnsupportedOperationException("SocketImpl.connect(addr, timeout)");
            connected = true;
            bound = true;
    

    上面连接的流程,默认timeout为0,直接连接,不支持超时。
    改过程为阻塞的。( The connection will then block until established or an error occurs.)

     protected void connect(String host, int port)
            throws UnknownHostException, IOException
        {
            boolean connected = false;
            try {
                InetAddress address = InetAddress.getByName(host);
                this.port = port;
                this.address = address;
    
                connectToAddress(address, port, timeout);
                connected = true;
            } finally {
                if (!connected) {
                    try {
                        close();
                    } catch (IOException ioe) {
                        /* Do nothing. If connect threw an exception then
                           it will be passed up the call stack */
                    }
                }
            }
        }
    

    最终也是AbstractPlainSocketImpl调用子类PlainSocketImpl的本地方法socketConnect,来连接。

    4.服务端socket创建, ServerSocket和Socket是完全相干的两个类。
    但是他们的初始化,共同点:如果指定端口,都会在初始化的时候尝试bind本地端口,并判断该端口是否有权限。区别:Socket如果初始化指定了(IP,port)除了bind外,会多一步connect的操作。

    new ServerSocket(80);
    
    public
    class ServerSocket implements java.io.Closeable
       public ServerSocket(int port) throws IOException {
            this(port, 50, null);
        }
    

    ServerSocket(port)默认队列大小50.当超过50的连接会被拒绝。

     getImpl().bind(epoint.getAddress(), epoint.getPort());
                getImpl().listen(backlog);
                bound = true;
    

    结下来看,accept():阻塞的,除非接收到一个连接。
    步骤:创建一个unconnected Socket,并调用implAccept(s)

         * Listens for a connection to be made to this socket and accepts
         * it. The method blocks until a connection is made.
         *
         * <p>A new Socket {@code s} is created and, if there
         * is a security manager,
         * the security manager's {@code checkAccept} method is called
         * with {@code s.getInetAddress().getHostAddress()} and
         * {@code s.getPort()}
         * as its arguments to ensure the operation is allowed.
         * This could result in a SecurityException.
         *
         * @exception  IOException  if an I/O error occurs when waiting for a
         *               connection.
         * @exception  SecurityException  if a security manager exists and its
         *             {@code checkAccept} method doesn't allow the operation.
         * @exception  SocketTimeoutException if a timeout was previously set with setSoTimeout and
         *             the timeout has been reached.
         * @exception  java.nio.channels.IllegalBlockingModeException
         *             if this socket has an associated channel, the channel is in
         *             non-blocking mode, and there is no connection ready to be
         *             accepted
      public Socket accept() throws IOException {
            if (isClosed())
                throw new SocketException("Socket is closed");
            if (!isBound())
                throw new SocketException("Socket is not bound yet");
            Socket s = new Socket((SocketImpl) null);
            implAccept(s);
            return s;
        }
    

    接下来,和客户端socket一样,默认新建SocksSocketImpl对象,并添加新的InetAddress对象

      protected final void implAccept(Socket s) throws IOException {
            SocketImpl si = null;
            try {
                if (s.impl == null)
                  s.setImpl();
                else {
                    s.impl.reset();
                }
                si = s.impl;
                s.impl = null;
                si.address = new InetAddress();
                si.fd = new FileDescriptor();
                getImpl().accept(si);
    
                SecurityManager security = System.getSecurityManager();
                if (security != null) {
                    security.checkAccept(si.getInetAddress().getHostAddress(),
                                         si.getPort());
                }
            } catch (IOException e) {
                if (si != null)
                    si.reset();
                s.impl = si;
                throw e;
            } catch (SecurityException e) {
                if (si != null)
                    si.reset();
                s.impl = si;
                throw e;
            }
            s.impl = si;
            s.postAccept();
        }
    
        /**
         * Constructor for the Socket.accept() method.
         * This creates an empty InetAddress, which is filled in by
         * the accept() method.  This InetAddress, however, is not
         * put in the address cache, since it is not created by name.
         */
        InetAddress() {
            holder = new InetAddressHolder();
        }
    
    

    调用新建SocksSocketImpl的accept,即AbstractPlainSocketImpl的accept,如下:

       /**
         * Accepts connections.
         * @param s the connection
         */
        protected void accept(SocketImpl s) throws IOException {
            acquireFD();
            try {
                socketAccept(s);//子类PlainSocketImpl的本地方法 native void socketAccept(SocketImpl s) throws IOException;
            } finally {
                releaseFD();//释放fd
            }
        }
    
        /*
         * "Release" the FileDescriptor for this impl.
         *
         * If the use count goes to -1 then the socket is closed.
         */
        void releaseFD() {
            synchronized (fdLock) {
                fdUseCount--;
                if (fdUseCount == -1) {
                    if (fd != null) {
                        try {
                            socketClose();
                        } catch (IOException e) {
                        } finally {
                            fd = null;
                        }
                    }
                }
            }
        }
    
    题外话:

    socket体系这里面涉及到了工厂的设计模式。方便内容的扩展。如果设置了factory,就按照自己设置的factory去实现
    SocketImpl。
    代码如下:

     /**
         * Sets impl to the system-default type of SocketImpl.
         * @since 1.4
         */
        void setImpl() {
            if (factory != null) {
                impl = factory.createSocketImpl();
                checkOldImpl();
            } else {
                // No need to do a checkOldImpl() here, we know it's an up to date
                // SocketImpl!
                impl = new SocksSocketImpl();
            }
            if (impl != null)
                impl.setSocket(this);
        }
    
    public
    interface SocketImplFactory {
        /**
         * Creates a new {@code SocketImpl} instance.
         *
         * @return  a new instance of {@code SocketImpl}.
         * @see     java.net.SocketImpl
         */
        SocketImpl createSocketImpl();
    }
    
       public static synchronized void setSocketImplFactory(SocketImplFactory fac)
            throws IOException
        {
            if (factory != null) {
                throw new SocketException("factory already defined");
            }
            SecurityManager security = System.getSecurityManager();
            if (security != null) {
                security.checkSetFactory();
            }
            factory = fac;
        }
    

    相关文章

      网友评论

          本文标题:socket及socket java api

          本文链接:https://www.haomeiwen.com/subject/bpegjqtx.html