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

          Java網(wǎng)絡(luò)編程從入門到精通(16):客戶端套接字(Socket)的超時(shí)

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

          上一篇:Java網(wǎng)絡(luò)編程從入門到精通(15):為什么要使用SocketAddress來(lái)管理網(wǎng)絡(luò)地址

              客戶端套接字的超時(shí)(timeout)就是指在客戶端通過Socket和服務(wù)器進(jìn)行通訊的過程中,由于網(wǎng)絡(luò)延遲,網(wǎng)絡(luò)阻塞等原因,造成服務(wù)器并未及時(shí)響應(yīng)客戶端的一種現(xiàn)象。在一段時(shí)間后,客戶端由于未收到服務(wù)端的響應(yīng)而拋出一個(gè)超時(shí)錯(cuò)誤; 其中客戶端所等待的時(shí)間就是超時(shí)時(shí)間。

          由于生產(chǎn)超時(shí)錯(cuò)誤的一端都是被動(dòng)端;也就是說(shuō),這一端是在接收數(shù)據(jù),而不是發(fā)送數(shù)據(jù)。對(duì)于客戶端Socket來(lái)說(shuō),只有兩個(gè)地方是在接收數(shù)據(jù);一個(gè)是在連接服務(wù)器時(shí);另一個(gè)是在連接服務(wù)器成功后,接收服務(wù)器發(fā)過來(lái)的數(shù)據(jù)時(shí)。因此,客戶端超時(shí)也分為兩種類型:連接超時(shí)和讀取數(shù)據(jù)超時(shí)。

          一、連接超時(shí)

          這種超時(shí)在前面的例子中已經(jīng)使用過。在Socket類中只有通過connect方法的第二個(gè)參數(shù)才能指定連接超時(shí)的時(shí)間。由于使用connect方法連接服務(wù)器必須要指定IP和端口;因此,無(wú)效的IP或端口將會(huì)引發(fā)連接超時(shí)錯(cuò)誤。

          二、讀取數(shù)據(jù)超時(shí)

          在連接服務(wù)器成功后,Socket所做的最重要的兩件事就是接收數(shù)據(jù)發(fā)送數(shù)據(jù);而在接收數(shù)據(jù)時(shí)可能因?yàn)榫W(wǎng)絡(luò)延遲、網(wǎng)絡(luò)阻塞等原因,客戶端一直處于等待狀態(tài);而客戶端在等待一段時(shí)間后,如果服務(wù)器還沒有發(fā)送數(shù)據(jù)到客戶端,那么客戶端Socket將會(huì)拋出一個(gè)超時(shí)錯(cuò)誤。

          我們可以通過Socket類的setSoTimeout方法來(lái)設(shè)置讀取數(shù)據(jù)超時(shí)的時(shí)間;時(shí)間的單位是毫秒。這個(gè)方法必須在讀取數(shù)據(jù)之前調(diào)用才會(huì)生效。如果將超時(shí)時(shí)間設(shè)為0,則不使用超時(shí)時(shí)間;也就是說(shuō),客戶端什么時(shí)候和服務(wù)器斷開,將完全取決于服務(wù)端程序的超時(shí)設(shè)置。如下面的語(yǔ)句將讀取數(shù)據(jù)超時(shí)時(shí)間設(shè)為5秒。

          Socket socket = new Socket();
          socket.setSoTimeout(
          5000);
          socket.connect(… …);
          socket.getInputStream().read();

          要注意的是不要將設(shè)置連接超時(shí)和讀取數(shù)據(jù)超時(shí)設(shè)置得太小,如果值太小,如100,可能會(huì)造成服務(wù)器的數(shù)據(jù)還沒來(lái)得及發(fā)過來(lái),客戶端就拋出超時(shí)錯(cuò)誤的現(xiàn)象。下面的代碼給出了一個(gè)用于測(cè)試連接超時(shí)和讀取數(shù)據(jù)超時(shí)的例子。

          package mynet;

          import java.net.*;

          public class SocketTimeout
          {
              
          public static void main(String[] args)
              {
                  
          long time1 = 0, time2 = 0;
                  Socket socket 
          = new Socket();
                  
          try
                  {
                      
          if (args.length < 4)
                      {
                          System.out.println(
          "參數(shù)錯(cuò)誤!");
                          
          return;
                      }

                      time1 
          = System.currentTimeMillis();
                      socket.connect(
          new InetSocketAddress(args[0], Integer
                              .parseInt(args[
          1])), Integer.parseInt(args[2]));
                      socket.setSoTimeout(Integer.parseInt(args[
          3]));
                      time1 
          = System.currentTimeMillis();
                      socket.getInputStream().read();
                  }
                  
          catch (SocketTimeoutException e)
                  {
                      
          if (!socket.isClosed() && socket.isConnected())
                          System.out.println(
          "讀取數(shù)據(jù)超時(shí)!");
                      
          else
                          System.out.println(
          "連接超時(shí)");
                  }
                  
          catch (Exception e)
                  {
                      System.out.println(e.getMessage());
                  }
                  
          finally
                  {
                      time2 
          = System.currentTimeMillis();
                      System.out.println(time2 
          - time1);
                  }
              }
          }

          SocketTimeout類的main方法需要4個(gè)參數(shù):IP(域名)、端口、連接超時(shí)、讀取數(shù)據(jù)超時(shí)。下面讓我們來(lái)用一組數(shù)據(jù)來(lái)測(cè)試這個(gè)例子。

          測(cè)試1 :無(wú)效IP引發(fā)的超時(shí)錯(cuò)誤

          執(zhí)行如下命令:

          java mynet.SocketTimeout 192.168.18.24 80 3000 5000

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

          連接超時(shí)
          3045

          注意:192.168.18.24是一個(gè)無(wú)效的IP;如果這個(gè)IP在網(wǎng)絡(luò)環(huán)境中存在,請(qǐng)換其它的無(wú)效的IP。在這個(gè)測(cè)試用例中不能將無(wú)效的IP換成無(wú)效的域名;這是因?yàn)槿绻褂昧擞蛎麃?lái)連接服務(wù)器,Java會(huì)先通過DNS將域名映射成相應(yīng)的IP;如果這個(gè)域名是無(wú)效的,在映射的過程中就會(huì)出錯(cuò);因此,程序也就不會(huì)執(zhí)行連接服務(wù)器操作,自然也就不會(huì)拋出“連接超時(shí)”錯(cuò)誤了。

          測(cè)試2 :無(wú)效端口引發(fā)的超時(shí)錯(cuò)誤

          執(zhí)行如下命令:

          java mynet.SocketTimeout  www.ptpress.com.cn 8888 3000 5000

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

          連接超時(shí)
          3075

          測(cè)試3 :讀取數(shù)據(jù)超時(shí)錯(cuò)誤

          執(zhí)行如下命令:

          java mynet.SocketTimeout www.ptpress.com.cn 80 3000 5000

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

          讀取數(shù)據(jù)超時(shí)!
          5008

          測(cè)試4 :將讀取數(shù)據(jù)超時(shí)設(shè)為0的效果

          執(zhí)行如下命令:

          java mynet.SocketTimeout www.ptpress.com.cn 80 3000 0

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

          Connection reset
          131519

          從前3個(gè)測(cè)試的輸出結(jié)果不難看出,每個(gè)測(cè)試用例都將連接超時(shí)和讀取數(shù)據(jù)超時(shí)分別設(shè)為30005000毫秒;而它們的運(yùn)行結(jié)果并不是30005000毫秒,而是在所設(shè)定的超時(shí)時(shí)間的左右搖擺;這主要是因?yàn)橄到y(tǒng)所輸出的時(shí)間并不都是超時(shí)時(shí)間;如有一些時(shí)間是Java處理錯(cuò)誤、向控制臺(tái)輸出信息的時(shí)間。另外,由于系統(tǒng)計(jì)時(shí)的誤差,也會(huì)影響到超時(shí)時(shí)間的準(zhǔn)確性。但不管怎樣,超時(shí)時(shí)間總會(huì)在所設(shè)定的時(shí)間周圍搖擺。

          對(duì)于測(cè)試4,將讀取數(shù)據(jù)超時(shí)設(shè)為0后,SocketTimeout類經(jīng)過了2分多鐘(131519毫秒)才拋出Connection reset錯(cuò)誤。這個(gè)拋出錯(cuò)誤的時(shí)間和服務(wù)端程序的超時(shí)設(shè)置有關(guān);也就是這個(gè)錯(cuò)誤是由于服務(wù)端程序主動(dòng)將客戶端網(wǎng)絡(luò)連接斷開而產(chǎn)生的。

          下一篇:
          Java網(wǎng)絡(luò)編程從入門到精通(17):Socket類的getter和setter方法(1)





          Android開發(fā)完全講義(第2版)(本書版權(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   昵稱:李寧_Lining

          posted on 2009-05-26 08:48 銀河使者 閱讀(3912) 評(píng)論(0)  編輯  收藏 所屬分類: java 原創(chuàng)網(wǎng)絡(luò)編程

          主站蜘蛛池模板: 中西区| 滨海县| 秦皇岛市| 宁乡县| 灵川县| 根河市| 河津市| 榆社县| 庆元县| 伊吾县| 左权县| 永安市| 筠连县| 琼中| 靖宇县| 全南县| 罗山县| 渝中区| 房产| 永登县| 前郭尔| 青海省| 淳安县| 二连浩特市| 五莲县| 长丰县| 金塔县| 麻江县| 北流市| 杨浦区| 尼勒克县| 蒲江县| 馆陶县| 七台河市| 淳化县| 宣城市| 织金县| 谢通门县| 沿河| 靖宇县| 辽阳县|