简单的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
    }
}