概念
說到長連接,對應的就是短連接了。下面先說明一下長連接和短連接的區別:
短連接與長連接
通俗來講,瀏覽器和服務器每進行一次通信,就建立一次連接,任務結束就中斷連接,即短連接。相反地,假如通信結束(如完成了某個HTML文件的信息獲取)后保持連接則為長連接。在HTTP/1.0中,默認使用短連接。從HTTP/1.1起,默認使用長連接,這樣做的優點是顯而易見的,一個網頁的加載可能需要HTML文件和多個CSS或者JS,假如每獲取一個靜態文件都建立一次連接,那么就太浪費時間了,而在保持連接的情況下,繼續GET即可。
對於頻繁請求資源的客戶來說,較適用長連接。但連接數最好進行限制,防止建立太多連接拖累服務端。一般瀏覽器對一個網站的連接是有限制的幾個,所以網站會將資源部署在多個域名上以實現瀏覽器同時請求。
短/長連接應當在TCP連接的范疇中來討論。有人常說HTTP的短連接和長連接如何如何,但是HTTP只是一個應用層協議,又是無狀態的,最終實質性的保持連接還是得靠傳輸層,即TCP。
keep-alive
我們使用瀏覽器的開發者工具查看網絡請求和響應信息時經常在HTTP請求頭部看到Connection: keep-alive,一般的瀏覽器都會帶着個頭去請求數據,假如有特殊需求可以用Connection: close斷開。HTTP頭部的Connection也不一定就被客戶端或服務端老老實實地遵循,畢竟各有各的考慮,尤其是在HTTP/1.0這還只是個實驗性的功能,而在HTTP/1.1默認長連接於是沒有對長連接做特殊的規定。長連接也不能無限期地長,服務端有可能在頭部放Keep-Alive,其中timeout等於一個值來規定保持連接的秒數,還可以用max來規定多少次請求后斷開。如果沒有說明怎么斷開,主動發起四次握手也可以實現連接的斷開。
HTTP的keep-alive與TCP的keep-alive到底是什么關系? 答:
TCP的keep alive是檢查當前TCP連接是否活着;HTTP的Keep-alive是要讓一個TCP連接活久點。
Socket.IO
該項目是從JavaScript遷移過來的。demo項目地址:
Android chat demo
在Android Studio里面引入Socket.IO,在build.gradle里面加入:
在Android Studio里面引入Socket.IO,在build.gradle里面加入:
compile ('io.socket:socket.io-client:0.8.3') {
// excluding org.json which is provided by Android
exclude group: 'org.json', module: 'json'
}
使用Socket.IO
Socket.IO-client 基本上和JS版本有相同的API,你使用IO.socket()來初始化Socket。
socket = IO.socket("http://localhost"); // 創建Socket.IO長連接對象
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
@Override
public void call(Object... args) {
socket.emit("foo", "hi");
socket.disconnect();
}
}).on("event", new Emitter.Listener() {
@Override
public void call(Object... args) {}
}).on(Socket.EVENT_DISCONNECT, new Emitter.Listener() {
@Override
public void call(Object... args) {}
}); // 注冊事件監聽對象 各種Event
socket.connect(); // 連接socket
這個庫使用org.json來解析和組成JSON字符串。
// Sending an object JSONObject obj = new JSONObject(); obj.put("hello", "server"); obj.put("binary", new byte[42]); socket.emit("foo", obj); // Receiving an object socket.on("foo", new Emitter.Listener() { @Override public void call(Object... args) { JSONObject obj = (JSONObject)args[0]; } });
提供了如下的選項可以設置:
IO.Options opts = new IO.Options(); opts.forceNew = true; opts.reconnection = false; socket = IO.socket("http://localhost", opts);
你可以使用這些參數來配置選項。注:如果你不想重用緩存的socket實例來查詢參數變化時,你應該使用forceNew選項。如果你的程序想要登出一個用戶,再登錄一個新用戶的時候可以使用這種方式:
IO.Options opts = new IO.Options(); opts.forceNew = true; opts.query = "auth_token=" + authToken; Socket socket = IO.socket("http://localhost", opts);
你可以得到一個回調來確認服務器收到一個消息:
socket.emit("foo", "woot", new Ack() {
@Override
public void call(Object... args) {}
});
反過來,你也可以發送一個確認消息,告訴服務器你收到了一個消息:
// ack from client to server socket.on("foo", new Emitter.Listener() { @Override public void call(Object... args) { Ack ack = (Ack) args[args.length - 1]; ack.call(); } });
你能夠使用SSL(HTTPS、WSS)的設置:
// default settings for all sockets IO.setDefaultSSLContext(mySSLContext); IO.setDefaultHostnameVerifier(myHostnameVerifier); // set as an option opts = new IO.Options(); opts.sslContext = mySSLContext; opts.hostnameVerifier = myHostnameVerifier; socket = IO.socket("https://localhost", opts);
查詢更多Java文檔可以查看:
http://socketio.github.io/socket.io-client-java/apidocs/
使用Transports訪問Http頭:(不常用,一般用來設置Cookie參數)
// Called upon transport creation. socket.io().on(Manager.EVENT_TRANSPORT, new Emitter.Listener() { @Override public void call(Object... args) { Transport transport = (Transport)args[0]; transport.on(Transport.EVENT_REQUEST_HEADERS, new Emitter.Listener() { @Override public void call(Object... args) { @SuppressWarnings("unchecked") Map<String, List<String>> headers = (Map<String, List<String>>)args[0]; // modify request headers headers.put("Cookie", Arrays.asList("foo=1;")); } }); transport.on(Transport.EVENT_RESPONSE_HEADERS, new Emitter.Listener() { @Override public void call(Object... args) { @SuppressWarnings("unchecked") Map<String, List<String>> headers = (Map<String, List<String>>)args[0]; // access response headers String cookie = headers.get("Set-Cookie").get(0); } }); } });
