概述
Socket連接與TCP連接
創建Socket連接時,可以指定使用的傳輸層協議,Socket可以支持不同的傳輸層協議(TCP或UDP),當使用TCP協議進行連接時,該Socket連接就是一個TCP連接。
Socket連接與HTTP連接
由於通常情況下Socket連接就是TCP連接,因此Socket連接一旦建立,通信雙方即可開始相互發送數據內容,直到雙方連接斷開。但在實際網絡應用中,客戶端到服務器之間的通信往往需要穿越多箇中間節點,例如路由器、網關、防火牆等,大部分防火牆默認會關閉長時間處於非活躍狀態的連接而導致 Socket 連接斷連,因此需要通過輪詢告訴網絡,該連接處於活躍狀態。
而HTTP連接使用的是“請求—響應”的方式,不僅在請求時需要先建立連接,而且需要客戶端向服務器發出請求後,服務器端才能回覆數據。
很多情況下,需要服務器端主動向客戶端推送數據,保持客戶端與服務器數據的實時與同步。此時若雙方建立的是Socket連接,服務器就可以直接將數據傳送給客戶端;若雙方建立的是HTTP連接,則服務器需要等到客戶端發送一次請求後才能將數據傳回給客戶端,因此,客戶端定時向服務器端發送連接請求,不僅可以保持在線,同時也是在“詢問”服務器是否有新的數據,如果有就將數據傳給客戶端。
首先,WEB使用HTTP協議作應用層協議,以封裝HTTP文本信息,然後使用TCP/IP做傳輸層協議將它發到網絡上。而我們平時說的最多的socket是什麼呢,實際上socket是對TCP/IP協議的封裝,Socket本身並不是協議,而是一個調用接口(API)。通過Socket,我們才能使用TCP/IP協議,Socket跟TCP/IP協議沒有必然的聯繫。
HTTP是轎車,提供了封裝或者顯示數據的具體形式;Socket是發動機,提供了網絡通信的能力。
實際上,傳輸層的TCP是基於網絡層的IP協議的,而應用層的HTTP協議又是基於傳輸層的TCP協議的,而Socket本身不算是協議,就像上面所說,它只是提供了一個針對TCP或者UDP編程的接口。
關於HTTP與HTTPS
HTTP協議是位於第四層協議TCP之上完成的應用層協議, 端到端都是明文傳送,別人一旦網絡抓包以後都可以看到你的提交與請求數據,這個好像不太安全. HTTP協議的默認端口是80這個是RFC文檔聲明的,屬於官方標準,沒什麼道理可以講.HTTPS是基於SSL加密傳輸的,這樣別人截獲你的數據包破解的概率要小一點,比HTTP安全一點,其默認端口是443, 好像QQ郵箱與谷歌的WEB Mail郵箱都是基於HTTPS. 但是HTTPS通信方式只是傳輸數據加密,都客戶端來說是透明的,它還是一樣要遵守HTTP協議規範來發送POST與GET請求等.
HTTP 協議相關技術補充
HTTP連接
HTTP協議即超文本傳送協議(Hypertext Transfer Protocol ),是Web聯網的基礎,也是手機聯網常用的協議之一,HTTP協議是建立在TCP協議之上的一種應用。
HTTP連接最顯著的特點是客戶端發送的每次請求都需要服務器回送響應,在請求結束後,會主動釋放連接。從建立連接到關閉連接的過程稱爲“一次連接”。
1)在HTTP 1.0中,客戶端的每次請求都要求建立一次單獨的連接,在處理完本次請求後,就自動釋放連接。
2)在HTTP 1.1中則可以在一次連接中處理多個請求,並且多個請求可以重疊進行,不需要等待一個請求結束後再發送下一個請求。
由於HTTP在每次請求結束後都會主動釋放連接,因此HTTP連接是一種“短連接”,要保持客戶端程序的在線狀態,需要不斷地向服務器發起連接請求。通常的做法是即時不需要獲得任何數據,客戶端也保持每隔一段固定的時間向服務器發送一次“保持連接”的請求,服務器在收到該請求後對客戶端進行回覆,表明知道客戶端“在線”。若服務器長時間無法收到客戶端的請求,則認爲客戶端“下線”,若客戶端長時間無法收到服務器的回覆,則認爲網絡已經斷開。
高層協議有:文件傳輸協議 FTP、電子郵件傳輸協議 SMTP、域名系統服務 DNS、網絡新聞傳輸協議NNTP 和 HTTP 協議等。
中介由三種:代理(Proxy)、網關(Gateway)和通道(Tunnel),一個代理根據 URI 的絕對格式來接受請求,重寫全部或部分消息,通過 URI 的標識把已格式化過的請求發送到服務器。網關是一個接收
代理,作爲一些其它服務器的上層,並且如果必須的話,可以把請求翻譯給下層的服務器協議。一 個通道作爲不改變消息的兩個連接之間的中繼點。當通訊需要通過一箇中介(例如:防火牆等)或者是中介不能
識別消息的內容時,通道經常被使用。
代理(Proxy):一箇中間程序,它可以充當一個服務器,也可以充當一個客戶機,爲其它客戶機建立請求。請求是通過可能的翻譯在內部或經過傳遞到其它的 服務器中。一個代理在發送請求信息之前,必須解釋並且如果可能重寫它。代理經常作爲通過防火牆的客戶機端的門戶,代理還可以作爲一個幫助應用來通過協議處 理沒有被用戶代理完成的請求。
網關(Gateway):一個作爲其它服務器中間媒介的服務器。與代理不同的是,網關接受請求就好象對被請求的資源來說它就是源服務器;發出請求的客戶機並沒有意識到它在同網關打交道。網關經常作爲通過防火牆的服務器端的門戶,網關還可以作爲一個協議翻譯器以便存取那些存儲在非HTTP 系統中的資源。
通道(Tunnel):是作爲兩個連接中繼的中介程序。一旦激活,通道便被認爲不屬於 HTTP 通訊,儘管通道可能是被一個 HTTP 請求初始化的。當被中繼 的連接兩端關閉時,通道便消失。當一個門戶(Portal)必須存在或中介(Intermediary)不能解釋中繼的通訊時通道被經常使用。
下面是一些經常在筆試或者面試中碰到的重要的概念,特在此做摘抄和總結。
一、什麼是TCP連接的三次握手
第一次握手:客戶端發送syn包(syn=j)到服務器,並進入SYN_SEND狀態,等待服務器確認;
第二次握手:服務器收到syn包,必須確認客戶的SYN(ack=j+1),同時自己也發送一個SYN包(syn=k),即SYN+ACK包,此時服務器進入SYN_RECV狀態;
第三次握手:客戶端收到服務器的SYN+ACK包,向服務器發送確認包ACK(ack=k+1),此包發送完畢,客戶端和服務器進入ESTABLISHED狀態,完成三次握手。
握手過程中傳送的包裏不包含數據,三次握手完畢後,客戶端與服務器才正式開始傳送數據。
理想狀態下,TCP連接一旦建立,在通信雙方中的任何一方主動關閉連接之前,TCP 連接都將被一直保持下去。
斷開連接時服務器和客戶端均可以主動發起斷開TCP連接的請求,斷開過程需要經過“四次握手”(過程就不細寫了,就是服務器和客戶端交互,最終確定斷開)
二、利用Socket建立網絡連接的步驟
建立Socket連接至少需要一對套接字,其中一個運行於客戶端,稱爲ClientSocket ,另一個運行於服務器端,稱爲ServerSocket 。
套接字之間的連接過程分爲三個步驟:服務器監聽,客戶端請求,連接確認。
1、服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態,等待客戶端的連接請求。
2、客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。
爲此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然後就向服務器端套接字提出連接請求。
3、連接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求時,就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,雙方就正式建立連接。
而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。
三、HTTP鏈接的特點
HTTP協議即超文本傳送協議(Hypertext Transfer Protocol ),是Web聯網的基礎,也是手機聯網常用的協議之一,HTTP協議是建立在TCP協議之上的一種應用。
HTTP連接最顯著的特點是客戶端發送的每次請求都需要服務器回送響應,在請求結束後,會主動釋放連接。從建立連接到關閉連接的過程稱爲“一次連接”。
四、TCP和UDP的區別(考得最多。。快被考爛了我覺得- -\)
1、TCP是面向鏈接的,雖然說網絡的不安全不穩定特性決定了多少次握手都不能保證連接的可靠性,但TCP的三次握手在最低限度上(實際上也很大程度上保證了)保證了連接的可靠性;
而UDP不是面向連接的,UDP傳送數據前並不與對方建立連接,對接收到的數據也不發送確認信號,發送端不知道數據是否會正確接收,當然也不用重發,所以說UDP是無連接的、不可靠的一種數據傳輸協議。
2、也正由於1所說的特點,使得UDP的開銷更小數據傳輸速率更高,因爲不必進行收發數據的確認,所以UDP的實時性更好。
知道了TCP和UDP的區別,就不難理解爲何採用TCP傳輸協議的MSN比採用UDP的QQ傳輸文件慢了,但並不能說QQ的通信是不安全的,
因爲程序員可以手動對UDP的數據收發進行驗證,比如發送方對每個數據包進行編號然後由接收方進行驗證啊什麼的,
即使是這樣,UDP因爲在底層協議的封裝上沒有采用類似TCP的“三次握手”而實現了TCP所無法達到的傳輸效率。
關於CMWAP/CMNET的理解學習
cmwap和cmnet是GPRS網絡的兩種接入方式。其實上吧,是wap方式和net方式。cm是chinamobile。
在國際上,通常只有一種GPRS接入方式,爲什麼在中國會有CMWAP和CMNET兩兄弟呢?(彩信之所以單獨配置接入點是因爲彩信服務需要連接專用的服務器,在這裏不作探討。) 其實,CMWAP 和 CMNET 只是中國移動人爲劃分的兩個GPRS接入方式。前者是爲手機WAP上網而設立的,後者則主要是爲PC、筆記本電腦、PDA等利用GPRS上網服務。它們在實現方式上並沒有任何差別,但因爲定位不同,所以和CMNET相比,CMWAP便有了部分限制,資費上也存在差別。
WAP只是一種GPRS應用模式,它與GRPS的接入方式是無關的。WAP應用採用的實現方式是“終端+WAP網關+WAP服務器”的模式,不同於一般Internet的“終端+服務器”的工作模式。主要的目的是通過WAP網關完成WAP-WEB的協議轉換以達到節省網絡流量和兼容現有WEB應用的目的。 WAP網關從技術的角度講,只是一個提供代理服務的主機,它不一定由網絡運營商提供。但據我所知,中國移動GPRS網絡目前只有唯一的一個WAP網關:10.0.0.172,有中國移動提供,用於WAP瀏覽(HTTP)服務。有一點需要注意,WAP網關和一般意義上的局域網網關是有差別的,標準的WAP網關僅僅實現了HTTP代理的功能,並未完成路由、NAT等局域網網關的功能。這就決定了它在應用上所受到的限制。
適用範圍纔是大家最關心的問題。CMNET擁有完全的Internet訪問權,這裏就不多說了,主要讓我們來看看CMWAP。因爲有了上面提到的限制,CMWAP的適用範圍就要看WAP網關所提供的支持了。目前,中國移動的WAP網關對外只提供HTTP代理協議(80和8080端口)和WAP網關協議(9201端口)。(據有的網友提到1080端口也是開放的,但無法連接。這也許是移動內部使用的一個Socks後門吧^_^)。
結合程序代碼進行理解
建立一個基於ssl的socket鏈接方式:
public class SSLSocketFactoryEx extends SSLSocketFactory {
SSLContext mSSLContext = SSLContext.getInstance("TLS");
public SSLSocketFactoryEx(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException,
KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
throws java.security.cert.CertificateException {
}
@Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)
throws java.security.cert.CertificateException {
}
};
mSSLContext.init(null, new TrustManager[] { tm }, null);
}
@Override
public Socket createSocket(Socket socket, String host, int port,
boolean autoClose) throws IOException, UnknownHostException {
return mSSLContext.getSocketFactory().createSocket(socket, host, port, autoClose);
}
@Override
public Socket createSocket() throws IOException {
return mSSLContext.getSocketFactory().createSocket();
}
}
獲取一個網絡鏈接對象:
//https
public static HttpClient getNewHttpClient() {
if (EMPConfig.newInstance().isNoHttpsCertificate()) {
//校驗證書
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
SSLSocketFactory sf = new SSLSocketFactoryEx(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
//不校驗證書,默認的網絡鏈接
return new DefaultHttpClient();
}
}
開始真實的網絡請求
/**
* <p>
* 從url中讀取比特數據。
* </p>
*
* @param url 給定的url
* @param buffer buffer緩衝區
* @param task 任務對象。
*
* @return 讀取出來的數據,以比特數組表示。
*
* @throws IOException 普通異常
*/
public byte[] read(String url, ByteArrayOutputStream buffer, EMPThreadPool.Task task)
throws Exception {
byte[] resultByts = null;
try {
if (task != null && task.isStop()) {
throw new HttpCancelException();
}
mHttpClient = NetUtils.getNewHttpClient();
String ct = null;
// 這裏最多嘗試兩次,兩次後即使還是移動收費提示頁我們也返回。
int times = 2;
do {
times--;
initHttpRequest(url, null, GET, null, null, true);
if (task != null && task.isStop()) {
throw new HttpCancelException();
}
// 發送請求
mHttpResponse = mHttpClient.execute(mHttpRequest);
int responseCode = mHttpResponse.getStatusLine().getStatusCode();
Utils.printLog("ResponseCode--->", responseCode);
if (responseCode != HttpStatus.SC_OK) {
String msg = EMPTips.getHttpException();
String errorCode = EMPTips.getErrorCode();
if (!Utils.isEmpty(errorCode)) {
msg += errorCode + ":" + String.valueOf(mResponseCode);
}
throw new HttpResponseException(msg);
}
ct = mHttpResponse.getFirstHeader("Content-Type").getValue();
} while (times > 0 && ct != null
&& (ct.indexOf("text/vnd.wap.wml") != -1 || ct.indexOf("application/vnd.wap.wmlc") != -1));
// 獲取返回的報文body
HttpEntity responseEntity = mHttpResponse.getEntity();
long len = responseEntity.getContentLength();
InputStream is = responseEntity.getContent();
readByByte(is, len, buffer, task);
resultByts = buffer.toByteArray();
is.close();
// 處理返回報文
resultByts = handleResponseBody(resultByts);
} catch (EMPException ne) {
Utils.printException(ne);
throw ne;
} catch (Exception e) {
Utils.printException(e);
throw new Exception(EMPTips.getHttpException());
} catch (Error err) {
Utils.printLog(null, err.toString());
throw new Exception(EMPTips.getErrSystemRes());
} finally {
if (mHttpClient != null) {
mHttpClient.getConnectionManager().shutdown();
}
}
return resultByts;
}