200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > 朝花夕拾之socket的基本使用以及mina框架简单介绍

朝花夕拾之socket的基本使用以及mina框架简单介绍

时间:2023-08-22 03:28:33

相关推荐

朝花夕拾之socket的基本使用以及mina框架简单介绍

工欲善其事,必先利其器,从互联网诞生到现在,基本上所有的程序都是网络程序,很少有单机版的程序了。 而网络编程的本质是两个设备之间的数据交换,当然,在计算机网络中,设备主要指计算机。我们现在进行网络编程,基本上都是使用已经封装好的框架,毕竟自己实现维护一套网络编程框架,费时费力不说,做出来之后还不一定好用,有那时间多保养保养头发。

Socket简介

记得大学学习java的时候,并没有接触网络编程相关的基础知识,一直都是单机版程序,没有见识到网络编程的美妙,所以对socket这个东西既熟悉又陌生。熟悉的原因是在MFC实验课中接触到socket知识的,没错,就是那门古老的开发语言,现在已经销声匿迹了,陌生的原因大概也就不言而喻了。好了,说了那么多,那socket到底是什么呢?

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。很多同学会把tcp、udp协议和socket搞混,其实Socket只是一种连接模式,不是协议。tcp、udp是两个最基本的协议,很多其它协议都是基于这两个协议。

用socket可以创建tcp连接,也可以创建udp连接,这意味着,用socket可以创建任何协议的连接。简单的来说,socket相当于一艘船,你把目的地(ip+端口)告诉它,把要运输的货物搬上去,然后它将货物送往目的地。这个货物具体需要怎么运输,运输完成之后是否还要通知你已经到达目的地,这就是实现协议的区别了。

Socket使用

首先来回顾一下socket的基本使用,创建一个服务端所需步骤

创建ServerSocket对象绑定监听端口。通过accept()方法监听客户端的请求。建立连接后,通过输入输出流读取客户端发送的请求信息。通过输出流向客户端发送请求信息。关闭相关资源。

嗯。。。Talk is cheap,Show me the code:

public class SocketServer {public static void main(String[] args) {//第一步SocketServer socketServer = new SocketServer();socketServer.startServer(2333);}private void startServer(int port) {try {ServerSocket serverSocket = new ServerSocket(port);System.out.println("服务器已启动,等待客户连接...");//第二步 调用accept()方法开始监听,等待客户端的连接 这个方法会阻塞当前线程Socket socket = serverSocket.accept();System.out.println("客户端连接成功");//第三步 建立输入输出流读数据BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));String receivedMsg;while ((receivedMsg = bufferedReader.readLine()) != null && !("end").equals(receivedMsg)) {System.out.println("客户端:" + receivedMsg);//第四步 给客户端发送请求String response = "hello client";System.out.println("我(服务端):" + response);bufferedWriter.write(response+ "\n");bufferedWriter.flush();}//关闭相关资源socket.close();serverSocket.close();bufferedWriter.close();bufferedReader.close();} catch (IOException e) {e.printStackTrace();}}}复制代码

创建一个客户端所需步骤,其实和服务端代码差不多:

创建Socket对象,指明需要连接的服务器的地址和端口。建立连接后,通过输出流向服务器发送请求信息。通过输入流获取服务器的响应信息。关闭相关资源

public class SocketClient {public static void main(String[] args){SocketClient socketClient = new SocketClient();socketClient.startClient(2333);}void startClient(int port){try {Socket clientSocket = new Socket("localhost",port);System.out.println("客户端已启动");//给服务器发消息BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(clientSocket.getOutputStream()));//接收服务器传过来的消息BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));//键盘输入消息 发送给服务端BufferedReader inputReader = new BufferedReader(new InputStreamReader(System.in));String readLine = null;while (!(readLine = inputReader.readLine()).equals("bye")){System.out.println("我(客户端):" + readLine);//将键盘输入的消息发送给服务器bufferedWriter.write(readLine+"\n");bufferedWriter.flush();String response = bufferedReader.readLine();System.out.println("服务端: " + response);}bufferedWriter.close();inputReader.close();clientSocket.close();}catch (IOException e){e.printStackTrace();}}}复制代码

以上对于socket的示例比较简单,只实现了客户端给服务器发送消息,服务器收到消息并回复客户端。现实的情况其实很复杂,如服务器高并发的处理,客户端怎么监听服务器随时发来的消息,客户端断线重连机制,心跳包的处理,还有在对消息的拆包、粘包的处理等等。而引入mina框架,我们不必关注复杂的网络通信的实现,只需专注于具体的业务逻辑。

Mina框架简介

MINA框架是对java的NIO包的一个封装,简化了NIO程序开发的难度,封装了很多底层的细节,让开发者把精力集中到业务逻辑上来。可能有一些同学不知道NIO是什么,这里简单介绍一下,NIO就是new IO,是jdk1.4引入的,它是同步非阻塞的,比如一个服务器多个客户端,客户端发送的请求都会注册到服务器的多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。它适用于连接数目多且连接比较短的架构,比如聊天服务器。

mina简单使用

Mina 的API 将真正的网络通信与我们的应用程序隔离开来,你只需要关心你要发送、接收的数据以及你的业务逻辑即可。如果想要使用mina,需要先去apache下载mina的jar包:mina下载地址,我使用的是2.0.16版本,下载解压之后,只需要使用mina-core-2.0.16.jar和slf4j-android.jar这两个包即可.

先看看我们实现的效果图(由于是视频转gif,可能不太好看出来,就是一个简单的文本通讯):

创建服务端

前面说mina将网络通信与我们的应用程序隔离开来,那我们看看怎么样实现一个TCP的服务端,最近在学习kotlin,以后的代码应该都用kotlin展示了:

//创建一个非阻塞的service端的socketval acceptor = NioSocketAcceptor()//设置编解码器 ProtocolCodecFilter拦截器 网络传输需要将对象转换为字节流acceptor.filterChain.addLast("codec",ProtocolCodecFilter(TextLineCodecFactory()))//设置读取数据的缓冲区大小acceptor.sessionConfig.readBufferSize = 2048//读写通道10秒内无操作进入空闲状态 acceptor.sessionConfig.setIdleTime(IdleStatus.BOTH_IDLE, 10)//绑定端口acceptor.bind(InetSocketAddress(8080))复制代码

这段代码我们就初始化了一个TCP服务端,其中,编解码器使用的是mina自带的换行符编解码器工厂,设置编解码器是因为在网络上传输数据时,发送端发送数据需要将对象转换为字节流进行传输,接收端收到数据后再将字节流转换回来。相当于双方约定一套规则,具体规则可以自己定,也可以用现成的。我这里只需要发送文本,就用内置的啦。

网络通信已经实现,那发送、接收数据呢?我们具体的业务逻辑都在IoHandler这个类中进行处理,编写一个类继承IoHandlerAdapter ,并重写它的几个方法,记得在bind端口之前调用acceptor.setHandler(MyIoHandlerAdapter()),不然无法监听到具体事件 :

/*** 向客户端发送消息后会调用此方法* @param session* @param message* @throws Exception*/@Throws(Exception::class)override fun messageSent(session: IoSession?, message: Any?) {super.messageSent(session, message)LogUtils.i("服务器发送消息成功")}/*** 从端口接受消息,会响应此方法来对消息进行处理* @param session* @param message* @throws Exception*/@Throws(Exception::class)override fun messageReceived(session: IoSession?, message: Any?) {super.messageReceived(session, message)val msg = message!!.toString()LogUtils.i("服务器接收消息成功:$msg")}/*** 服务器与客户端创建连接* @param session* @throws Exception*/@Throws(Exception::class)override fun sessionCreated(session: IoSession?) {super.sessionCreated(session)LogUtils.i("服务器与客户端创建连接")}/*** 服务器与客户端连接打开* @param session* @throws Exception*/@Throws(Exception::class)override fun sessionOpened(session: IoSession?) {super.sessionOpened(session)LogUtils.i("服务器与客户端连接打开")}/*** 关闭与客户端的连接时会调用此方法* @param session* @throws Exception*/@Throws(Exception::class)override fun sessionClosed(session: IoSession?) {super.sessionClosed(session)LogUtils.i("关闭与客户端的连接时会调用此方法")}/*** 服务器进入空闲状态* @param session* @param status* @throws Exception*/@Throws(Exception::class)override fun sessionIdle(session: IoSession?, status: IdleStatus?) {super.sessionIdle(session, status)LogUtils.i("服务器进入空闲状态")}/*** 异常* @param session* @param cause* @throws Exception*/@Throws(Exception::class)override fun exceptionCaught(session: IoSession?, cause: Throwable) {super.exceptionCaught(session, cause)LogUtils.i("服务器异常$cause")}复制代码

大家应该注意到IoSession这个东西了,每一个方法参数里都有它,那它具体是干什么的呢?

IoSession是一个接口,这个接口用于表示Server 端与Client 端的连接,IoAcceptor.accept()的时候返回实例。这个接口有如下常用的方法:

WriteFuture write(Object message):这个方法用于写数据,该操作是异步的。CloseFuture close(boolean immediately):这个方法用于关闭IoSession,该操作也是异步的,参数指定true 表示立即关闭,否则就在所有的写操作都flush 之后再关闭。Object setAttribute(Object key,Object value):这个方法用于给我们向会话中添加一些属性,这样可以在会话过程中都可以使用,类似于HttpSession 的setAttrbute()方法。IoSession 内部使用同步的HashMap 存储你添加的自定义属性。SocketAddress getRemoteAddress():这个方法获取远端连接的套接字地址。void suspendWrite():这个方法用于挂起写操作,那么有void resumeWrite()方法与之配对。对于read()方法同样适用。ReadFuture read():这个方法用于读取数据, 但默认是不能使用的, 你需要调用IoSessionConfig 的setUseReadOperation(true)才可以使用这个异步读取的方法。一般我们不会用到这个方法,因为这个方法的内部实现是将数据保存到一个BlockingQueue,假如是Server 端,因为大量的Client 端发送的数据在Server 端都这么读取,那么可能会导致内存泄漏,但对于Client,可能有的时候会比较便利。IoService getService():这个方法返回与当前会话对象关联的IoService 实例。

换言之,拿到IoSession就能够进行两端打call了,什么?你问我怎么打call?

fun sendText(message: String,client IoSession){var ioBuffer = IoBuffer.allocate(message.toByteArray().size)ioBuffer.put(message.toByteArray())ioBuffer.flip()client.write(ioBuffer)}复制代码

创建客户端

无论是Server 端还是Client 端,在Mina中的执行流程都是一样的。唯一不同的就是IoService 的Client 端实现是IoConnector。

val connector = NioSocketConnector()// 设置链接超时时间connector.connectTimeoutMillis = 15000// 添加过滤器connector.filterChain.addLast("codec",ProtocolCodecFilter(TextLineCodecFactory()))connector.setDefaultRemoteAddress(InetSocketAddress(ip, port))val future = connector.connect()future.awaitUninterruptibly()// 等待连接创建完成var session = future.session// 获得IoSession复制代码

IoHandlerAdapter 和服务端一样,这里不做过多介绍。

最后贴上服务端代码:

class MinaServer : IoHandlerAdapter(){private val acceptor: NioSocketAcceptorprivate var isConnected = falseprivate var handler: Handler by Delegates.notNull()init {//创建一个非阻塞的service端的socketacceptor = NioSocketAcceptor()//设置编解码器 ProtocolCodecFilter拦截器 网络传输需要将对象转换为字节流acceptor.filterChain.addLast("codec",ProtocolCodecFilter(TextLineCodecFactory()))//设置读取数据的缓冲区大小acceptor.sessionConfig.readBufferSize = 2048//读写通道10秒内无操作进入空闲状态acceptor.sessionConfig.setIdleTime(IdleStatus.BOTH_IDLE, 10)handler = Handler()}fun connect(port: Int): MinaServer {if (isConnected)return thisthread {try {//注册回调 监听和客户端之间的消息acceptor.handler = thisacceptor.isReuseAddress = true//绑定端口acceptor.bind(InetSocketAddress(port))isConnected = truehandler.post {connectCallback?.onOpened()}} catch (e: Exception) {e.printStackTrace()handler.post {connectCallback?.onError(e)}LogUtils.i("服务器连接异常")isConnected = false}}return this}fun sendText(message: String){for (client in acceptor.managedSessions.values){var ioBuffer = IoBuffer.allocate(message.toByteArray().size)ioBuffer.put(message.toByteArray())ioBuffer.flip()client.write(ioBuffer)}}/*** 向客户端发送消息后会调用此方法* @param session* @param message* @throws Exception*/@Throws(Exception::class)override fun messageSent(session: IoSession?, message: Any?) {super.messageSent(session, message)LogUtils.i("服务器发送消息成功")connectCallback?.onSendSuccess()}/*** 从端口接受消息,会响应此方法来对消息进行处理* @param session* @param message* @throws Exception*/@Throws(Exception::class)override fun messageReceived(session: IoSession?, message: Any?) {super.messageReceived(session, message)handler.post {connectCallback?.onGetMessage(message)}val msg = message!!.toString()LogUtils.i("服务器接收消息成功:$msg")}/*** 服务器与客户端创建连接* @param session* @throws Exception*/@Throws(Exception::class)override fun sessionCreated(session: IoSession?) {super.sessionCreated(session)handler.post {connectCallback?.onConnected()}LogUtils.i("服务器与客户端创建连接")}/*** 服务器与客户端连接打开* @param session* @throws Exception*/@Throws(Exception::class)override fun sessionOpened(session: IoSession?) {super.sessionOpened(session)LogUtils.i("服务器与客户端连接打开")}/*** 关闭与客户端的连接时会调用此方法* @param session* @throws Exception*/@Throws(Exception::class)override fun sessionClosed(session: IoSession?) {super.sessionClosed(session)handler.post {connectCallback?.onDisConnected()}LogUtils.i("关闭与客户端的连接时会调用此方法")}/*** 服务器进入空闲状态* @param session* @param status* @throws Exception*/@Throws(Exception::class)override fun sessionIdle(session: IoSession?, status: IdleStatus?) {super.sessionIdle(session, status)LogUtils.i("服务器进入空闲状态")}/*** 异常* @param session* @param cause* @throws Exception*/@Throws(Exception::class)override fun exceptionCaught(session: IoSession?, cause: Throwable) {super.exceptionCaught(session, cause)handler.post {connectCallback?.onError(cause)}LogUtils.i("服务器异常$cause")}private var connectCallback:ConnectCallback? = nullfun setConnectCallback(callback:ConnectCallback){this.connectCallback = callback}interface ConnectCallback{fun onSendSuccess()fun onGetMessage(message: Any?)fun onOpened()fun onConnected()fun onDisConnected()fun onError(cause: Throwable)}}复制代码

在服务端中的activity中使用:

var mServer = MinaServer()mServer.connect(2333).setConnectCallback(object : MinaServer.ConnectCallback {override fun onSendSuccess() {//发送消息成功}override fun onGetMessage(message: Any?) {//接收消息成功val msg = message.toString()}override fun onOpened() {}override fun onConnected() {}override fun onDisConnected() {}override fun onError(cause: Throwable) {Toast.makeText(applicationContext, "服务器异常" + cause.toString(), Toast.LENGTH_SHORT).show()}})复制代码

再看客户端代码:

lass MinaClient : IoHandlerAdapter(){private val connector: NioSocketConnectorprivate var session: IoSession? = nullvar isConnected = falseprivate var handler:Handler by Delegates.notNull()init {connector = NioSocketConnector()// 设置链接超时时间connector.connectTimeoutMillis = 15000// 添加过滤器connector.filterChain.addLast("codec",ProtocolCodecFilter(TextLineCodecFactory()))handler = Handler()}fun connect(ip: String, port: Int): MinaClient {if (isConnected)return thisthread {connector.handler = thisconnector.setDefaultRemoteAddress(InetSocketAddress(ip, port))//开始连接try {val future = connector.connect()future.awaitUninterruptibly()// 等待连接创建完成session = future.session// 获得sessionisConnected = session != null && session!!.isConnected} catch (e: Exception) {e.printStackTrace()handler.post {connectCallback?.onError(e)}println("客户端链接异常...")}}return this}fun disConnect(){if (isConnected){session?.closeOnFlush()connector.dispose()}else{connectCallback?.onDisConnected()}}fun sendText(message: String){var ioBuffer = IoBuffer.allocate(message.toByteArray().size)ioBuffer.put(message.toByteArray())ioBuffer.flip()session?.write(ioBuffer)}/*** 向服务端端发送消息后会调用此方法* @param session* @param message* @throws Exception*/@Throws(Exception::class)override fun messageSent(session: IoSession?, message: Any?) {super.messageSent(session, message)LogUtils.i("客户端发送消息成功")handler.post {connectCallback?.onSendSuccess()}}/*** 从端口接受消息,会响应此方法来对消息进行处理* @param session* @param message* @throws Exception*/@Throws(Exception::class)override fun messageReceived(session: IoSession?, message: Any?) {super.messageReceived(session, message)LogUtils.i("客户端接收消息成功:")handler.post {connectCallback?.onGetMessage(message)}}/*** 服务器与客户端创建连接* @param session* @throws Exception*/@Throws(Exception::class)override fun sessionCreated(session: IoSession?) {super.sessionCreated(session)LogUtils.i("服务器与客户端创建连接")handler.post {connectCallback?.onConnected()}}/*** 服务器与客户端连接打开* @param session* @throws Exception*/@Throws(Exception::class)override fun sessionOpened(session: IoSession?) {super.sessionOpened(session)LogUtils.i("服务器与客户端连接打开")}/*** 关闭与客户端的连接时会调用此方法* @param session* @throws Exception*/@Throws(Exception::class)override fun sessionClosed(session: IoSession?) {super.sessionClosed(session)LogUtils.i("关闭与客户端的连接时会调用此方法")isConnected = falsehandler.post {connectCallback?.onDisConnected()}}/*** 客户端进入空闲状态* @param session* @param status* @throws Exception*/@Throws(Exception::class)override fun sessionIdle(session: IoSession?, status: IdleStatus?) {super.sessionIdle(session, status)LogUtils.i("客户端进入空闲状态")}/*** 异常* @param session* @param cause* @throws Exception*/@Throws(Exception::class)override fun exceptionCaught(session: IoSession?, cause: Throwable) {super.exceptionCaught(session, cause)LogUtils.i("客户端异常$cause")handler.post {connectCallback?.onError(cause)}}private var connectCallback:ConnectCallback? = nullfun setConnectCallback(callback:ConnectCallback){this.connectCallback = callback}interface ConnectCallback{fun onSendSuccess()fun onGetMessage(message: Any?)fun onConnected()fun onDisConnected()fun onError(cause: Throwable)}}复制代码

客户端的activity中使用:

var mClient = MinaClient()mClient.connect("192.168.0.108", 2333).setConnectCallback(object : MinaClient.ConnectCallback {override fun onGetMessage(message: Any?) {val msg = message.toString()}override fun onConnected() {}override fun onDisConnected() {Toast.makeText(applicationContext, "断开连接成功", Toast.LENGTH_SHORT).show()}override fun onError(cause: Throwable) {Toast.makeText(applicationContext, "服务器异常" + cause.toString(), Toast.LENGTH_SHORT).show()}override fun onSendSuccess() {}})复制代码

界面布局比较简单,就是一个recyclerview+几个button,如果觉得我讲得不够清楚T^T,可以到github上查看源码:minaSimple

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。