UDP通信與TCP通信的實現
UDP通信
我們在使用UDP通信方式時,我們會這樣實現
//設置socket
val socket = DatagramSocket()
val serverPort = 9000
val address = InetAddress.getByName("ip地址")
//發送
val bytes = message.toByteArray(Charsets.UTF_8)
val len = bytes.size
val sendPacket = DatagramPacket(bytes, len, address, serverPort)
socket.send(sendPacket)
//接收
socket.receive(receivePacket)
val data = String(receivePacket.data, Charsets.UTF_8)
//處理接收到的數據
//關閉連接
socket.close()
TCP客戶端通信
我們在使用TCP客戶端通信方式時,我們會這樣實現
//設置socket
val serverPort = 9000
val address = InetAddress.getByName("ip地址")
val socket = Socket(address, serverPort)
val input = socket.getInputStream()
val output = socket.getOutputStream()
//發送
output.write(message.toByteArray(Charsets.UTF_8))
//接收
val len = input.read(receive)
val data = String(receive, 0, len, Charsets.UTF_8)
//處理接收到的數據
//關閉連接
socket.close()
這樣的話,如果我們需要將應用層中的UDP連接轉換為TCP連接,就要大量地修改代碼。
使用統一接口
統一接口之后可以在不需要大量修改應用層代碼的情況下,使用與當前功能類似但是底層實現不同的功能。
以之前我們實現的UDP與TCP兩種通信方式為例,要將其中任意一種轉換為另一種時,又或者有新的通信方式需要采用,每次都繁復地修改應用層代碼很明顯不是個好主意。
我們可以簡單地分析一下這兩種通信方式,他們都要經歷初始化(設置socket)-> 發送或者接收 -> 處理數據 -> 關閉連接,那我們就可以將這些他們共有的部分抽象出來給應用層使用。
定義接口
新建一個Communicate.kt
文件,實現Communicate
接口
interface Communicate {
/**
* 通信端口
*/
var serverPort: Int
/**
* 通信地址
*/
var address: String
/**
* 輸入編碼
*/
var inCharset: Charset
/**
* 輸出編碼
*/
var outCharset: Charset
/**
* 發送數據
* @param message 數據內容
*/
fun send(message: String)
/**
* 開始接收數據
* @param onReceive 處理接收到的數據的函數,函數返回值為是否繼續接收消息.
* 請不要在函數中使用stopReceive()函數停止接收數據,這不會起作用。
* @return 是否開啟成功
*/
fun startReceive(onReceive: OnReceiveFunc): Boolean
/**
* 停止接收數據
*/
fun stopReceive()
/**
* 開啟通信,用於TCP建立連接
* @return 是否開啟成功
*/
fun open(): Boolean
/**
* 關閉通信
*/
fun close()
}
上面的代碼塊中還用到了OnReceiveFunc
,這用到了kotlin中的類型映射,類似於c語言中的typedef
,下面是OnReceiveFunc
的實現,他接收一個字符串作為參數,返回一個布爾型變量。
typealias OnReceiveFunc = (String) -> Boolean
在具體使用時利用kotlin的特性,可以直接寫OnReceiveFunc
方法體。
communicate.startReceive {
binding.textView.text = it
return@startReceive false
}
而在java中的使用方法如下
communicate.startReceive(result -> {
binding.textView.setText(result);
return false;
});
注:這里的
communicate
是一個實現了Communicate
接口的通信對象,而我們並沒有關心到底采用了什么通信方式。
這部分中我們可以使用靜態方法來讓應用層創建對象(即選擇想要的連接方式)更加方便。
interface Communicate { companion object { @JvmStatic val TCPClient: Communicate get() = TCP() @JvmStatic val UDP: Communicate get() = UDP() } //其他代碼 }
其中用到了
@JvmStatic
的注解,這讓java調用Communicate
時可以少一層companion
。
實現接口
我們再實現UDP
與TCPClient
這兩個類,他們都實現了Communicate
接口。
我沒有實現TCPServer,已經實現的兩種具體實現可以參考我的gitee倉庫
實現應用層
這樣一來在應用層調用就可以使用同一種風格,比如聲明一個UDP通信對象
private val communicate = Communicate.UDP.apply {
address = "ip地址"
serverPort = 9000
inCharset = Charset.forName("gb2312")
outCharset = Charset.forName("gb2312")
open()
}
而聲明一個TCPClient通信對象只需要這樣
private val communicate = Communicate.TCPClient.apply {
//與UDP完全一樣
}
而調用部分就更不用說了,完全不需要修改。這樣一來當我們需要修改當前通信方式時只需要將Communicate.UDP
改為Communicate.TCPClient
,極大地降低了后續修改的工作量。
總結
實現了統一接口之后確實可以使后續修改實現更加方便,程序結構也更加工程化。