JDK源碼-Socket套接字系列

套接字基本流程

ServerSocket

//指定端口的構(gòu)造器
public ServerSocket(int port) throws IOException {
        this(port, 50, null);
    }
上個構(gòu)造器調(diào)用了該方法:port指定了服務(wù)器端socket監(jiān)聽的端口
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指服務(wù)器端處理請求的隊列最大的長度,默認(rèn)為50.
InetSocketAddress是SocketAddress的實現(xiàn)類.封裝了hostname,InetAddress,port等參數(shù).

想了解InetAddress可以參看這篇文章:http://www.reibang.com/p/0ba3a391de03

  • 通過bind方法,對該ip+port進(jìn)行監(jiān)聽
public void bind(SocketAddress endpoint, int backlog) throws IOException {
        if (isClosed())//判斷socket是否關(guān)閉
            throw new SocketException("Socket is closed");
        if (!oldImpl && isBound())//判斷socket的監(jiān)聽狀態(tài)
            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;
        }
    }
這里調(diào)用SocketImpl的bind方法: getImpl().bind(epoint.getAddress(), epoint.getPort());
SocketImpl的抽象實現(xiàn)類: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)調(diào)用了默認(rèn)抽象實現(xiàn)類AbstractPlainSocketImpl的accept:
protected void accept(SocketImpl s) throws IOException {
        //先獲取fd
        acquireFD();
        try {
            //該方法還是調(diào)用了本地方法:PlainSocketImpl:native void socketAccept(SocketImpl s) throws IOException;
            socketAccept(s);
        } finally {
            releaseFD();
        }
    }
//添加synchronized鎖,保證多線程情況的數(shù)據(jù)一致性
FileDescriptor acquireFD() {
        synchronized (fdLock) {
            fdUseCount++;
            return fd;
        }
    }
  • 需要特別注意的AbstractPlainSocketImpl有兩個參數(shù):fdLock對象是為了給每個操作socket的方法,添加同步鎖synchronized鎖,fdUseCount用來記錄當(dāng)前服務(wù)器端socket保持連接的線程數(shù).因為在關(guān)閉ServerSocket的時候,需要將socket中的數(shù)據(jù)清空之后,才能關(guān)閉.
 /* 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();
                    }
                }
            }
        }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子坎缭,更是在濱河造成了極大的恐慌屈芜,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,194評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件喻粹,死亡現(xiàn)場離奇詭異蟆融,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)守呜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,058評論 2 385
  • 文/潘曉璐 我一進(jìn)店門型酥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人查乒,你說我怎么就攤上這事弥喉。” “怎么了侣颂?”我有些...
    開封第一講書人閱讀 156,780評論 0 346
  • 文/不壞的土叔 我叫張陵档桃,是天一觀的道長。 經(jīng)常有香客問我憔晒,道長藻肄,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,388評論 1 283
  • 正文 為了忘掉前任拒担,我火速辦了婚禮嘹屯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘从撼。我一直安慰自己州弟,他們只是感情好钧栖,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,430評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著婆翔,像睡著了一般拯杠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上啃奴,一...
    開封第一講書人閱讀 49,764評論 1 290
  • 那天潭陪,我揣著相機(jī)與錄音,去河邊找鬼最蕾。 笑死依溯,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瘟则。 我是一名探鬼主播黎炉,決...
    沈念sama閱讀 38,907評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼醋拧!你這毒婦竟也來了慷嗜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,679評論 0 266
  • 序言:老撾萬榮一對情侶失蹤丹壕,失蹤者是張志新(化名)和其女友劉穎洪添,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體雀费,經(jīng)...
    沈念sama閱讀 44,122評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡干奢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,459評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了盏袄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片忿峻。...
    茶點(diǎn)故事閱讀 38,605評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖辕羽,靈堂內(nèi)的尸體忽然破棺而出逛尚,到底是詐尸還是另有隱情,我是刑警寧澤刁愿,帶...
    沈念sama閱讀 34,270評論 4 329
  • 正文 年R本政府宣布绰寞,位于F島的核電站,受9級特大地震影響铣口,放射性物質(zhì)發(fā)生泄漏滤钱。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,867評論 3 312
  • 文/蒙蒙 一脑题、第九天 我趴在偏房一處隱蔽的房頂上張望件缸。 院中可真熱鬧,春花似錦叔遂、人聲如沸他炊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,734評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽痊末。三九已至蚕苇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凿叠,已是汗流浹背捆蜀。 一陣腳步聲響...
    開封第一講書人閱讀 31,961評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留幔嫂,地道東北人。 一個月前我還...
    沈念sama閱讀 46,297評論 2 360
  • 正文 我出身青樓誊薄,卻偏偏與公主長得像履恩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子呢蔫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,472評論 2 348

推薦閱讀更多精彩內(nèi)容

  • JAVA套接字之TCP編程 1 TCP協(xié)議 TCP是面向諒解的協(xié)議切心。所謂連接,就是兩個對等實體為進(jìn)行數(shù)據(jù)通信而進(jìn)行...
    yanzhelee閱讀 1,145評論 0 3
  • 一片吊、通信前提 兩臺計算機(jī)要進(jìn)行通信绽昏,就要滿足以下條件:1.兩臺主機(jī)要有唯一的標(biāo)識,也就是IP地址俏脊,用來表示它們所處...
    不知名的蛋撻閱讀 676評論 0 0
  • 1.import static是Java 5增加的功能,就是將Import類中的靜態(tài)方法全谤,可以作為本類的靜態(tài)方法來...
    XLsn0w閱讀 1,214評論 0 2
  • 面向?qū)ο笾饕槍γ嫦蜻^程。 面向過程的基本單元是函數(shù)爷贫。 什么是對象:EVERYTHING IS OBJECT(萬物...
    sinpi閱讀 1,046評論 0 4
  • 今天是周末认然,我像平日一樣去附近的商場敷面膜。我是走路去的漫萄,從我家到商場有一個地下商城卷员,我慢悠悠的提著裝面膜的袋子走...
    青荷妖妖閱讀 357評論 15 12