Java中用Socket和多线程制作聊天窗口中遇到的问题

最近在学习Socket网络连接,在制作聊天服务器时采用了多线程的方式。下面是代码:

服务器

package socketConnect;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Iterator;

public class SimpleChatServer {

    ArrayList<PrintWriter> clientOutputStreams;

    public class ClientHandler implements Runnable {

        BufferedReader reader;
        Socket sock;

        public ClientHandler(Socket clientSocket) {
            // TODO Auto-generated constructor stub
            sock = clientSocket;
            try {
                InputStreamReader isReader = new InputStreamReader(clientSocket.getInputStream());
                reader = new BufferedReader(isReader);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

        }

        @Override
        public void run() {
            // TODO Auto-generated method stub
            String message;
            System.out.println("服务端线程在跑");
            try {
                while ((message = reader.readLine()) != null) {
                    System.out.println("Sread: " + message);
                    tellEveryone(message);
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

    }

    public void go() {
        clientOutputStreams = new ArrayList<>();
        try {
            ServerSocket serverSock = new ServerSocket(8080);
            while (true) {
                Socket clientSocket = serverSock.accept();
                PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());
                clientOutputStreams.add(writer);

                Thread t = new Thread(new ClientHandler(clientSocket));
                t.start();
                System.out.println("got a connection");
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }   
    }

    public void tellEveryone(String message) {
        Iterator<PrintWriter> it = clientOutputStreams.iterator();
        while (it.hasNext()) {
            PrintWriter writer = (PrintWriter)it.next();
            writer.println(message);
            writer.flush();
        }   
    }

    public static void main(String[] args) {
        new SimpleChatServer().go();
    }
}

客户端

package socketConnect;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;

public class ClientA {

    JTextField outgoing;
    JTextArea incoming = new JTextArea(15, 50);
    PrintWriter pw;
    BufferedReader br;
    Socket socket;

    public void go() {
        JFrame frame = new JFrame("Chat Client");
        JPanel panel = new JPanel();
        incoming.setLineWrap(true);
        incoming.setWrapStyleWord(true);
        incoming.setEditable(false);
        JScrollPane scrollPane = new JScrollPane(incoming);
        scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
        scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
        outgoing = new JTextField(20);
        JButton button = new JButton("Send");
        button.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                // TODO Auto-generated method stub
                String text = getClass().getEnclosingClass().getSimpleName() + ": " + outgoing.getText();
                try {
                    pw.println(text);
                    pw.flush();

                } catch (Exception e2) {
                    // TODO: handle exception
                }
                outgoing.setText("");
                outgoing.requestFocus();
            }
        });
        panel.add(outgoing);
        panel.add(button);
        setUpNetWorking();

        Thread readerThread = new Thread(new IncomingReader());
        readerThread.start();

        frame.getContentPane().add(BorderLayout.NORTH, scrollPane);
        frame.getContentPane().add(BorderLayout.CENTER, panel);
        frame.setSize(400, 500);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);;
    }

    private void setUpNetWorking() {
        try {
            socket = new Socket("127.0.0.1", 8080);
            pw = new PrintWriter(socket.getOutputStream());
            InputStreamReader isReader = new InputStreamReader(socket.getInputStream());
            br = new BufferedReader(isReader);
            System.out.println("networking established");
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        System.out.println("程序运行开始");
        new ClientA().go();
    }

    public class IncomingReader implements Runnable {

        @Override
        public void run() {
            // TODO Auto-generated method stub
            System.out.println("线程开始跑了");
            String message; 
            try {
                while ((message = br.readLine()) != null) {
                    System.out.println("Aread: " + message);
                    incoming.append(message + "\n");
                }
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("线程跑完了");
        }

    }
}

以上是代码,运行不报错,可以正常实现聊天。
但是想知道一些细节的原理:
1. 先运行服务端,跑起来后控制台无输出。然后运行客户端,控制台首先会闪现一下以下的输出(是闪现的,特别快,所以我截不了图,手打的):

                                                                程序运行开始
                                                                networking established
                                                                线程开始跑了。

这应该是是客户端运行时的输出,然后是这样的输出:
图片说明
所以当服务端建立接口之后等待客户端的请求,客户端请求该端口之后,是整个客户端程序运行了一遍,然后服务器的代码才会接着往下运行吗?但是如果整个客户端程序运行了一遍,为什么控制台不输出“线程跑完了”?还是说客户端监听服务器信息的那个线程一直在独立空间中运行着?
请大神帮我解答一下服务器和客户端运行时代码的运行过程以及客户端子线程为什么跑不完?
2. 当我向聊天框中输入信息时:
图片说明
点击send,会出现以下结果:
图片说明
所以为什么整个客户端又重新运行了一次????我只是点击了send触发了监听器监听的事件,事件中flush了输出流的内容,为啥整个客户端好像重新run了一次??另外,这个子线程是一直监听服务器的信息吗?服务器没有新的消息它也不结束,就一直等待吗?

请大神解答一下,我对多线程也是刚刚接触,而且有点强迫症,想搞清楚一切不理解的东西。。

weixin_44198768
请叫我大星星 其他的问题还请大神帮忙解答一下! 为什么客户端第一次发送数据后,点击send,会重新走一遍客户端所有代码??
4 个月之前 回复
weixin_44198768
请叫我大星星 今天查阅了有关BufferedReader的readLine()函数的有关问题,发现readLine()函数在网络数据流中会进入阻塞模式,也就是什么都没读取到也不会返回null,只有当流结束的时候才会返回null,大多数时候都会使用特定字符来表示流结束来跳出循环。
4 个月之前 回复

1个回答

Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
立即提问
相关内容推荐