Kotlin TCP客户端

本文将讲述如何用Kotlin语言写一个最简单的TCP连接,作为客户端连接TCP服务器。
假定我的TCP服务器每次发送一串字符串供客户端接收。

基本连接、断连、接收、发送函数

这里我使用java.net.Socket这个现成的工具类来实现连接,先声明一个套接字:

1
var socket: Socket

然后是输入和输出流:

1
2
var reader: BufferedReader
var writer: PrintWriter

接下来就可以开始连接TCP服务器了:

1
2
3
4
5
6
fun connect() {
socket = Socket() // 声明新的套接字
socket.connect(InetSocketAddress("192.168.4.1", "8080"), 5000) // TCP服务器地址192.168.4.1,端口8080,超时时间5000ms,超过则抛出异常
reader = BufferedReader(InputStreamReader(socket.getInputStream())) // 获取输入流,用于读取服务器发送过来的字符串
writer = PrintWriter(socket.getOutputStream(), true) // 获取输出流,用于发送字符串到服务器
}

然后这里是手动断开与TCP服务器的连接:

1
2
3
4
5
fun disconnect() {
writer.close() // 关闭PrintWriter
reader.close() // 关闭BufferedReader
socket.close() // 关闭套接字
}

向TCP服务器发送字符串,这个再简单不过了,而且不用考虑其它类型的变量,直接字符串大法,什么都直接转成字符串来发送:

1
2
3
fun send(message: String) {
writer.println(message)
}

主动从TCP服务器接收字符串,如果要实现自动接收得新建一个定时器循环调用这个函数:

1
2
3
4
5
fun receive(size: Int): String {
val buffer = CharArray(size) // 创建一个有size个元素的字符数组(也就是字符串)
val length = reader.read(buffer) // 从TCP服务器读取字符串
return String(buffer, 0, length) // 返回接收到的字符串
}

到这里,一个最基础的Kotlin TCP客户端代码就完成了。

发送心跳包检测连接是否丢失

此外,还可以加入一个isConnected变量,来实时检测TCP连接是否丢失。当我们连接上TCP服务器时,如果TCP服务器关闭或发生了其它异常导致TCP连接丢失,我们的客户端是不会被告知的,这时候如果再进行数据收发操作就会产生异常。
所以,我们可以在初始化的时候声明一个isConnected的Boolean变量;在connect()函数中连接完成后赋值为true,并发送心跳包实时检测TCP连接是否丢失;在disconnect()函数中赋值为false;在send()receive(size: Int)中加入检测是否已连接的代码,如果未连接则不执行操作。
以下为一个简易的心跳包代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
fun heartbeat() {
timer(period = 1000) { // 每1000毫秒检测一次
try {
val value = receive() // 进行任意与TCP服务器有交流的操作,最好不要随意向服务器发数据,所以这里主动调用接收
} catch (e: Exception) { // 如果发生异常,则表明TCP连接是丢失了的
if (isConnected) { // 如果不是手动断开连接的
isConnected = false // 赋值为false
disconnect() // 仍然需要关闭那些东西
}
cancel() // 关闭定时器
}
}
}

connect()函数的最后调用heartbeat()即可。

一次性获取服务器发过来的字符串

上面receive(size: Int)函数指定了一次接收多少长度的数据。我们也可以改成用Scanner来一次性读取整行数据。

在初始化那里,把reader改成:

1
var reader: Scanner

connect()函数里,把reader改成:

1
reader = Scanner(InputStreamReader(socket.getInputStream()))

最后,把receive(size: Int)函数改成下面这样即可:

1
2
3
fun receive(): String {
return reader.readText()
}

完整代码

下面我把connect()函数里面服务器的连接地址和端口改成了可以自定义的那种,并把整个TCP客户端封装成一个类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.PrintWriter
import java.net.InetSocketAddress
import java.net.Socket

class TCPConnect(private val serverIP: String, private val serverPort: Int) {
private lateinit var socket: Socket
private lateinit var reader: BufferedReader
// private lateinit var reader: Scanner
private lateinit var writer: PrintWriter
var isConnected = false

fun connect() {
socket = Socket()
socket.connect(InetSocketAddress(serverIP, serverPort), 5000)
reader = BufferedReader(InputStreamReader(socket.getInputStream()))
// reader = Scanner(InputStreamReader(socket.getInputStream()))
writer = PrintWriter(socket.getOutputStream(), true)
isConnected = true
heartbeat()
}

fun disconnect() {
isConnected = false
writer.close()
reader.close()
socket.close()
}

fun send(message: String) {
writer.println(message)
}

fun receive(): String {
// return reader.readText()
val buffer = CharArray(24)
val length = reader.read(buffer)
return String(buffer, 0, length)
}

fun heartbeat() {
timer(period = 1000) {
try {
val value = receive()
} catch (e: Exception) {
if (isConnected) {
isConnected = false
disconnect()
}
cancel()
}
}
}
}

因为我TCP服务器的代码是用C语言提供的,这里就先不放出来了,以后有缘再用Kotlin写个这样简易的代码出来~