JDK源码-Socket套接字系列

套接字基本流程

ServerSocket

//指定端口的构造器
public ServerSocket(int port) throws IOException {
        this(port, 50, null);
    }
上个构造器调用了该方法:port指定了服务器端socket监听的端口
public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException {
        setImpl();
        if (port < 0 || port > 0xFFFF)
            throw new IllegalArgumentException(
                       "Port value out of range: " + port);
        if (backlog < 1)
          backlog = 50;
        try {
            bind(new InetSocketAddress(bindAddr, port), backlog);
        } catch(SecurityException e) {
            close();
            throw e;
        } catch(IOException e) {
            close();
            throw e;
        }
    }
backlog指服务器端处理请求的队列最大的长度,默认为50.
InetSocketAddress是SocketAddress的实现类.封装了hostname,InetAddress,port等参数.

想了解InetAddress可以参看这篇文章:http://www.jianshu.com/p/0ba3a391de03

  • 通过bind方法,对该ip+port进行监听
public void bind(SocketAddress endpoint, int backlog) throws IOException {
        if (isClosed())//判断socket是否关闭
            throw new SocketException("Socket is closed");
        if (!oldImpl && isBound())//判断socket的监听状态
            throw new SocketException("Already bound");
        if (endpoint == null)
            endpoint = new InetSocketAddress(0);
        if (!(endpoint instanceof InetSocketAddress))
            throw new IllegalArgumentException("Unsupported address type");
        InetSocketAddress epoint = (InetSocketAddress) endpoint;
        if (epoint.isUnresolved())
            throw new SocketException("Unresolved address");
        if (backlog < 1)
          backlog = 50;
        try {
            SecurityManager security = System.getSecurityManager();
            if (security != null)
                security.checkListen(epoint.getPort());
            getImpl().bind(epoint.getAddress(), epoint.getPort());
            getImpl().listen(backlog);
            bound = true;
        } catch(SecurityException e) {
            bound = false;
            throw e;
        } catch(IOException e) {
            bound = false;
            throw e;
        }
    }
这里调用SocketImpl的bind方法: getImpl().bind(epoint.getAddress(), epoint.getPort());
SocketImpl的抽象实现类:AbstractPlainSocketImpl:
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);
        if (socket != null)
            socket.setBound();
        if (serverSocket != null)
            serverSocket.setBound();
    }

  • 通过accept方法来接收socket连接:
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;
    }
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();
            //接收socket
            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();
    }
这里getImpl().accept(si)调用了默认抽象实现类AbstractPlainSocketImpl的accept:
protected void accept(SocketImpl s) throws IOException {
        //先获取fd
        acquireFD();
        try {
            //该方法还是调用了本地方法:PlainSocketImpl:native void socketAccept(SocketImpl s) throws IOException;
            socketAccept(s);
        } finally {
            releaseFD();
        }
    }
//添加synchronized锁,保证多线程情况的数据一致性
FileDescriptor acquireFD() {
        synchronized (fdLock) {
            fdUseCount++;
            return fd;
        }
    }
  • 需要特别注意的AbstractPlainSocketImpl有两个参数:fdLock对象是为了给每个操作socket的方法,添加同步锁synchronized锁,fdUseCount用来记录当前服务器端socket保持连接的线程数.因为在关闭ServerSocket的时候,需要将socket中的数据清空之后,才能关闭.
 /* number of threads using the FileDescriptor */
    protected int fdUseCount = 0;

    /* lock when increment/decrementing fdUseCount */
    protected final Object fdLock = new Object();

protected void close() throws IOException {
        synchronized(fdLock) {
            if (fd != null) {
                if (!stream) {
                    ResourceManager.afterUdpClose();
                }
                if (fdUseCount == 0) {
                    if (closePending) {
                        return;
                    }
                    closePending = true;
                    /*
                     * We close the FileDescriptor in two-steps - first the
                     * "pre-close" which closes the socket but doesn't
                     * release the underlying file descriptor. This operation
                     * may be lengthy due to untransmitted data and a long
                     * linger interval. Once the pre-close is done we do the
                     * actual socket to release the fd.
                     */
                    try {
                        socketPreClose();
                    } finally {
                        socketClose();
                    }
                    fd = null;
                    return;
                } else {
                    /*
                     * If a thread has acquired the fd and a close
                     * isn't pending then use a deferred close.
                     * Also decrement fdUseCount to signal the last
                     * thread that releases the fd to close it.
                     */
                    if (!closePending) {
                        closePending = true;
                        fdUseCount--;
                        socketPreClose();
                    }
                }
            }
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • JAVA套接字之TCP编程 1 TCP协议 TCP是面向谅解的协议。所谓连接,就是两个对等实体为进行数据通信而进行...
    yanzhelee阅读 1,212评论 0 3
  • 一、通信前提 两台计算机要进行通信,就要满足以下条件:1.两台主机要有唯一的标识,也就是IP地址,用来表示它们所处...
    不知名的蛋挞阅读 722评论 0 0
  • 1.import static是Java 5增加的功能,就是将Import类中的静态方法,可以作为本类的静态方法来...
    XLsn0w阅读 1,277评论 0 2
  • 面向对象主要针对面向过程。 面向过程的基本单元是函数。 什么是对象:EVERYTHING IS OBJECT(万物...
    sinpi阅读 1,103评论 0 4
  • 今天是周末,我像平日一样去附近的商场敷面膜。我是走路去的,从我家到商场有一个地下商城,我慢悠悠的提着装面膜的袋子走...
    青荷妖妖阅读 400评论 15 12