隨筆 - 312, 文章 - 14, 評論 - 1393, 引用 - 0

          導航

          <2009年8月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          公告

          關注我的新浪微博

          我的著作









          常用鏈接

          留言簿(126)

          我參與的團隊

          隨筆分類(818)

          隨筆檔案(310)

          文章分類(1)

          文章檔案(8)

          相冊

          ADSL、3G查詢

          CSDN

          eclipse

          ibm

          Java EE

          Linux

          Web

          云服務

          代理網站

          關注的網站

          協議

          喜歡的Blog

          國內廣告平臺

          圖書出版

          在線培訓

          開發工具

          微博客戶端

          手機鈴聲

          操作系統

          • ReactOS
          • 一個與windowXP/2003兼容的操作系統

          數學

          文件格式

          源碼資源

          移動(Mobile)

          編程語言

          英語學習

          最新隨筆

          搜索

          •  

          積分與排名

          • 積分 - 1974645
          • 排名 - 6

          最新評論

          閱讀排行榜

          評論排行榜

          Java網絡編程從入門到精通(29):服務端Socket的選項

          本文為原創,如需轉載,請注明作者和出處,謝謝!

          上一篇:Java網絡編程從入門到精通(28):獲取ServerSocket信息的方法及FTP原理

          ServerSocket類有以下三個選項:

          1.       SO_TIMEOUT 設置accept方法的超時時間。

          2.       SO_REUSEADDR設置服務端同一個端口是否可以多次綁定。

          3.       SO_RECBUF設置接收緩沖區的大小。

          一、SO_TIMEOUT選項

          可以通過SeverSocket類的兩個方法(setSoTimeoutgetSoTimeout)來設置和獲得SO_TIMEOUT選項的值,這兩個方法的定義如下:

          public synchronized void setSoTimeout(int timeout) throws SocketException
          public synchronized int getSoTimeout() throws IOException

          setSoTimeout方法的timeout參數表示accept方法的超時時間,單位是毫秒。在通常情況下,ServerSocket類的accept方法在等待客戶端請求時處于無限等待狀態。如HTTP服務器在沒有用戶訪問網頁時會一直等待用戶的請求。一般不需要對服務端設置等待客戶端請求超時,但在某些特殊情況下,服務端規定客戶端必須在一定時間內向服務端發出請求,這時就要設置等待客戶端請求超時,也就是accept方法的超時時間。當設置客戶端請求超時后,accept方法在等待超時時間后拋出一個SocketTimeoutException異常。下面的代碼演示了如何設置和獲得SO_TIMEOUT選項的值,超時時間通過命令行參數方式傳入AcceptTimeout。

          package server;

          import java.net.*;

          public class AcceptTimeout
          {
              
          public static void main(String[] args) throws Exception
              {
                  
          if (args.length == 0)
                      
          return;
                  ServerSocket serverSocket 
          = new ServerSocket(1234);
                  
          int timeout = Integer.parseInt(args[0]);
                  
                  serverSocket.setSoTimeout(Integer.parseInt(args[
          0]));
                  System.out.println((timeout 
          > 0? "accept方法將在"
                          
          + serverSocket.getSoTimeout() + "毫秒后拋出異常!" : "accept方法永遠阻塞!");;
                  serverSocket.accept();
              }
          }

          執行下面的命令:

          java server.AcceptTimeout 3000

          運行結果:

          accept方法將在3000毫秒后拋出異常!
          Exception in thread 
          "main" java.net.SocketTimeoutException: Accept timed out
              at java.net.PlainSocketImpl.socketAccept(Native Method)
              at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:
          384)
              at java.net.ServerSocket.implAccept(ServerSocket.java:
          450)
              at java.net.ServerSocket.accept(ServerSocket.java:
          421)
              at chapter5.AcceptTimeout.main(AcceptTimeout.java:
          16)

          setSoTimeout方法可以在ServerSocket對象綁定端口之前調用,也以在綁定端口之后調用。如下面的代碼也是正確的:

          ServerSocket serverSocket = new ServerSocket();
          serverSocket.setSoTimeout(
          3000);
          serverSocket.bind(
          new InetSocketAddress(1234));

          二、SO_REUSEADDR選項

          SO_REUSEADDR選項決定了一個端口是否可以被綁定多次??梢酝ㄟ^SeverSocket類的兩個方法(setReuseAddresgetReuseAddress)來設置和獲得SO_TIMEOUT選項的值,這兩個方法的定義如下:

          public void setReuseAddress(boolean on) throws SocketException 
          public boolean getReuseAddress() throws SocketException

          在大多數操作系統中都不允許一個端口被多次綁定。如果一個ServerSocket對象綁定了已經被占用的端口,那么ServerSocket的構造方法或bind方法就會拋出一個BindException異常。

          Java提供這個選項的主要目的是為了防止由于頻繁綁定釋放一個固定端口而使系統無法正常工作。當ServerSocket對象關閉后,如果ServerSocket對象中仍然有未處理的數據,那么它所綁定的端口可能在一段時間內不會被釋放。這就會造成其他的ServerSocket對象無法綁定這個端口。在設置這個選項時,如果某個端口是第一次被綁定,無需調用setReuseAddress方法,而再次綁定這個端口時,必須使用setReuseAddress方法將這個選項設為true。而且這個方法必須在調用bind方法之前調用。下面的代碼演示了如何設置和獲得這個選項的值:

          package server;

          import java.net.*;

          public class TestReuseAddr1
          {
              
          public static void main(String[] args) throws Exception
              {
                  ServerSocket serverSocket1 
          = new ServerSocket(1234);
                  System.out.println(serverSocket1.getReuseAddress());
                  
                  ServerSocket serverSocket2 
          = new ServerSocket();
                  serverSocket2.setReuseAddress(
          true);
                  serverSocket2.bind(
          new InetSocketAddress(1234));
                  
                  ServerSocket serverSocket3 
          = new ServerSocket();
                  serverSocket3.setReuseAddress(
          true);
                  serverSocket3.bind(
          new InetSocketAddress(1234));
              }
          }

          運行結果:false

          在上面代碼中第一次綁定端口1234,因此,serverSocket1對象無需設置SO_REUSEADDR選項(這個選項在大多數操作系統上的默認值是false)。而serverSocket2serverSocket3并不是第一次綁定端口1234,因此,必須設置這兩個對象的SO_REUSEADDR值為true。在設置SO_REUSEADDR選項時要注意,必須在ServerSocket對象綁定端口之前設置這個選項。

              也許有的讀者可能有這樣的疑問。如果多個ServerSocket對象同時綁定到一個端口上,那么當客戶端向這個端口發出請求時,該由哪個ServerSocket對象來接收客戶端請求呢?在給出答案之前,讓我們先看看下面的代碼的輸出結果是什么。

          package server;

          import java.net.*;

          public class TestReuseAddr2 extends Thread
          {
              String s;
              
          public void run()
              {
                  
          try
                  {
                      ServerSocket serverSocket 
          = new ServerSocket();
                      serverSocket.setReuseAddress(
          true);
                      serverSocket.bind(
          new InetSocketAddress(1234));
                      Socket socket 
          = serverSocket.accept();
                      System.out.println(s 
          + "" + socket);
                      socket.close();
                      serverSocket.close();
                  }
                  
          catch (Exception e)
                  {
                  }
              }
              
          public TestReuseAddr2(String s)
              {
                  
          this.s = s;
              }
              
          public static void main(String[] args)
              {
                  
          for (int i = 1; i <= 5; i++)
                      
          new TestReuseAddr2("ServerSocket" + i).start();
              }
          }

          執行下面的命令:

          java server.TestReuseAddr2


              連續執行5次下面的命令:

          telnet localhost 1234

          執行結果:

          ServerSocket1:Socket[addr=/127.0.0.1,port=11724,localport=1234]
          ServerSocket3:Socket[addr
          =/127.0.0.1,port=11725,localport=1234]
          ServerSocket5:Socket[addr
          =/127.0.0.1,port=11726,localport=1234]
          ServerSocket2:Socket[addr
          =/127.0.0.1,port=11727,localport=1234]
          ServerSocket4:Socket[addr
          =/127.0.0.1,port=11728,localport=1234]

              上面的運行結果只是一種可能,如果多次按著上面的步驟操作,可能得到不同的運行結果。由此可以斷定,當多個ServerSocket對象同時綁定一個端口時,系統會隨機選擇一個ServerSocket對象來接收客戶端請求。但要注意,這個接收客戶端請求的ServerSocket對象必須關閉(如019行如示),才能輪到其他的ServerSocket對象接收客戶端請求。如果不關閉這個ServerSocket對象,那么其他的ServerSocket對象將永遠無法接收客戶端請求。讀者可以將 serverSocket.close()去掉,再執行上面操作步驟,看看會有什么結果。

          三、SO_RCVBUF選項

          可以通過SeverSocket類的兩個方法(setReceiveBufferSizegetReceiveBufferSize)來設置和獲得SO_RCVBUF選項的值,這兩個方法的定義如下:

          public synchronized void setReceiveBufferSize (int size) throws SocketException
          public synchronized int getReceiveBufferSize() throws SocketException

              其中size參數表示接收緩沖區的大小,單位是字節。設置了ServerSocket類的SO_RCVBUF選項,就相當于設置了Socket對象的接收緩沖區大小。這個Socket對象是由accept返回的。下面積代碼演示了如何使用這兩個方法來設置和獲得接收緩沖區的大?。?br />
          package server;

          import java.net.*;

          public class TestReceiveBufferSize
          {
              
          public static void main(String[] args) throws Exception
              {
                  ServerSocket serverSocket 
          = new ServerSocket(1234);
                  serverSocket.setReceiveBufferSize(
          2048); // 將接收緩沖區設為2K
                  while (true)
                  {
                      Socket socket 
          = serverSocket.accept();
                      
          // 如果客戶端請求使用的是本地IP地址,重新將Socket對象的接
                      
          // 收緩沖區設為1K            
                      if (socket.getInetAddress().isLoopbackAddress())
                          socket.setReceiveBufferSize(
          1024);
                      System.out.println(
          "serverSocket:"
                                      
          + serverSocket.getReceiveBufferSize());
                      System.out.println(
          "socket:" + socket.getReceiveBufferSize());
                      socket.close();
                  }
              }
          }

          執行如下命令:
          java server.TestReceiveBufferSize
          執行如下三個命令 (192.168.18.100為本機IP地址):
          telnet 192.168.18.100 1234
          telnet localhost 
          1234
          telnet 
          192.168.18.100 1234
          運行結果:
          serverSocket:2048
          socket:
          2048
          serverSocket:
          2048
          socket:
          1024
          serverSocket:
          2048
          socket:
          2048

          從上面的運行結果可以看出,在執行telnet localhost 1234命令后,由于localhost是本地地址,因此程序通過Socket對象的接收緩沖區設為1024,而在執行其他兩條命令后,由于192.168.18.100不是本機地址,所以Socket對象的接收緩沖區仍然保留著serverSocket的值:2048。因此,我們可以得出一個結論,設置ServerSocket對象的接收緩沖區就相當于設置了所有從accept返回的Socket對象的接收緩沖區,只要不單獨對某個Socket對象重新設置,這些Socket對象的接收緩沖區就會都保留這個值。

          無論在ServerSocket對象綁定到端口之前還是之后設置SO_RCVBUF選項都有效,但如果要設置大于64K的接收緩沖區時,就必須在ServerSocket對象綁定端口之前設置SO_RCVBUF選項。如下面的代碼將接收緩沖區的大小設為100K。

          ServerSocket serverSocket = new ServereSocket();
          serverSocket. setReceiveBufferSize(
          100 * 1024);  // 將接收緩沖區的大小設為100K。
          serverSocket.bind(new InetSocketAddress(1234));

             一般情況下,并不需要設置這個選項,它的默認值(一般為8K)足可以滿足大多數情況。但有時為了適應特殊的需要,必須更改接收緩沖區的值。如在一些網絡游戲中,需要實時地向服務器傳送各種動作、指令信息。這就需要將接收緩沖區設小一點。這樣可以在一定程度上增加游戲客戶端的靈敏度。如果需要傳送大量的數據,如HTTPFTP等協議。這就需要較大的接收緩沖區。

          四、設置ServerSocket的性能偏好 

          Java SE5.0及以上版本中為ServerSocket類增加了一個setPerformancePreferences方法。這個和方法和Socket類中的setPerformancePreferences的作用一樣,用來設置連接時間、延遲和帶寬的相對重要性。setPerformancePerferences方法的定義如下:

          public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)

          下一篇:Java網絡編程從入門到精通(30):定制accept方法





          Android開發完全講義(第2版)(本書版權已輸出到臺灣)

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



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


          新浪微博:http://t.sina.com.cn/androidguy   昵稱:李寧_Lining

          posted on 2009-08-12 14:50 銀河使者 閱讀(3212) 評論(1)  編輯  收藏 所屬分類: java 、 原創 、網絡編程

          評論

          # re: Java網絡編程從入門到精通(29):服務端Socket的選項  回復  更多評論   

          現在已經很少接觸了,跟著學習
          2009-08-12 15:14 | 凡人修仙傳最新
          主站蜘蛛池模板: 屏南县| 鲜城| 禄劝| 乌鲁木齐县| 宁乡县| 定西市| 扶沟县| 滨海县| 临邑县| 阿拉善盟| 伊吾县| 二手房| 方正县| 武川县| 肃南| 宜兰市| 镇雄县| 石屏县| 长沙市| 化德县| 喀什市| 万源市| 仁怀市| 长兴县| 集安市| 佛坪县| 淮南市| 洛扎县| 太谷县| 商南县| 涞水县| 雷州市| 托里县| 涞源县| 双辽市| 浮梁县| 商丘市| 郓城县| 潢川县| 莆田市| 宜春市|