隨筆 - 312, 文章 - 14, 評論 - 1393, 引用 - 0
          數據加載中……

          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返回的。下面積代碼演示了如何使用這兩個方法來設置和獲得接收緩沖區的大小:

          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 | 凡人修仙傳最新
          主站蜘蛛池模板: 黄骅市| 马山县| 大英县| 泽州县| 马公市| 安宁市| 安徽省| 台江县| 邯郸县| 南阳市| 仲巴县| 阳新县| 韶关市| 六枝特区| 绥德县| 成都市| 荃湾区| 巴楚县| 乌鲁木齐县| 思南县| 方城县| 潞城市| 清苑县| 峨山| 黄陵县| 旌德县| 淳化县| 青田县| 丹巴县| 辽宁省| 麻栗坡县| 上饶县| 和政县| 灯塔市| 固始县| 织金县| 桑日县| 乌什县| 共和县| 阿克| 阿图什市|