代碼中通過代理訪問外部資源(轉貼)
http://spaces.msn.com/members/savagardn/Blog/cns!1pzu-0W6-WxMGeUbOUArh6Eg!105.entry
目前網絡上最流行的協議就是HTTP協議。HTTP協議有許多優點,例如它能夠穿越防火墻。同時HTTP也是很多其他協議的基礎,例如SOAP協議就是建立在HTTP協議之上的。
Java通過兩種API對HTTP提供支持,一種是servlet API,它覆蓋了服務器端的編程問題;另一種是java.net包,它通過HttpURLConnection類在客戶端提供了對HTTP協議的支持。但是我在使用servlet API的時候曾經遇到過一些問題。本文將介紹我曾遇到過的一些問題和相應的解決辦法。
基礎知識
通常在企業網絡中,一個終端通過代理服務器同互聯網連接起來,代理服務器負責監視網絡流量和執行安全規則。在java.net API中,軟件可以通過兩個屬性來支持代理服務器,它們分別是http.proxyHost和http.proxyPort。它們必須被設定為相應的代理服務器和端口,下面的代碼展示了如何設定這兩個屬性:
在上面的程序中,你需要根據實際情況設定代理服務器和端口。如果你不知道該如何設置的話,可以詢問你們公司的網絡管理員。
使用驗證
通常公司會要求員工在連接到互聯網之前登錄到代理服務器。通過登錄這種機制使公司可以更好的監控員工對互聯網的使用,例如監控員工都訪問了哪些網站。HttpURLConnection通過驗證類支持代理服務器驗證。下面是一個如何利用HttpURLConnection類進行驗證的例子。首先需要實現一個驗證者:
然后,通過Authenticator.setDefault()方法注冊驗證者:
問題
上面介紹的都是HttpURLConnection類能夠正常工作的情況。但是我在實際情況中遇到了一些網絡,在這些網絡中,HttpURLConnection類無法正常工作。最后我發現關鍵的問題在于使用了不正確的DNS配置。在實際情況中,HttpURLConnection類總是先嘗試利用DNS服務器來解析地址名稱。通常情況下,這種嘗試會失敗,而代理服務器會將連接重新定向。但是某些DNS服務器會返回一些不正確的響應,從而導致程序拋出UnknownHostException異常。作為一個程序員,系統不會為了你的程序更改DNS服務器的配置,因此你需要找出解決這個問題的方法。我的方案是通過自己實現HTTP協議來解決這個問題。例如一個GET命令可以用下面的代碼來實現:
在上面的代碼中,大家可以注意到代理服務器的用戶名和密碼的格式是:
username:password,
并且程序對它們基于Base 64進行了編碼。如果需要參考HTTP協議,可以到http://www.ietf.org/rfc/rfc2616.txt。
Java通過兩種API對HTTP提供支持,一種是servlet API,它覆蓋了服務器端的編程問題;另一種是java.net包,它通過HttpURLConnection類在客戶端提供了對HTTP協議的支持。但是我在使用servlet API的時候曾經遇到過一些問題。本文將介紹我曾遇到過的一些問題和相應的解決辦法。
基礎知識
通常在企業網絡中,一個終端通過代理服務器同互聯網連接起來,代理服務器負責監視網絡流量和執行安全規則。在java.net API中,軟件可以通過兩個屬性來支持代理服務器,它們分別是http.proxyHost和http.proxyPort。它們必須被設定為相應的代理服務器和端口,下面的代碼展示了如何設定這兩個屬性:
String url = "http://www.digitalcq.com/", proxy = "proxy.digitalcq.com", port = "8080"; URL server = new URL(url); Properties systemProperties = System.getProperties(); systemProperties.setProperty("http.proxyHost",proxy); systemProperties.setProperty("http.proxyPort",port); HttpURLConnection connection = ( HttpURLConnection)server.openConnection(); connection.connect(); InputStream in = connection.getInputStream(); readResponse(in); |
在上面的程序中,你需要根據實際情況設定代理服務器和端口。如果你不知道該如何設置的話,可以詢問你們公司的網絡管理員。
使用驗證
通常公司會要求員工在連接到互聯網之前登錄到代理服務器。通過登錄這種機制使公司可以更好的監控員工對互聯網的使用,例如監控員工都訪問了哪些網站。HttpURLConnection通過驗證類支持代理服務器驗證。下面是一個如何利用HttpURLConnection類進行驗證的例子。首先需要實現一個驗證者:
public class SimpleAuthenticator extends Authenticator { private String username, password; public SimpleAuthenticator(String username,String password) { this.username = username; this.password = password; } protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication( username,password.toCharArray()); } } |
然后,通過Authenticator.setDefault()方法注冊驗證者:
String url = "http://www.digitalcq.com/", proxy = "proxy.digitalcq.com", port = "8080", username = "usr", password = "pwd"; Authenticator.setDefault(new SimpleAuthenticator( username,password)); URL server = new URL(url); Properties systemProperties = System.getProperties(); systemProperties.setProperty("http.proxyHost",proxy); systemProperties.setProperty("http.proxyPort",port); HttpURLConnection connection = ( HttpURLConnection)server.openConnection(); connection.connect(); InputStream in = connection.getInputStream(); readResponse(in); |
問題
上面介紹的都是HttpURLConnection類能夠正常工作的情況。但是我在實際情況中遇到了一些網絡,在這些網絡中,HttpURLConnection類無法正常工作。最后我發現關鍵的問題在于使用了不正確的DNS配置。在實際情況中,HttpURLConnection類總是先嘗試利用DNS服務器來解析地址名稱。通常情況下,這種嘗試會失敗,而代理服務器會將連接重新定向。但是某些DNS服務器會返回一些不正確的響應,從而導致程序拋出UnknownHostException異常。作為一個程序員,系統不會為了你的程序更改DNS服務器的配置,因此你需要找出解決這個問題的方法。我的方案是通過自己實現HTTP協議來解決這個問題。例如一個GET命令可以用下面的代碼來實現:
String url = "http://www.digitalcq.com/", proxy = "proxy.digitalcq.com", port = "8080", authentication = "usr:pwd"; URL server = new URL(url); Socket socket = new Socket(proxy,port); Writer writer = new OutputStreamWriter(socket.getOutputStream(), "US-ASCII"); writer.write("GET " + server.toExternalForm() + " HTTP/1.0\r\n"); writer.write("Host: " + server.getHost() + "\r\n"); writer.write("Proxy-Authorization: Basic " + new sun.misc.BASE64Encoder().encode( authentication.getBytes()) + "\r\n\r\n"); writer.flush(); BufferedReader reader = new BufferedReader(new InputStreamReader( socket.getInputStream(),"US-ASCII")); String line = reader.readLine(); if(line != null && line.startsWith("HTTP/")) { int sp = line.indexOf(' '); String status = line.substring(sp + 1,sp + 4); if(status.equals("200")) { while(line.length() != 0) line = reader.readLine(); readResponse(reader); } else throw new FileNotFoundException("Host reports error " + status); } else throw new IOException("Bad protocol"); reader.close(); writer.close(); socket.close(); |
在上面的代碼中,大家可以注意到代理服務器的用戶名和密碼的格式是:
username:password,
并且程序對它們基于Base 64進行了編碼。如果需要參考HTTP協議,可以到http://www.ietf.org/rfc/rfc2616.txt。
posted on 2005-11-07 15:44 bluesky 閱讀(498) 評論(0) 編輯 收藏 所屬分類: 基礎知識