Получил "сломанный канал (запись не удалась)" в моей практике использования SSL API - PullRequest
0 голосов
/ 11 марта 2020

Я работаю над крошечным java проектом, который может использовать TLS для связи с другими. Он состоит из двух частей: сервера и клиента. Вот класс с именем «MultiThreadServer» в серверной части:

public class MultiThreadServer implements Runnable {
    SSLSocket ss;
    public static Chatroom lobby;

    public static HashMap<SSLSocket, Chatroom> ss_current_channels = new HashMap<SSLSocket, Chatroom>();
    public static HashMap<SSLSocket, String> ss_nick = new HashMap<SSLSocket, String>();

    public MultiThreadServer(SSLSocket ss) {
        this.ss = ss;

    }
    /*
     * args0 = jks file path => depreciated use file path "./server.cer" instead
     * args0 = listen port 
     * args1 = password
     */
    public static void main(String args[]) throws Exception {
        lobby = new Chatroom("lobby");

        KeyStore ks = KeyStore.getInstance("JKS");
        ks.load(new FileInputStream("./server.cer"), args[1].toCharArray());

        KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        kmf.init(ks, args[1].toCharArray());

        SSLContext sc = SSLContext.getInstance("TLS");
        sc.init(kmf.getKeyManagers(), null, SecureRandom.getInstanceStrong());

        SSLServerSocketFactory sssf = sc.getServerSocketFactory();
        SSLServerSocket sss = (SSLServerSocket) sssf.createServerSocket(Integer.parseInt(args[0]));
        System.out.println("\n\nServer created sucessfully!");
        System.out.println("\nwaiting for connecting...\n\n");
        while (true) {
            SSLSocket ss = (SSLSocket) sss.accept();
            System.out.println("A new client connected!");
            //System.out.println(ss.hashCode());
            (new Thread(new MultiThreadServer(ss))).start();
        }
    }

    @Override
    public void run() {
        try {
            if (ss_nick.get(ss) == null) {
                ss_nick.put(ss, "anonymous");
            }
            if (ss_current_channels.get(ss) == null) {
                ss_current_channels.put(ss, lobby);
                lobby.UserJoin(ss);
            }

            BufferedReader br = new BufferedReader(new InputStreamReader(ss.getInputStream()));
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(ss.getOutputStream()));
            String in;
            while (true) {
                while ((in = br.readLine()) != null) {
                    String[] command = ProtocolParser.parse(in);

                    if (command == null) {
                        continue;
                    }

                    switch (command[0]) {
                    case "SAY":
                        if (ss_current_channels.get(ss) == null) {
                            bw.write("you have to join a channel first\n");
                            bw.flush();
                        } else {
                            ss_current_channels.get(ss).appendLog(ss_nick.get(ss) + "> " + command[1]);
                        }
                        break;
                    case "JOIN": // direct continue to VIEW
                        try {
                            ss_current_channels.get(ss).UserLeave(ss);

                            ss_current_channels.put(ss, Chatroom.getChatroom(command[1])); // thrown at here
                            Chatroom.getChatroom(command[1]).UserJoin(ss);
                        } catch (NoSuchChannelException e) { //Create one
                            try {
                                new Chatroom(command[1]).UserJoin(ss);
                                ss_current_channels.put(ss, Chatroom.getChatroom(command[1]));
                            } catch (ChatroomExistsException e1) {
                            } // not possible
                        }

                        // change array to fulfill the condition "VIEW" wants
                        command = new String[]{ "VIEW", command[1], "100" };
                    case "VIEW":
                        String logs = Chatroom.getChatroom(command[1]).getLog(Integer.parseInt(command[2]));
                        bw.write(logs + '\n');
                        bw.flush();
                        break;
                    case "NICK":
                        ss_current_channels.get(ss).appendLog(
                                "Now " + ss_nick.get(ss) + " has changed his(her) nickname" + " to " + command[1]+"!");
                        ss_nick.put(ss, command[1]);
                        break;
                    default:
                        break;
                    }
                }
            }
        } catch (SocketException se){
            try {
                ss_current_channels.get(ss).UserLeave(ss);
                ss_nick.remove(ss);
                ss_current_channels.remove(ss);
                ss.close();
                Thread.currentThread().interrupt();
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (NoSuchChannelException e) {
            try {
                BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(ss.getOutputStream()));
                bw.write("NoSuchChannel! Try JOIN channel!\n");
                bw.flush();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }
}

Когда я использую клиент для подключения к серверу, закрою клиент, затем снова подключусь и что-то напишу в сокет, следующие ошибки будут происходят на сервере:

javax.net.ssl.SSLProtocolException: Broken pipe (Write failed)
    at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:126)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
    at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:259)
    at java.base/sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:988)
    at java.base/sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:233)
    at java.base/sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:312)
    at java.base/sun.nio.cs.StreamEncoder.implFlush(StreamEncoder.java:316)
    at java.base/sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:153)
    at java.base/java.io.OutputStreamWriter.flush(OutputStreamWriter.java:254)
    at java.base/java.io.BufferedWriter.flush(BufferedWriter.java:257)
    at me.petjelinux.ServerChat.Server.Chatroom.messageUsers(Chatroom.java:59)
    at me.petjelinux.ServerChat.Server.Chatroom.appendLog(Chatroom.java:65)
    at me.petjelinux.ServerChat.Server.MultiThreadServer.run(MultiThreadServer.java:92)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.net.SocketException: Broken pipe (Write failed)
    at java.base/java.net.SocketOutputStream.socketWrite0(Native Method)
    at java.base/java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:110)
    at java.base/java.net.SocketOutputStream.write(SocketOutputStream.java:150)
    at java.base/sun.security.ssl.SSLSocketOutputRecord.deliver(SSLSocketOutputRecord.java:320)
    at java.base/sun.security.ssl.SSLSocketImpl$AppOutputStream.write(SSLSocketImpl.java:983)
    ... 10 more

Я знаю, что это исключение может быть вызвано записью в соединение, которое уже закрыто, но я просто не могу понять, как это произошло в этой ситуации (переподключиться с тем же клиентом), Может кто-нибудь объяснить и, возможно, предоставить способ исправить это? Спасибо! При необходимости я отправлю коды из клиентской части.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...