在前一篇我们已经顺利使用WebSocket连上Ptt了,接着要做的事情就是读取Server回传的内容。我的主要参考来源是@g21589的PTTCrawler。
首先承接前篇的内容,我需要做一个PttClient类别,用来负责接收与Ptt的连线内容,这个Class会是Singleton,因为预期上我会在多个Fragment以及将来须开启的Service上使用到共通的内容。
class PttClient private constructor(serverUri: URI, header: MutableMap<String, String>) :
WebSocketClient(serverUri, Draft_6455(), header) {
// ...
companion object {
private var instance: PttClient? = null
public fun getInstance(): PttClient {
if (instance == null) {
val hashMap = mutableMapOf<String, String>()
hashMap["origin"] = "https://term.ptt.cc"
instance = PttClient(URI.create("wss://ws.ptt.cc:443/bbs"), hashMap)
instance!!.draft.reset()
instance!!.pipedInputStream = PipedInputStream(4096)
instance!!.pipedOutputStream = PipedOutputStream(instance!!.pipedInputStream)
}
return instance!!
}
}
// ...
}
可以看到我有分别多使用了PipedInputStream以及PipedOutputStream,这是用来持续读取PTT Server回传的byte array内容,在PttClient启动时我有开启一条Thread来做这件事。
class PttClient private constructor(serverUri: URI, header: MutableMap<String, String>) :
WebSocketClient(serverUri, Draft_6455(), header) {
...
private lateinit var readThread: Thread
private lateinit var pipedInputStream: PipedInputStream
private lateinit var pipedOutputStream: PipedOutputStream
public fun start() {
connectBlocking(30, TimeUnit.SECONDS)
readThread = Thread {
initReader()
}
readThread.start()
}
private var posX = -1
private var posY = -1
private lateinit var currentScreen: Array<CharArray> //用来模拟Ptt一页的画面内容
private fun initReader() {
val inputStreamReader = InputStreamReader(pipedInputStream, Charset.forName("big5"))
val bufferedReader = BufferedReader(inputStreamReader)
val pushBackReader = PushbackReader(bufferedReader, 128)
val charArray = CharArray(4096)
posX = -1
posY = -1
currentScreen = Array(72) { CharArray(80) }
while (true) {
val read = pushBackReader.read(charArray)
if (read == -1) {
pushBackReader.close()
bufferedReader.close()
inputStreamReader.close()
break
} else {
var i = 0
while (i < read) {
when (charArray[i]) {
Char(0x08) -> { // BS, 退格
// ...
}
Char(0x0A) -> { // LF, 换行
// ...
}
Char(0x0D) -> { // CR, 回车(Enter)
// ...
}
Char(0x1B) -> { // ESC
// ...
}
else -> {
// ...
}
i++
}
}
}
}
override fun onMessage(bytes: ByteBuffer?) {
super.onMessage(bytes)
Log.d(tag, "onMessage: $bytes")
// 收到byte array内容後直接导入pipedOutputStream内,
// 由initReader function中的内容进行读取。
bytes?.run {
pipedOutputStream.write(array())
pipedOutputStream.flush()
}
}
...
}
initReader中的内容可直接参考开头所讲的PTTCrawler,里面主要是分别解析出Ptt的VT100控制码以及文字内容的部分。另外InputStreamReader中有特别使用big5,是因为Ptt的WebSocket预设是使用big5的文字编码来传输,根据这篇文章在登入时的ID後面加上","能够将编码改成UTF-8,不过因为我是懒惰鬼就没有继续尝试了,先单纯以big-5把功能做完为主。
解析完成後,以一开始连线成功的画面print log的话能看到如下:
private fun printScreen(): String {
if (!this::currentScreen.isInitialized) {
return ""
}
val stringBuilder = StringBuilder()
for (i in 0 until 24) {
for (j in 0 until 80) {
if (currentScreen[i][j] != Char(0x00)) {
stringBuilder.append(currentScreen[i][j])
}
}
stringBuilder.append("\n")
}
Log.d(tag, "printScreen: \n$stringBuilder")
return stringBuilder.toString()
}
Result:
<<: Ruby on Rails CRUD 之 U(Update)
今天来试用 Heroku,并请使用 Heroku 的 Python 范例。 在这之前我已经有注册过 ...
0x1 前言 昨天把 Controller 跟 Route 建立好了,今天来针对回覆内容做更新,并简...
前言 如果只有画面像的话,那也太弱了吧! 赶紧来实作新增闹钟的功能,做完拿去炫耀给边身边的人看! 实...
在一个应用程序中,有着各种不同类型的资料,这些不同的资料也有属於他们的生命周期,有些资料就像之前介绍...
这几天有朋友跟我说,不要再写劝世文了啦,直接实战说明最快! 好吧,既然如此,我就直接解说,我是如何选...