<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    隨筆 - 312, 文章 - 14, 評(píng)論 - 1393, 引用 - 0
    數(shù)據(jù)加載中……

    Java網(wǎng)絡(luò)編程從入門(mén)到精通(25):創(chuàng)建ServerSocket對(duì)象

    本文為原創(chuàng),如需轉(zhuǎn)載,請(qǐng)注明作者和出處,謝謝!

    上一篇:Java網(wǎng)絡(luò)編程從入門(mén)到精通(24):實(shí)現(xiàn)HTTP斷點(diǎn)續(xù)傳下載工具(附源代碼)

    ServerSocket類(lèi)的構(gòu)造方法有四種重載形式,它們的定義如下:

    public ServerSocket() throws IOException
    public ServerSocket(int port) throws IOException
    public ServerSocket(int port, int backlog) throws IOException
    public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException

        在上面的構(gòu)造方法中涉及到了三個(gè)參數(shù):portbacklogbindAddr。其中portServerSocket對(duì)象要綁定的端口,backlog是請(qǐng)求隊(duì)列的長(zhǎng)度,bindAddrServerSocket對(duì)象要綁定的IP地址。

    一、通過(guò)構(gòu)造方法綁定端口

    通過(guò)構(gòu)造方法綁定端口是創(chuàng)建ServerSocket對(duì)象最常用的方式。可以通過(guò)如下的構(gòu)造方法來(lái)綁定端口:

    public ServerSocket(int port) throws IOException

    如果port參數(shù)所指定的端口已經(jīng)被綁定,構(gòu)造方法就會(huì)拋出IOException異常。但實(shí)際上拋出的異常是BindException。從圖4.2異常類(lèi)繼承關(guān)系圖可以看出,所有和網(wǎng)絡(luò)有關(guān)的異常都是IOException類(lèi)的子類(lèi)。因此,為了ServerSocket構(gòu)造方法還可以拋出其他的異常,就使用了IOException

    如果port的值為0,系統(tǒng)就會(huì)隨機(jī)選取一個(gè)端口號(hào)。但隨機(jī)選取的端口意義不大,因?yàn)榭蛻?hù)端在連接服務(wù)器時(shí)需要明確知道服務(wù)端程序的端口號(hào)。可以通過(guò)ServerSockettoString方法輸出和ServerSocket對(duì)象相關(guān)的信息。下面的代碼輸入了和ServerSocket對(duì)象相關(guān)的信息。

    ServerSocket serverSocket = new ServerSocket(1320);
    System.out.println(serverSocket);

    運(yùn)行結(jié)果:

    ServerSocket[addr=0.0.0.0/0.0.0.0,port=0,localport=1320]

    上面的輸出結(jié)果中的addr是服務(wù)端綁定的IP地址,如果未綁定IP地址,這個(gè)值是0.0.0.0,在這種情況下,ServerSocket對(duì)象將監(jiān)聽(tīng)服務(wù)端所有網(wǎng)絡(luò)接口的所有IP地址。port永遠(yuǎn)是0localportServerSocket綁定的端口,如果port值為0(不是輸出結(jié)果的port,是ServerSocket構(gòu)造方法的參數(shù)port),localport是一個(gè)隨機(jī)選取的端口號(hào)。

    在操作系統(tǒng)中規(guī)定1 ~ 1023為系統(tǒng)使用的端口號(hào)。端口號(hào)的最小值是1,最大值是65535。在Windows中用戶(hù)編寫(xiě)的程序可以綁定端口號(hào)小于1024的端口,但在Linux/Unix下必須使用root登錄才可以綁定小于1024的端口。在前面的文章中曾使用Socket類(lèi)來(lái)判斷本機(jī)打開(kāi)了哪些端口,其實(shí)使用ServerSocket類(lèi)也可以達(dá)到同樣的目的。基本原理是用ServerSocket來(lái)綁定本機(jī)的端口,如果綁定某個(gè)端口時(shí)拋出BindException異常,就說(shuō)明這個(gè)端口已經(jīng)打開(kāi),反之則這個(gè)端口未打開(kāi)。

    package server;

    import java.net.*;

    public class ScanPort
    {
        
    public static void main(String[] args)
        {
            
    if (args.length == 0)
                
    return;
            
    int minPort = 0, maxPort = 0;
            String ports[] 
    = args[0].split("[-]");
            minPort 
    = Integer.parseInt(ports[0]);
            maxPort 
    = (ports.length > 1? Integer.parseInt(ports[1]) : minPort;
            
    for (int port = minPort; port <= maxPort; port++)
                
    try
                {
                    ServerSocket serverSocket 
    = new ServerSocket(port);
                    serverSocket.close();
                }
                
    catch (Exception e)
                {
                    System.err.println(e.getClass());
                    System.err.println(
    "端口" + port + "已經(jīng)打開(kāi)!");
                }
        }
    }

    在上面的代碼中輸出了創(chuàng)建ServerSocket對(duì)象時(shí)拋出的異常類(lèi)的信息。ScanPort通過(guò)命令行參數(shù)將待掃描的端口號(hào)范圍傳入程序,參數(shù)格式為:minPort-maxPort,如果只輸入一個(gè)端口號(hào),ScanPort程序只掃描這個(gè)端口號(hào)。

             測(cè)試

    java server.ScanPort 1-1023
        運(yùn)行結(jié)果
    class java.net.BindException
    端口80已經(jīng)打開(kāi)!
    class java.net.BindException
    端口135已經(jīng)打開(kāi)!

    二、設(shè)置請(qǐng)求隊(duì)列的長(zhǎng)度

    在編寫(xiě)服務(wù)端程序時(shí),一般會(huì)通過(guò)多線程來(lái)同時(shí)處理多個(gè)客戶(hù)端請(qǐng)求。也就是說(shuō),使用一個(gè)線程來(lái)接收客戶(hù)端請(qǐng)求,當(dāng)接到一個(gè)請(qǐng)求后(得到一個(gè)Socket對(duì)象),會(huì)創(chuàng)建一個(gè)新線程,將這個(gè)客戶(hù)端請(qǐng)求交給這個(gè)新線程處理。而那個(gè)接收客戶(hù)端請(qǐng)求的線程則繼續(xù)接收客戶(hù)端請(qǐng)求,這個(gè)過(guò)程的實(shí)現(xiàn)代碼如下:


    ServerSocket serverSocket = new ServerSocket(1234);   // 綁定端口
    // 處理其他任務(wù)的代碼
    while(true)
    {
        Socket socket = serverSocket.accept(); // 等待接收客戶(hù)端請(qǐng)求
       
    // 處理其他任務(wù)的代碼
        new ThreadClass(socket).start();   // 創(chuàng)建并運(yùn)行處理客戶(hù)端請(qǐng)求的線程
    }

    上面代碼中ThreadClass類(lèi)是Thread類(lèi)的子類(lèi),這個(gè)類(lèi)的構(gòu)造方法有一個(gè)Socket類(lèi)型的參數(shù),可以通過(guò)構(gòu)造方法將Socket對(duì)象傳入ThreadClass對(duì)象,并在ThreadClass對(duì)象的run方法中處理客戶(hù)端請(qǐng)求。這段代碼從表面上看好象是天衣無(wú)縫,無(wú)論有多少客戶(hù)端請(qǐng)求,只要服務(wù)器的配置足夠高,就都可以處理。但仔細(xì)思考上面的代碼,我們可能會(huì)發(fā)現(xiàn)一些問(wèn)題。如果在第2行和第6行有足夠復(fù)雜的代碼,執(zhí)行時(shí)間也比較長(zhǎng),這就意味著服務(wù)端程序無(wú)法及時(shí)響應(yīng)客戶(hù)端的請(qǐng)求。

    假設(shè)第2行和第6行的代碼是Thread.sleep(3000),這將使程序延遲3秒。那么在這3秒內(nèi),程序不會(huì)執(zhí)行accept方法,因此,這段程序只是將端口綁定到了1234上,并未開(kāi)始接收客戶(hù)端請(qǐng)求。如果在這時(shí)一個(gè)客戶(hù)端向端口1234發(fā)來(lái)了一個(gè)請(qǐng)求,從理論上講,客戶(hù)端應(yīng)該出現(xiàn)拒絕連接錯(cuò)誤,但客戶(hù)端卻顯示連接成功。究其原因,就是這節(jié)要討論的請(qǐng)求隊(duì)列在起作用。

    在使用ServerSocket對(duì)象綁定一個(gè)端口后,操作系統(tǒng)就會(huì)為這個(gè)端口分配一個(gè)先進(jìn)先出的隊(duì)列(這個(gè)隊(duì)列長(zhǎng)度的默認(rèn)值一般是50),這個(gè)隊(duì)列用于保存未處理的客戶(hù)端請(qǐng)求,因此叫請(qǐng)求隊(duì)列。而ServerSocket類(lèi)的accept方法負(fù)責(zé)從這個(gè)隊(duì)列中讀取未處理的客戶(hù)端請(qǐng)求。如果請(qǐng)求隊(duì)列為空,accept則處于阻塞狀態(tài)。每當(dāng)客戶(hù)端向服務(wù)端發(fā)來(lái)一個(gè)請(qǐng)求,服務(wù)端會(huì)首先將這個(gè)客戶(hù)端請(qǐng)求保存在請(qǐng)求隊(duì)列中,然后accept再?gòu)恼?qǐng)求隊(duì)列中讀取。這也可以很好地解釋為什么上面的代碼在還未執(zhí)行到accept方法時(shí),仍然可以接收一定數(shù)量的客戶(hù)端請(qǐng)求。如果請(qǐng)求隊(duì)列中的客戶(hù)端請(qǐng)求數(shù)達(dá)到請(qǐng)求隊(duì)列的最大容量時(shí),服務(wù)端將無(wú)法再接收客戶(hù)端請(qǐng)求。如果這時(shí)客戶(hù)端再向服務(wù)端發(fā)請(qǐng)求,客戶(hù)端將會(huì)拋出一個(gè)SocketException異常。

    ServerSocket類(lèi)有兩個(gè)構(gòu)造方法可以使用backlog參數(shù)重新設(shè)置請(qǐng)求隊(duì)列的長(zhǎng)度。在以下幾種情況,仍然會(huì)采用操作系統(tǒng)限定的請(qǐng)求隊(duì)列的最大長(zhǎng)度:

    •  backlog的值小于等于0
    • backlog的值大于操作系統(tǒng)限定的請(qǐng)求隊(duì)列的最大長(zhǎng)度。
    • ServerSocket構(gòu)造方法中未設(shè)置backlog參數(shù)。

    下面積代碼演示了請(qǐng)求隊(duì)列的一些特性,請(qǐng)求隊(duì)列長(zhǎng)度通過(guò)命令行參數(shù)傳入SetRequestQueue

    package server;

    import java.net.*;

    class TestRequestQueue
    {
        
    public static void main(String[] args) throws Exception
        {
            
    for (int i = 0; i < 10; i++)
            {
                Socket socket 
    = new Socket("localhost"1234);
                socket.getOutputStream().write(
    1);
                System.out.println(
    "已經(jīng)成功創(chuàng)建第" + String.valueOf(i + 1+ "個(gè)客戶(hù)端連接!");
            }
        }
    }
    public class SetRequestQueue
    {
        
    public static void main(String[] args) throws Exception
        {
            
    if (args.length == 0)
                
    return;
            
    int queueLength = Integer.parseInt(args[0]);
            ServerSocket serverSocket 
    = new ServerSocket(1234, queueLength);
            System.out.println(
    "端口(1234)已經(jīng)綁定,請(qǐng)按回車(chē)鍵開(kāi)始處理客戶(hù)端請(qǐng)求!");
            System.in.read();
            
    int n = 0;
            
    while (true)
            {
                System.out.println(
    "<準(zhǔn)備接收第" + (++n) + "個(gè)客戶(hù)端請(qǐng)求!");
                Socket socket 
    = serverSocket.accept();
                System.out.println(
    "正在處理第" + n + "個(gè)客戶(hù)端請(qǐng)求");
                Thread.sleep(
    3000);
                System.out.println(
    "" + n + "個(gè)客戶(hù)端請(qǐng)求已經(jīng)處理完畢!>");
            }
        }
    }

       測(cè)試(按著以下步驟操作)
    1. 執(zhí)行如下命令(在執(zhí)行這條命令后,先不要按回車(chē)鍵):

    java server.SetRequestQueue 2


       運(yùn)行結(jié)果:

    端口(1234)已經(jīng)綁定,請(qǐng)按回車(chē)鍵開(kāi)始處理客戶(hù)端請(qǐng)求!
        2. 執(zhí)行如下命令:  

    java server.TestRequestQueue

    運(yùn)行結(jié)果:

    已經(jīng)成功創(chuàng)建第1個(gè)客戶(hù)端連接!
    已經(jīng)成功創(chuàng)建第2個(gè)客戶(hù)端連接!
    Exception in thread 
    "main" java.net.SocketException: Connection reset by peer: socket write error
                           at java.net.SocketOutputStream.socketWrite0(Native Method)
                           at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:
    92)
                           at java.net.SocketOutputStream.write(SocketOutputStream.java:
    115)
                           at server.TestRequestQueue.main(SetRequestQueue.java:
    12)


        3. 按回車(chē)鍵繼續(xù)執(zhí)行SetRequestQueue后,運(yùn)行結(jié)果如下:

    端口(1234)已經(jīng)綁定,請(qǐng)按回車(chē)鍵開(kāi)始處理客戶(hù)端請(qǐng)求!
    <準(zhǔn)備接收第1個(gè)客戶(hù)端請(qǐng)求!
    正在處理第1個(gè)客戶(hù)端請(qǐng)求
    第1個(gè)客戶(hù)端請(qǐng)求已經(jīng)處理完畢!>
    <準(zhǔn)備接收第2個(gè)客戶(hù)端請(qǐng)求!
    正在處理第2個(gè)客戶(hù)端請(qǐng)求
    第2個(gè)客戶(hù)端請(qǐng)求已經(jīng)處理完畢!>
    <準(zhǔn)備接收第3個(gè)客戶(hù)端請(qǐng)求!

        從第二步的運(yùn)行結(jié)果可以看出,當(dāng)TestRequestQueue創(chuàng)建兩個(gè)Socket連接之后,服務(wù)端的請(qǐng)求隊(duì)列已滿,并且服務(wù)端暫時(shí)無(wú)法繼續(xù)執(zhí)行(由于System.in.read()的原因而暫停程序的執(zhí)行,等待用戶(hù)的輸入)。因此,服務(wù)端程序無(wú)法再接收客戶(hù)端請(qǐng)求。這時(shí)TestRequestQueue拋出了一個(gè)SocketException異常。在TestRequestQueue已經(jīng)創(chuàng)建成功的兩個(gè)Socket連接已經(jīng)保存在服務(wù)端的請(qǐng)求隊(duì)列中。在這時(shí)按任意鍵繼續(xù)執(zhí)行SetRequestQueueaccept方法就會(huì)從請(qǐng)求隊(duì)列中將這兩個(gè)客戶(hù)端請(qǐng)求隊(duì)列中依次讀出來(lái)。從第三步的運(yùn)行結(jié)果可以看出,服務(wù)端處理完這兩個(gè)請(qǐng)求后(一個(gè)<…>包含的就是一個(gè)處理過(guò)程),請(qǐng)求隊(duì)列為空,這時(shí)accept處理阻塞狀態(tài),等待接收第三個(gè)客戶(hù)端請(qǐng)求。如果這時(shí)再運(yùn)行TestRequestQueue,服務(wù)端會(huì)接收幾個(gè)客戶(hù)端請(qǐng)求呢?如果將請(qǐng)求隊(duì)列的長(zhǎng)度設(shè)為大于10的數(shù),TestRequestQueue的運(yùn)行結(jié)果會(huì)是什么呢?讀者可以自己做一下這些實(shí)驗(yàn),看看和自己認(rèn)為的結(jié)果是否一致。

    三、綁定IP地址

    在有多個(gè)網(wǎng)絡(luò)接口或多個(gè)IP地址的計(jì)算機(jī)上可以使用如下的構(gòu)造方法將服務(wù)端綁定在某一個(gè)IP地址上:

    public ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException

    bindAddr參數(shù)就是要綁定的IP地址。如果將服務(wù)端綁定到某一個(gè)IP地址上,就只有可以訪問(wèn)這個(gè)IP地址的客戶(hù)端才能連接到服務(wù)器上。如一臺(tái)機(jī)器上有兩塊網(wǎng)卡,一塊網(wǎng)卡連接內(nèi)網(wǎng),另一塊連接外網(wǎng)。如果用Java實(shí)現(xiàn)一個(gè)Email服務(wù)器,并且只想讓內(nèi)網(wǎng)的用戶(hù)使用它。就可以使用這個(gè)構(gòu)造方法將ServerSocket對(duì)象綁定到連接內(nèi)網(wǎng)的IP地址上。這樣外網(wǎng)就無(wú)法訪問(wèn)Email服務(wù)器了。可以使用如下代碼來(lái)綁定IP地址:

    ServerSocket serverSocket = new
    ServerSocket(
    12340, InetAddress.getByName("192.168.18.10"));

        上面的代碼將IP地址綁定到了192.168.18.10上,因此,服務(wù)端程序只能使用綁定了這個(gè)IP地址的網(wǎng)絡(luò)接口進(jìn)行通訊。
    四、默認(rèn)構(gòu)造方法的使用

        除了使用ServerSocket類(lèi)的構(gòu)造方法綁定端口外,還可以用ServerSocketbind方法來(lái)完成構(gòu)造方法所做的工作。要想使用bind方法,必須得用ServerSocket類(lèi)的默認(rèn)構(gòu)造方法(沒(méi)有參數(shù)的構(gòu)造方法)來(lái)創(chuàng)建ServerSocket對(duì)象。bind方法有兩個(gè)重載形式,它們的定義如下:

    public void bind(SocketAddress endpoint) throws IOException
    public void bind(SocketAddress endpoint, int backlog) throws IOException

         bind方法不僅可以綁定端口,也可以設(shè)置請(qǐng)求隊(duì)列的長(zhǎng)度以及綁定IP地址。bind方法的作用是為了在建立ServerSocket對(duì)象后設(shè)置ServerSocket類(lèi)的一些選項(xiàng)。而這些選項(xiàng)必須在綁定端口之前設(shè)置,一但綁定了端口后,再設(shè)置這些選項(xiàng)將不再起作用。下面的代碼演示了bind方法的使用及如何設(shè)置ServerSocket類(lèi)的選項(xiàng)。

    ServerSocket serverSocket1 = new ServerSocket();
    serverSocket1.setReuseAddress(true);
    serverSocket1.bind(new InetSocketAddress(1234));
    ServerSocket serverSocket2 = new ServerSocket();
    serverSocket2.setReuseAddress(true);
    serverSocket2.bind(new InetSocketAddress("192.168.18.10"1234));
    ServerSocket serverSocket3 = new ServerSocket();
    serverSocket3.setReuseAddress(true);
    serverSocket3.bind(new InetSocketAddress("192.168.18.10"1234), 30);       

    在上面的代碼中設(shè)置了SO_REUSEADDR 選項(xiàng)(這個(gè)選項(xiàng)將在后面的文章中詳細(xì)討論)。如果使用下面的代碼,這個(gè)選項(xiàng)將不起作用。

    ServerSocket serverSocket3 = new ServerSocket(1234);
    serverSocket3.setReuseAddress(
    true);

    在第6行綁定了IP地址和端口。使用構(gòu)造方法是無(wú)法得到這個(gè)組合的(想綁定IP地址,必須得設(shè)置backlog參數(shù)),因此,bind方法比構(gòu)造方法更靈活。

    下一篇:
    Java網(wǎng)絡(luò)編程從入門(mén)到精通(26):在服務(wù)端接收和發(fā)送數(shù)據(jù)





    Android開(kāi)發(fā)完全講義(第2版)(本書(shū)版權(quán)已輸出到臺(tái)灣)

    http://product.dangdang.com/product.aspx?product_id=22741502



    Android高薪之路:Android程序員面試寶典 http://book.360buy.com/10970314.html


    新浪微博:http://t.sina.com.cn/androidguy   昵稱(chēng):李寧_Lining

    posted on 2009-07-12 19:40 銀河使者 閱讀(3766) 評(píng)論(2)  編輯  收藏 所屬分類(lèi): java 原創(chuàng)網(wǎng)絡(luò)編程

    評(píng)論

    # re: Java網(wǎng)絡(luò)編程從入門(mén)到精通(25):創(chuàng)建ServerSocket對(duì)象  回復(fù)  更多評(píng)論   

    嗯,好,值得學(xué)習(xí),多多研究~
    2009-07-12 21:25 | 瑜伽館

    # re: Java網(wǎng)絡(luò)編程從入門(mén)到精通(25):創(chuàng)建ServerSocket對(duì)象  回復(fù)  更多評(píng)論   

    很好,拜讀了!
    2009-07-18 23:56 | 心夢(mèng)帆影
    主站蜘蛛池模板: 亚洲一区爱区精品无码| 日韩亚洲Av人人夜夜澡人人爽| 久久精品7亚洲午夜a| 色偷偷女男人的天堂亚洲网| 青青青视频免费观看| 222www免费视频| 免费国产高清视频| 亚洲精品456在线播放| 成年网站免费入口在线观看 | 久久久高清免费视频| 亚洲国产精品综合久久一线| 亚洲国产精品白丝在线观看 | 曰韩无码AV片免费播放不卡 | 亚洲欧美日韩一区二区三区在线| 两性色午夜视频免费播放| 大陆一级毛片免费视频观看| 亚洲AV本道一区二区三区四区| 精品免费AV一区二区三区| 免费福利视频导航| 亚洲色中文字幕无码AV| 久久亚洲精品无码gv| 在线观看免费视频资源| 在线精品亚洲一区二区小说| 亚洲AV无码专区在线观看成人| 美女内射毛片在线看免费人动物| 国产成人精品亚洲精品| 亚洲AV无码一区二区三区久久精品 | 日韩大片免费观看视频播放| av无码国产在线看免费网站| 国产亚洲av片在线观看16女人| 精品在线免费视频| 成人免费视频软件网站| 亚洲综合区图片小说区| 最近免费中文字幕中文高清 | 四虎1515hh永久久免费| 亚洲αv在线精品糸列| 一级毛片大全免费播放下载| 日韩中文无码有码免费视频 | 国产 亚洲 中文在线 字幕| 5g影院5g天天爽永久免费影院| 久久亚洲国产中v天仙www|