PTT自2017年6月開始實驗性開放WebSocket,到2020年1月1日已公告正式支援,現今以WebSocket登入PTT已經成為主要的連線方式之一,由於我以前也沒用過,正好趁這次的機會嘗試看看。
在連線上我使用的是TooTallNate的Java-WebSocket。
dependencies {
implementation "org.java-websocket:Java-WebSocket:1.5.1"
}
然後網路權限不能忘記,我自己在做新專案的時候真的蠻常忘記這個的Orz。
<uses-permission android:name="android.permission.INTERNET" />
使用上需要繼承org.java_websocket.client.WebSocketClient,裡面會包含Server的callback內容。
import android.util.Log
import org.java_websocket.client.WebSocketClient
import org.java_websocket.drafts.Draft_6455
import org.java_websocket.handshake.ServerHandshake
import java.net.URI
import java.nio.ByteBuffer
class WebSocketTest(serverUri: URI, header: MutableMap<String, String>) :
WebSocketClient(serverUri, Draft_6455(), header) {
private val tag = "WebSocketTest"
override fun onOpen(handshakedata: ServerHandshake?) {
Log.d(tag, "onOpen: ")
}
override fun onMessage(message: String?) {
Log.d(tag, "onMessage: $message")
}
override fun onMessage(bytes: ByteBuffer?) {
super.onMessage(bytes)
Log.d(tag, "onMessage: ${bytes?.array()?.size}")
}
override fun onClose(code: Int, reason: String?, remote: Boolean) {
Log.d(tag, "onClose: code=$code, reason=$reason, remote=$remote")
}
override fun onError(ex: Exception?) {
Log.d(tag, "onError: $ex")
}
}
接著直接嘗試看看:
val hashMap = mutableMapOf<String, String>()
hashMap["origin"] = "https://term.ptt.cc"
val client = WebSocketTest(URI.create("wss://ws.ptt.cc:443/bbs"), hashMap)
client.connectBlocking(30,TimeUnit.SECONDS)
成功連線的話會看到以下log
D/WebSocketTest: onOpen:
D/WebSocketTest: onMessage: 1024
D/WebSocketTest: onMessage: 1024
D/WebSocketTest: onMessage: 190
可以看到Ptt Server是直接回傳ByteBuffer,也就是我們在連線時看到的畫面內容,接下來就是需要對這些Byte Array做解析。
另外在連線時需加上header origin,若沒加的話會連線失敗,Log如下:
D/WebSocketTest: onClose: code=1002, reason=Invalid status code received: 403 Status line: HTTP/1.1 403 Forbidden, remote=false
稍微Google了一下看起來這是WebSocket協定中定義的欄位,總之是必須的。
Origin header
The WebSocket standard defines an Origin header field, which web browsers set to the URL that originates a WebSocket request. This can be used to differentiate between WebSocket connections from different hosts, or between those made from a browser and some other kind of network client. However, remember that the Origin header is essentially advisory: non-browser clients can easily set the Origin header to any value, and thus “pretend” to be a browser.You can think of the Origin header as roughly analogous to the X-Requested-With header used by AJAX requests. Web browsers send a header of X-Requested-With: XMLHttpRequest, which can be used to distinguish between AJAX requests made by a browser and those made directly. However, this header is easily set by non-browser clients, and thus isn’t trusted as a source of authentication.
In the same way, you can use the Origin header as an advisory mechanism—one that helps differentiate WebSocket requests from different locations and hosts, but you shouldn’t rely on it as a source of authentication.
下一篇預計會是Parsing的部分了。不過正好進入中秋連假,希望我能撐住吧...