ASP.NET 網站呼叫 Web service 時出現 SocketException (0x274c)

這個問題不太好找,值得記錄一下。

問題描述

在 ASP.NET 2.0 網站中呼叫另一個網站的 Web service 時,「有些」用戶端瀏覽器會出現以下錯誤:

連線嘗試失敗,因為連線對象有一段時間並未正確回應,或是連線建立失敗,因為連線的主機無法回應。 111.xxx.xxx.xxx:80
[SocketException (0x274c): 連線嘗試失敗,因為連線對象有一段時間並未正確回應,或是連線建立失敗,因為連線的主機無法回應。 111.xxx.xxx.xxx:80]

問題難解的原因有兩個。首先,是這個問題只有在少數用戶端環境才會發生,開發團隊不易模擬出問題環境。其次,整個網站的運作架構有點複雜(load-balance、proxy、switch、firewall...blah blah blah),以至於碰到 80 port 被擋住時,花了很多時間在懷疑網路繞徑和防火牆等問題。畢竟,採用 web service 的原因之一,就是因為 80 port 一般都能通行無阻嘛!

發生原因

基本規則:當你在 .NET 程式中利用 Web service proxy 類別來呼叫 Web services,而且該 Web service 是位在 proxy/NAT router 背後,此呼叫動作可能就會失敗。

可是,我們碰到的狀況是只有少數用戶端會出錯,這點倒是令我想不透。

根據 Rick Strahl 的文章「Slow Http client calls from ASP.NET 2.0? Make sure you check your Proxy Settings!」, 裡頭說 .NET 2.0 的 HTTP client 類別預設會去抓取機器上的 IE proxy 設定,然而 ASP.NET 應用程式的執行帳戶(IIS 6 為 NETWORK SERVICE)權限很低,無法讀取那些儲存在 registry 裡面的 IE 設定。這條線索也挺有幫助。

解決方法

首先,開發團隊總算模擬出可以讓問題重現的環境和操作程序。經過測試發現,只要用戶端瀏覽器有指定透過公司內部的 proxy 伺服器連接 Internet,程式就能順利執行。

但是公司內部的 proxy 伺服器僅供企業內網的用戶端使用,故外網的使用者仍會有問題。因此,便從 Strahl 的文章得到這個靈感:何不試試為 Web service 物件指定 proxy 伺服器?

於是,為了讓 ASP.NET 程式在呼叫 Web service 時透過我們指定的 proxy 去連接,我在 web.config 裡面加了以下設定:

<system.net>
  <defaultProxy>
    <proxy
      bypassonlocal="true"
      usesystemdefault="false"
      proxyaddress="http://proxy.xxx.com:3128" />
  </defaultProxy>
</system.net>

如此一來,無論是內網還是外網的使用者,便都能夠順利執行程式了。

不過這種修改 web.config 的方式也會影響 .NET FtpWebRequest 類別,導致應用程式原本順利執行的 FTP 功能發生錯誤:The Requested FTP Command Is Not Supported When Using HTTP Proxy。此時便可以改用程式指定 proxy 的方式解決,參考以下範例:

MyService.Foo ws = new MyService.Foo();
WebProxy proxyObj = new System.Net.WebProxy("http://proxy.xxx.com:3128", True);
ws.Proxy = proxyObj;
ws.DoSomeThing();


這樣就不用去動 web.config 了。

當然,你也可以修改 FTP 傳輸處理的程式碼,將 FtpWebRequest 的 Proxy 設為 null。要用哪一種方式,就看實際情況而定了。

小結

在撰寫 Web service 應用程式時,須了解:
  • .NET 從 2.0 起,利用 wsdl.exe 或 Visual Studio 的 Add Web Reference 功能產生的 Web service proxy 類別預設會使用機器上的網際網路 proxy 連線設定來存取 Web service。此內定行為對某些網路環境會造成問題,須自行指定透過 proxy 連線的方式存取 Web service。
  • 透過 proxy 連線的方式有兩種:(1) 以程式建立 WebProxy 物件,然後指定給 Web service proxy 物件的 Proxy 屬性;(2) 在應用程式組態檔中設定 proxy 元素。
  • 應用程式組態檔的 proxy 元素可以控制 .NET 網路傳輸相關類別要不要使用 IE 的 proxy 設定,亦可明白指定 proxy server 的位址。

沒有留言:

技術提供:Blogger.
回頂端⬆️