美文网首页
JDK源码-Socket套接字系列

JDK源码-Socket套接字系列

作者: 薛云龙 | 来源:发表于2017-09-21 19:47 被阅读72次
套接字基本流程

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();
                    }
                }
            }
        }
    }

相关文章

  • JDK源码-Socket套接字系列

    ServerSocket 想了解InetAddress可以参看这篇文章:http://www.jianshu.co...

  • AF_UNSPEC、AF_INET和AF_INET6之间的关系

    创建套接字 socket(3SOCKET) 调用创建指定系列和指定类型的套接字。 如果未指定协议,则系统将选择支持...

  • iOS 原生Socket和CocoaAsyncSocket框架的

    一、Socket到底是什么? 1、Socket原理 1.1、套接字(Socket)概念 套接字(Socket)是通...

  • day19

    socket套接字 socket又叫套接字,实现网络通信的python通过提供socket标准库来支持socket...

  • 2018-09-12 day18网络编程

    1.socket套接字 socket又叫套接字,就是进行数据通信两端。分为服务端套接字和客户端套接字 套接字编程:...

  • Socket

    Socket socket是套接字,多指传输层网络接口。 Socket和SocketServer是基于套接字的服务...

  • Python基础语法-3

    socket套接字使用流程:1.创建套接字2.使用套接字收/发数据3.关闭套接字 创建一个TCP socket 创...

  • Socket 网络编程(一)

    Socket Socket,又称“套接字”,应用程序通常通过“套接字”向网络发出请求或应答网络请求。 Socket...

  • CocoaAsyncSocket --Socket学习

    Socket理论 套接字(Socket)概念 套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信...

  • iOS 网络(三)-Socket

    一、Socket原理 1.套接字(Socket)概念 套接字(Socket)是通信的基石,是支持TCP/IP协议的...

网友评论

      本文标题:JDK源码-Socket套接字系列

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