首页 > 编程语言 >java如何实现Socket的长连接和短连接

java如何实现Socket的长连接和短连接

时间:2023-09-24 12:32:31浏览次数:44  
标签:java Socket writer 关闭 null 连接 socket

讨论Socket必讨论长连接和短连接

一、长连接和短连接的概念

  1、长连接与短连接的概念:前者是整个通讯过程,客户端和服务端只用一个Socket对象,长期保持Socket的连接;后者是每次请求,都新建一个Socket,处理完一个请求就直接关闭掉Socket。所以,其实区分长短连接就是:整个客户和服务端的通讯过程是利用一个Socket还是多个Socket进行的。

  可能你会想:这还不简单,长连接不就是不关Socket嘛,短连接不就是每次都关Socket每次都new Socket嘛。然而事实其实并没有那么简单的,请继续看下面的整理

  2、关闭流而保持Socket正常?

    在网上百度了一下,发现很多人都是以关闭流还是关闭Socket来区分长连接和短连接的,其实,个人感觉这种区分方法并没有什么意义:因为这里面有一个事实是,流关闭之后,便不能进行消息的发送(对应关闭输出流)或者接受(对应关闭输入流),因为其实关闭了对应的流,对应连接也就关闭了(这里所说的连接是发送消息的通道!),所以,流关闭而保持Socket开启,是没有达到长连接的效果,贴上测试代码:












//发送核心方法


public String send(String send) throws IOException {


String rtn = null;


BufferedWriter writer = null;


OutputStreamWriter ow = null;


OutputStream os = null;


try{


os = socket.getOutputStream();


ow = new OutputStreamWriter(os);


writer = new BufferedWriter(ow);


char [] sendChar = send.toCharArray();


ArrayList list = new ArrayList();


for(char ch:sendChar){


list.add((int)ch);


}


//进行加密操作


list = encry(list);


Iterator it = list.iterator();


while(it.hasNext()){


writer.write(it.next());


}


writer.flush();


rtn = "发送成功!";


}finally{


//注意:直接关闭流将会导致socket关闭,只能通过shutdownOutput/input的方式关闭流


//另外,流关闭之后,相当于关闭底层的连接,除非新new个socket,否则和客户端的连接相当于断开


//          if(writer!=null){


//              writer.close();


//          }


//          if(ow!=null){


//              ow.close();


//          }


//          if(os!=null){


//os.close();


//          }


//socket.shutdownOutput();流关闭之后,相当于关闭底层的连接,除非新
new个socket,否则和客户端的连接相当于断开


}


return rtn;


}





  这是我写的一个测试的发送消息的核心方法,在关闭了对应的流(无论是输出或者输入)之后,下一次调用getInputStream或者getOutputStream会抛出异常说:Socket is closed;这里讲明一个事实:Socket和流联系着,流关闭了,Socket其实也就相当于关闭状态!

  其实这个也很好理解,Socket本来就是依靠流进行关闭的,流,就只有一个,你关闭了流,Socket赖以通讯的渠道也就关闭了,与客户的连接也断开了,所以抛出异常是很合理的。

  所以,流关闭而要求Socket正常通讯是不可能的!

  所以,如何实现长连接?

二、长连接的正确实现方式

  1、不关闭流实现长连接?

    前面讨论了,流关闭了而不关闭Socket,还是无法达到长连接的效果的,所以,要长连接,流必须不能关闭!那么,是不是直接不关闭流,然后每次要发消息就直接往流里面任进去数据,然后调用flush()方法强制刷新就行了?其实不行的,这样客户端是无法正常接收信息的,你会发觉就算服务端flush了,客户端还是会一直在read方法那里阻塞!具体原因各位可以看一下java api文档的截图:

java如何实现Socket的长连接和短连接_长连接

 

 文档说明了,如果流一直可用,而且没有读到流的末尾(就是对应着对方流已经关闭或者网络断开!),read会一直阻塞!其实这样做也是可以解释清楚的:本来服务端的read方法就不知道Server端的消息什么时候发送完,说不定我以为数据发送完 了,但其实是因为网络延迟而导致部分数据延后到来(况且也不可能所有数据同时到达),所以,read方法只能一直在阻塞等待对方的应答。所以,怎么实现长连接?

  2、实现长连接的方法

  A、客户端自动退出开读取的动作。前面说了,就算服务端调用了flush方法进行输出刷新,客户端也不一定能退出read的动作,所以还是会阻塞。所以,退出动作必须有客户端程序自己完成,我们可以在服务端没发送完一段消息并且刷新前就进行一个写入结束符号的标志,客户端解析到结束符号时,变可直接退出read的循环读取操作,避免一直阻塞。

  B、可以调用有读取一定字节到某个数组的read方法(不过好像这个不太行,毕竟每次消息的长度好像会变的),当然,这只是针对消息定长的情况。

  下面贴上长连接实现后的代码(其实就是比前面的代码加多了读入结束标记符号)












//发送核心方法


public String send(String send) throws IOException {


String rtn = null;


BufferedWriter writer = null;


OutputStreamWriter ow = null;


OutputStream os = null;


try{


os = socket.getOutputStream();


ow = new OutputStreamWriter(os);


writer = new BufferedWriter(ow);


char [] sendChar = send.toCharArray();


ArrayList list = new ArrayList();


for(char ch:sendChar){


list.add((int)ch);


}


//进行加密操作


list = encry(list);


Iterator it = list.iterator();


while(it.hasNext()){


writer.write(it.next());


}


//写入结束标志符号:%


writer.write(\'%\');


writer.flush();


rtn = "发送成功!";


}finally{


//注意:直接关闭流将会导致socket关闭,只能通过shutdownOutput/input的方式关闭流


//另外,流关闭之后,相当于关闭底层的连接,除非新new个socket,否则和客户端的连接相当于断开


//          if(writer!=null){


//              writer.close();


//          }


//          if(ow!=null){


//              ow.close();


//          }


//          if(os!=null){


//os.close();


//          }


//socket.shutdownOutput();流关闭之后,相当于关闭底层的连接,除非新new个socket,否则和客户端的连接相当于断开


}


return rtn;


}





 三、短连接

    短连接就基本没什么好讲的啦,只是每次关闭Socket和流时需要注意一下事情:

    1、虽然前面说了流关闭了,Socket就不可用了,但是,我们还是要显式的关闭Socket的,因为在Socekt中还有中状态:叫做半连接状态,当我们只是用到输出流的时候,我们关闭了输出流,并且不能直接调用close方法,只能调用shutDown对应方法(具体请查看java API),其实输入流还是连接着的(只是我们没有用到而已!),这时候,如果没有显式关闭Soceket,很容易导致内存泄露,所以,所有流Socket都要显式关闭

    2、短连接和长连接有不同的用途:对于某次服务只需要一次回话的客户,使用短连接显得简单;但是,如果该次服务需要很多交互式的操作通信,那还是长连接比较高性能,毕竟,Socket的打开和关闭都是很耗性能的。

 

四、总结

  1、对应流关闭,Socket的对应输入(出)数据的通道也就关闭,此时无法达到长连接效果;

  2、关闭Socket,记得显式关闭流与socket,顺序是线管流再关socket.

  3、要实先长连接,一般需要发送结束标记符号来告诉客户端服务端的某段消息已经发送完毕,否则客户端会一直阻塞在read方法。

 

标签:java,Socket,writer,关闭,null,连接,socket
From: https://blog.51cto.com/u_15988698/7585296

相关文章

  • java---集合(数据结构)(重点)[上]
    13.1概念以前存储一组相同类型的数据使用数组,固定大小,具有连续性的存储空间。比如,5个长度的数组再存入数据时,如果现在已经存满,存入第六个元素,这时数组空间不够,扩容。Arrays.copyOf(),很不方便,如果扩容频率太高,也影响你程序运行效率。集合来解决数组固定,如果扩容又影响效率的问......
  • java——mybatis随笔
    教程:https://www.cnblogs.com/xiaobaibailongma/p/17019484.html    本地示例:https://www.cnblogs.com/xiaobaibailongma/p/17019676.html      =========================================================================      gitee:示例......
  • SSH连接慢
    连接linux服务器一般都是使用SSH远程连接的方式。有时,SSH连接速度很慢,但是ping时一切正常。大致是有以下几种原因:server的sshd会去DNS查找访问的clientip的hostname,如果DNS不可用或者没有相关记录,就会消耗一段时间。在authenticationgssapi-with-mic有时候也会消耗一段时间......
  • Could not open client transport with JDBC Uri: jdbc:hive2://node1:10000: java.n
    今天发现连接beeline是时候连接不上,不应该啊昨晚还可以的qaq  破案了,我启动了metastore之后忘记去启动hiveserver2hiveserver2都没启动能连上就怪了 一定一定要记得启动顺序!!!hadoop+metastore+hiveserver2+beeline ......
  • 无涯教程-JavaScript - PERCENTRANK.INC函数
    描述PERCENTRANK.INC函数返回数据集中的值的排名,作为数据集的百分比(包括0..1)。此功能可用于判断数据集中值的相对位置。语法PERCENTRANK.INC(array,x,[significance])争论Argument描述Required/OptionalArrayThearrayorrangeofdatawithnumericvaluesthat......
  • 哪个JavaScript混淆工具更好用?测评葡萄牙的JScrambler和中国的JShaman
    两款顶级JavaScript混淆工具测评:JScrambler和JShaman出于JavaScript代码安全需求,JavaScript混淆已经被广泛使用。在这个领域中,有免费的小工具,也有专业、商业级的产品。商业产品在功能强度、保护效果、稳定性等各方面都是全优于免费小工具的。本文将对两款专业、商业JavaScript......
  • 《动手学深度学习 Pytorch版》 7.7 稠密连接网络
    7.7.1从ResNet到DenseNetDenseNet可以视为ResNet的逻辑扩展。ResNet将函数展开为\(f(\boldsymbol{x})=x+g(\boldsymbol{x})\),即一个简单的线性项和一个复杂的非线性项。若将\(f\)拓展成超过两部分,则DenseNet便是其中一种方案。这即是DenseNet和ResNet的主要区......
  • 解密TCP连接断开:四次挥手的奥秘和数据传输的安全
    TCP连接断开在当今数字化时代,互联网已经成为了人们生活中不可或缺的一部分。而在互联网的基础之上,TCP协议扮演着关键的角色,它负责着数据在网络中的可靠传输。在TCP连接的建立过程中,我们已经了解了三次握手的过程和原理。然而,连接的建立只是TCP协议的一部分,同样重要的是连接的断......
  • 深入了解Java中的StringJoiner类
    在Java编程中,字符串的拼接是一个常见的操作。Java提供了多种方法来实现字符串拼接,其中之一就是StringJoiner类。本文将详细介绍StringJoiner的用法和功能。StringJoiner简介StringJoiner是Java8引入的一个用于拼接字符串的工具类。它允许我们以指定的分隔符将一组字符串连接成一个......
  • JavaScript-实例对象与 new 命令
    对象是什么 面向对象编程(ObjectOrientedProgramming,缩写为OOP)是目前主流的编程范式。它将真实世界各种复杂的关系,抽象为一个个对象,然后由对象之间的分工与合作,完成对真实世界的模拟。每一个对象都是功能中心,具有明确分工,可以完成接受信息、处理数据、发出信息等任务。对象可以......