简单的Kotlin使用Socket与服务端进行通信
- 客户端
- 连接
- 发送信息
- 接收信息
- 断开连接
- 服务端
- 监听端口&连接
- 完整客户端代码
个人的一些学习经验分享
客户端
连接
与http不同,Socket是长连接,所以只要开始的时候连接一次就好了,理论上客户端和服务端一旦建立连接,则不会主动断掉
fun initConnect():Socket{
val sc:Socket = Socket(ip, port) //通过socket连接服务器,参数ip为服务端ip地址,port为服务端监听端口
sc.setSoTimeout(10000) //设置连接超时限制
if (sc != null) { //判断一下是否连上,避免NullPointException
System.out.println("connect server successful")
} else {
System.out.println("connect server failed,now retry...")
InitConnect() //没连上就重试一次
}
return sc
}
发送信息
先要从socket获取输出流
/**
* 发送数据至服务器
* @param message 要发送至服务器的字符串
*/
fun sendMessage(msg: String?) {
var message = msg
val dout =sc!!.getOutputStream() //获取输出流
try {
if (dout != null && message != null) { //判断输出流或者消息是否为空,为空的话会产生nullpoint错误
message = "$message\n" //末尾加上换行服务器端才有消息返回
val me = message.toByteArray() //基本输出流只能输出字符数组,如果要直接输出字符串要使用OutputStreamWriter
dout!!.write(me)
dout!!.flush() //输出完记得刷新一下
} else {
System.out.println("The message to be sent is empty or have no connect")
}
System.out.println("send message successful")
} catch (e: IOException) {
System.out.println("send message to cilent failed")
e.printStackTrace()
}
dout!!.close() //用完记得关,留到最后统一关闭也可以
}
接收信息
fun receiveMessage(): String? {
var message: String? = "" //先构造个字符串,避免NullPointException
val din = InputStreamReader(sc.getInputStream(), "gb2312") //获取输入流并转换为StreamReader,约定编码格式
try {
val inMessage = CharArray(1024)
val a = din!!.read(inMessage) //a存储返回消息的长度
if (a <= -1) { //接受到的消息没有长度,即代表服务端发送了空的消息
return null
}
message = String(inMessage, 0, a) //用string的构造方法来转换字符数组为字符串
System.out.println("接收到:"+message)
} catch (e: IOException) {
System.out.println("receive message failed")
e.printStackTrace()
}
din!!.close()
return message
}
断开连接
这里是统一关闭的写法,如果使用的是上面的函数,则将输入输出流关闭部分去掉
/**
* 关闭连接
*/
public fun CloseConnect() {
try {
if (din != null) { //先检查一下,如果真的是空的情况下,不加空判断则会报错
din!!.close()
}
if (dout != null) {
dout!!.close()
}
if (sc != null) {
sc!!.close()
}
} catch (e: IOException) {
e.printStackTrace()
}
System.out.println("关闭连接")
}
服务端
服务端与监听端在简单使用的情况下,收发信息和断开连接方法是一样的,唯一的区别在于服务端的连接需要多一个步骤
监听端口&连接
val server:ServerSocket = ServerSocket(port) //先用ServerSocket监听端口,监测到连接则返回一个Socket对象
while(true){
sc = server.accept(); //不断监听,测试有无连接
val thread:ConnectThread = ConnectThread(sc) //服务端要使用多线程来处理连接
thread.start()
}
完整客户端代码
写了一个简单的登陆操作作为演示
import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStream
import java.io.OutputStreamWriter
import java.net.Socket
import com.alibaba.fastjson.*
/*
Author:星辰玄光
Tips:仅适用于客户端
*/
class Connect {
//普通数据交互接口
private val sc: Socket? = null
// //图片交互接口
// private val ImageSocket: Socket? = null
//普通交互流
private var dout: OutputStream? = null
private var din: InputStreamReader? = null
// //图片交互流
// private val imageInputStream: InputStream? = null
// private val imageFileOutputSteam: DataOutputStream? = null
//已连接标记
var isConnect = false
// var ImageConncet = false
/**
* 初始化普通交互连接
*/
fun InitConnect(){
try {
val sc:Socket = Socket(ip, port) //通过socket连接服务器
val din = InputStreamReader(sc.getInputStream(), "gb2312") //获取输入流并转换为StreamReader,约定编码格式
val dout =OutputStreamWriter(sc.getOutputStream()) //获取输出流
sc.setSoTimeout(10000) //设置连接超时限制
if (sc != null && din != null && dout != null) { //判断一下是否都连上,避免NullPointException
isConnect = true
System.out.println("connect server successful")
} else {
System.out.println("connect server failed,now retry...")
InitConnect()
}
} catch (e: IOException) { //获取输入输出流是可能报IOException的,所以必须try-catch
e.printStackTrace()
}
}
/**
* 发送数据至服务器
* @param message 要发送至服务器的字符串
*/
fun sendMessage(message: String?) {
var message = message
try {
if (isConnect) {
if (dout != null && message != null) { //判断输出流或者消息是否为空,为空的话会产生nullpoint错误
message = """
$message
""".trimIndent() //末尾加上换行让服务器端有消息返回
val me = message.toByteArray()
dout!!.write(me)
dout!!.flush()
} else {
System.out.println("The message to be sent is empty or have no connect")
}
System.out.println("send message successful")
} else {
System.out.println("no connect to send message")
}
} catch (e: IOException) {
System.out.println("send message to cilent failed")
e.printStackTrace()
}
}
fun receiveMessage(): String? {
var message: String? = ""
try {
if (isConnect) {
System.out.println("开始接收服务端信息")
val inMessage = CharArray(1024) //设置接受缓冲,避免接受数据过长占用过多内存
val a = din!!.read(inMessage) //a存储返回消息的长度
if (a <= -1) {
return null
}
System.out.println("reply length:$a")
message = String(inMessage, 0, a) //必须要用new string来转换
System.out.println(message)
} else {
System.out.println("no connect to receive message")
}
} catch (e: IOException) {
System.out.println("receive message failed")
e.printStackTrace()
}
return message
}
/**
* 登陆方法
* @param name 用户名
* @param password 登陆密码
*/
fun login(name: String?, password: String?) {
InitConnect() //仅是演示的时候将初始化和关闭放在具体方法中,实际使用中需要多次重复使用Socket,所以不能这样使用
val job = JSONObject() //创建一个json对象存放login信息
job.put("name", name) //装填数据
job.put("password", password)
job.put("login", "1")
job.put("type", "search")
var Msend: String = ""
Msend = job.toJSONString(); //转换为jsonstring
System.out.println("编码成功,编码结果为:"+Msend);
sendMessage(Msend) //发送消息
var reply: String? = ""
reply = receiveMessage() //接收服务器返回信息
/**
* @TODO 收到服务端返回的信息后进行相关处理
*/
CloseConnect()
}
/**
* 关闭连接
*/
public fun CloseConnect() {
try {
if (din != null) {
din!!.close()
}
if (dout != null) {
dout!!.close()
}
if (sc != null) {
sc!!.close()
}
} catch (e: IOException) {
e.printStackTrace()
}
isConnect = false
System.out.println("关闭连接")
}
companion object {
//服务端地址及接口
private const val ip = "127.0.0.1" //本地服务端地址
private const val port = 2333
}
}