【新手求教】用定时器检测超时后关闭socket抛出socket closed 异常

sdlqzql 2016-08-31 01:06:57
由于是自己自学的java,基础不好,遇到这个问题,研究好久没得出答案,特来请教。希望懂的朋友指点迷津,万谢。
异常提示:
java.net.SocketException: Socket closed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:150)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at java.net.SocketInputStream.read(SocketInputStream.java:107)
at my.start.AutoStart$SocketTherad.run(AutoStart.java:108)
java.net.SocketException: Socket is closed
at java.net.Socket.getInputStream(Socket.java:903)
at my.start.AutoStart$SocketTherad.run(AutoStart.java:104)
java.net.SocketException: Socket is closed
at java.net.Socket.getInputStream(Socket.java:903)
at my.start.AutoStart$SocketTherad.run(AutoStart.java:104)


package my.start;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Timer;
import java.util.TimerTask;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

//创建AutoStart类,让他实现ServletContextListener(服务程序上下文监听器)接口
//实现ServletContextListener接口需要重写contextDestroyed()和contextInitialized()两个方法
//记得要在web.xml文件中加入
// <listener>
// <listener-class>my.test.AutoStart</listener-class>
// </listener>
//这样,tomcat在启动时,就会同时调用该接口的contextInitialized()方法
//在contextInitialized()方法中,开启一个线程,用soket监听一个端口,从而实现TCP通信
public class AutoStart extends HttpServlet{

private static final long serialVersionUID = 1L;
private int soketPort = 9014;//要监听的服务器端口,可以根据需要进行修改
@Override
public void init() throws ServletException {
// TODO Auto-generated method stub
super.init();
new MyThread().start();//创建一个MyThread线程对象,并启动线程
System.out.println("开启监听线程");
}


//创建MyThread类,继承Thread方法
class MyThread extends Thread
{
//重写Thread类的run()方法,用来实现MyThread线程的功能
public void run()
{
//System.out.println("测试开始");
try {
ServerSocket ss = new ServerSocket(soketPort);
System.out.println("监听到"+(soketPort+"")+"端口");
while(true){
//System.out.println("已经创建soket");
//ss对象的accept()方法可以监听指定端口有没有TCP连接
//在没有TCP连接之前程序将阻塞在这里
Socket socket = ss.accept();
System.out.println("有客户端接入");
//监听到指定端口有TCP连接后,创建soket对象,程序不再堵塞,往下执行
//创建一个线程,去监听客户端通过TCP发来的数据
Thread sockThread=new SocketTherad(socket);
//启动线程
sockThread.start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
int timeNum;
class SocketTherad extends Thread{ //继承Thread类来创建线程
Socket socket;
InputStream is;
OutputStream os;
boolean run_flag=true;//控制run()函数是否继续运行标志位

Timer heartBeatTimer = new Timer(); //心跳包定时器
private void startHeartBeatThread() {
TimerTask heartBeatTask = new TimerTask() {
public void run() {
timeNum++;
System.out.println("timerNum="+(timeNum+""));
if(timeNum==2){//超时则关闭socket连接及定时器
try {
is.close();
os.close();//关闭输出流
socket.close();//关闭soket
run_flag=false;//跳出死循环
System.out.println("TCP连接断开");
heartBeatTimer.cancel();//关闭定时器
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
};
heartBeatTimer.schedule(heartBeatTask, 10*1000, 10*1000);
}

public SocketTherad(Socket socket) {
this.socket = socket;//传递过来的soket参数赋予给socket对象
}

public void run() {//run()函数用来实现线程要完成的任务
startHeartBeatThread();
while(run_flag)
{
try {
String str = null;
is = socket.getInputStream();//获取输入流
os=socket.getOutputStream();
byte[] buffer = new byte[200];//数据缓冲区;
int length=0;
length = is.read(buffer);//读取接收到的数据流长度

if(length != (-1)){//不是-1,说明读取到有效的数据
str = new String(buffer,0,length);//输入流转换成str字符串
System.out.print("收到数据:");
System.out.println(str);
}
else if(length == (-1)){//接收到数据长度为-1,说明客户端主动关闭了TCP连接
is.close();//关闭输入流
os.close();//关闭输出流
socket.close();//关闭soket
run_flag=false;//跳出死循环
System.out.println("TCP连接断开");
}
buffer = null;
System.gc();//垃圾回收
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

}
...全文
332 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
ps45221 2016-09-01
  • 打赏
  • 举报
回复

while(run_flag) {
    .....
}
这部分代码有问题,当你检测到超时,并设置
run_flag=false;
时,此时并不能保证while循环里面立马停止了,更坏的情况下还可能导致死循环。 所以你即使标记了false,接下来close掉socket,但是你while循环还在继续读数据,就出现这个异常了。 至于为什么会出现这种情况,是由于JVM的指令重排序,具体的请GOOGLE! 解决方法: 把run_flag定义成volatile的。
qq_36005882 2016-08-31
  • 打赏
  • 举报
回复
对socket我也不是很了解,建议:1.最好不要的init()中开副线程;2.最好不要在while里面写死循环;3.你可以看看端口号是否占用或异常

81,115

社区成员

发帖
与我相关
我的任务
社区描述
Java Web 开发
社区管理员
  • Web 开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧