首页 新闻 会员 周边

关于WebSocket的问题 为什么浏览器关闭后会出一下异常

0
悬赏园豆:50 [待解决问题]

java.net.SocketException: Software caused connection abort: recv failed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at java.net.SocketInputStream.read(SocketInputStream.java:90)
at test.UserSocket.run(UserSocket.java:43)
java.net.SocketException: Software caused connection abort: recv failed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:129)
at java.net.SocketInputStream.read(SocketInputStream.java:90)
at test.UserSocket.run(UserSocket.java:43)

 

这是一个WebScoket 的小测试。我开了3个浏览器测试正常后,关闭其中一个就会这个异常以下是我的代码麻烦各位大神给看看

package test;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;

import sun.applet.Main;

public class ServerSocketTest {
    public static List<Socket> clientSocket = new ArrayList<Socket>();
    
    public ServerSocketTest() throws IOException{
        ServerSocket ss = new ServerSocket(30000);
        System.out.println("服务器启动等待客户端连接");
        while(true){
            Socket s =ss.accept();//等待客户端连接
            clientSocket.add(s);
            System.out.println("客户端总人数"+clientSocket.size());
            //为新用户启动线程
            new UserSocket(s).start();
        }
    }
    public static void main(String[] args) throws IOException {
        new ServerSocketTest();
    }
    
    
    
}

下面是线程代码

package test;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.security.MessageDigest;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import sun.misc.BASE64Encoder;

public class UserSocket extends Thread{
    private Socket socket;
    
    public UserSocket(Socket socke){
        this.socket = socke;
    }
    @Override
    public void run() {
        try {
            InputStream is = socket.getInputStream();//获取用户输入流
            OutputStream ops = socket.getOutputStream();//获取用户输出流
            byte[] buff = new byte[1024];//字节
            String red = ""; //用了存放客户端请求过来的内容(客户端信息)
            // 读取数据,此时建立与wabSocket的握手
            int count = is.read(buff);//读取客户端请求内容的长度
            if(count > 0){
                //客户端请求数据转化字符串
                red = new String(buff,0,count);
                //获取WebSocket的值
                String seckey = getSecWebSocketKey(red);
                String response = "HTTP/1.1 101 Switching Protocols\r\nUpgrade: "
                    + "websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: "
                    + getSecWebSocketAccept(seckey) + "\r\n\r\n";
                //推送向客户端
                ops.write(response.getBytes("utf-8"));
            int hasRedad = 0;
            // 不断读取WebSocket发送过来的数据
            System.out.println("while循环前,等待前端推送数据。。。。。。。。。。。。");
            while((hasRedad = is.read(buff))>0){//判断循环读取
                System.out.println("后台接收到值,进入While循环处理");
                /*
                 * 因为WebSocket发送过来的数据遁寻了一定的协议格式, 其中第3~6个字节是数据掩码,
                 * 从第七个字节开始才是真正的有效数据。 因此程序使用第3~6个字节对后面的数据进行了处理
                 */
                for (int i = 0; i < hasRedad - 6; i++) {
                    buff[i + 6] = (byte) (buff[i % 4 + 2] ^ buff[i + 6]);
                }
                //获得从浏览器发送过来的数据
                String pushMsg = new String(buff,6,hasRedad - 6, "utf-8");//第一个值要读取的字节,从第几个开始读取,字符串的总长度,字符集
                //便利Socket集合,向每个Socket对象发送信息
                for (Iterator<Socket> it = ServerSocketTest.clientSocket.iterator();it.hasNext(); ) {
                    try {
                        Socket s = it.next();
                        byte[] pushHead = new byte[2];
                        pushHead[0] = buff[0];
                        pushHead[1] = (byte) pushMsg.getBytes("utf-8").length;
                        //发送前两个字节
                        s.getOutputStream().write(pushHead);
                        //发送有效数据
                        s.getOutputStream().write(pushMsg.getBytes("utf-8"));
                        
                    } catch (SocketException e) {
                        //如果捕获到异常将其从集合中删除
                        // 如果捕捉到异常,表明该Socket已经关闭
                        it.remove();
                    }
                }
                
            }
                
                
                
                
                
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        
        
    }
    
    // 获取WebSocket请求的Seckey
    private String getSecWebSocketKey(String req) {
        // 构建正则表达式,获取Sec-WebSocket-Key:后面的内容
        Pattern p = Pattern.compile("^(Sec-WebSocket-Key:).+",
                Pattern.CASE_INSENSITIVE | Pattern.MULTILINE);
        Matcher m = p.matcher(req);
        if (m.find()) {
            // 提取Sec-WebSocket-Key
            String foundstring = m.group();
            return foundstring.split(":")[1].trim();
        } else {
            return null;
        }
    }

    // 根据WebSocket请求的Seckey计算SecAccept
    private String getSecWebSocketAccept(String key) throws Exception {
        String guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
        key += guid;
        MessageDigest md = MessageDigest.getInstance("SHA-1");
        md.update(key.getBytes("ISO-8859-1"), 0, key.length());
        byte[] shalHash = md.digest();
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(shalHash);
    }
    
    
}
问题补充:

刚才又调试了几次,发现了一些新的东西。

同时开启多个页面进行程序测试。当其中一个页面关闭时,这个页面向后台发送了一条数据,后台接收后处理并发送给其他页面。但是这个页面关闭了,本线程下发送给其他页面的信息全部发送失败。由于发送信息发送不出去,本线程就在此处

    } catch (SocketException e) {
                        //如果捕获到异常将其从集合中删除
                        // 如果捕捉到异常,表明该Socket已经关闭
                        it.remove();
                    }

把其他的Socket对象全部删除了,导致所有的Socket对象连接都断开了。求教大神们有没有什么靠谱的解决办法

暮色听雨声的主页 暮色听雨声 | 初学一级 | 园豆:156
提问于:2015-06-14 17:28
< >
分享
所有回答(2)
-1

反正我觉得浏览关闭肯定是会引用异常的,看每个浏览自己怎么处理了,和为服务器端,能做的就是多写几个try catch去处理它们。

angelshelter | 园豆:9887 (大侠五级) | 2015-06-14 22:18

但是他不能把其他的Socket也给删除掉啊  删除自己的就好了嘛,,这个代码改怎么控制呀  求帮助呀 大神

支持(0) 反对(0) 暮色听雨声 | 园豆:156 (初学一级) | 2015-06-14 22:20

@倾听暮色下的雨声: 什么?你不是用了多线程了嘛,一个客户访问对应一个socket,一个socket对应一个线程,如果客户a把浏览器关了,就对应地把socketa关了呗,又不影响客户b的正常访问。

支持(0) 反对(0) angelshelter | 园豆:9887 (大侠五级) | 2015-06-14 22:24

@angelshelter: 不行唉! 还是会报错的

java.net.SocketException: Software caused connection abort: recv failed
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at java.net.SocketInputStream.read(SocketInputStream.java:90)
    at test.UserSocket.run(UserSocket.java:44)
支持(0) 反对(0) 暮色听雨声 | 园豆:156 (初学一级) | 2015-06-14 22:29

@倾听暮色下的雨声: 你看楼下的回复吧,it.remove();是很容易引起异常的,一般的,只要是迭带,就不能用remove.

支持(0) 反对(0) angelshelter | 园豆:9887 (大侠五级) | 2015-06-15 21:37

@angelshelter: 

 这是我见过的 最靠谱的一个回答,谢谢喽  大神
支持(0) 反对(0) 暮色听雨声 | 园豆:156 (初学一级) | 2015-06-15 21:41

@angelshelter: 去掉it.remove();方法后 我的代码是这样的

for ( it = ServerSocketTest.clientSocket.iterator();it.hasNext(); ) {
                    try {
                        Socket s = it.next().getSocket();
                        byte[] pushHead = new byte[2];
                        pushHead[0] = buff[0];
                        pushHead[1] = (byte) pushMsg.getBytes("utf-8").length;
                        //发送前两个字节
                        s.getOutputStream().write(pushHead);
                        //发送有效数据
                        s.getOutputStream().write(pushMsg.getBytes("utf-8"));
                    } catch (SocketException e) {
                        //如果捕获到异常将其从集合中删除
                        // 如果捕捉到异常,表明该Socket已经关闭
                        //it.remove();
                    }
                }
            }
            }
            
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
                ServerSocketTest.clientSocket.remove(this.sok);
                try {
                    this.sok.getSocket().close();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
        }

我只是截取了一部分, 但是这样写的话 ,程序还是会异常的 ,难道这个Scoket 就不能用this.sok.getSocket().close(); 方法么? 这个Socket 出异常了 就不能把它从集合中删除掉么? 我这样写有错么?  无脑了。。。。。

支持(0) 反对(0) 暮色听雨声 | 园豆:156 (初学一级) | 2015-06-15 21:46

@倾听暮色下的雨声: 我的建议是,你试试看this.sok或this.sok.getSocket()的值是不是为空,如果不为空调用close还是报错的话,就不要调用close了。

还有,你这一次如果remove是写在finally,如果remove()还是被包含在for ( it = ServerSocketTest.clientSocket.iterator();it.hasNext(); )这个循环内的话,还是会报错的,反正就是这个循环内不能remove,要在循环外remove.

支持(0) 反对(0) angelshelter | 园豆:9887 (大侠五级) | 2015-06-15 21:51
-1

for (Iterator<Socket> it = ServerSocketTest.clientSocket.iterator();it.hasNext(); )

{
                    try {
                        Socket s = it.next();
                        byte[] pushHead = new byte[2];
                        pushHead[0] = buff[0];
                        pushHead[1] = (byte) pushMsg.getBytes("utf-8").length;
                        //发送前两个字节
                        s.getOutputStream().write(pushHead);
                        //发送有效数据
                        s.getOutputStream().write(pushMsg.getBytes("utf-8"));
                       
                    } catch (SocketException e) {
                        //如果捕获到异常将其从集合中删除
                        // 如果捕捉到异常,表明该Socket已经关闭
                        it.remove();
                    }
                }

 

你这段代码有问题,调用 it.remove() 后,还会继续使用迭代器,迭代器在遍历时不能修改。

Launcher | 园豆:45045 (高人七级) | 2015-06-15 11:59
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册