zoukankan      html  css  js  c++  java
  • JavaSE简单实现多线程聊天

    1.1 主程序入口

    在主程序入口处,通过设置MyWindow的第一个参数,如果为true则为服务器,如果为false,则为客户端,当然也可以设置第二个参数,区分客户端和服务器的窗口标题。

    public class JavaMain {
    
        public static void main(String[] args) {
            MyWindow w=new MyWindow(false,"QQ聊天");  //运行时将false改成true, 先启动服务端,然后再改成false启动客户端
            w.setNet("192.168.1.103", 12345);
        }
    }

    1.2 界面程序

    界面程序根据主程序传来的参数不同而创建客户端和服务器窗口,根据界面的构造函数中第一个参数,isServer设置服务器窗体或者是客户端窗体。

    public class MyWindow extends JFrame {
        private static final long serialVersionUID = 1L;
        // 定义一个成员变量
        Client myClient = null;
        Server myServer=null;
        JTextArea area=null;
        
        // 设置默认的IP地址和端口
        private String ipAddress="127.0.0.1";
        private int    nPort=50000;
        
        private boolean isServer=false;
    
        // 构造函数
        public MyWindow(boolean isServer,String title) {
            this.isServer=isServer;
            setTitle(title);
            setSize(300, 400);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            this.setLayout(null);
            initComponents();
            setVisible(true);
        }
        
        // 为外界提供设置IP地址和端口的方法
        public void setNet(String ip,int port){
            ipAddress=ip;
            nPort=port;
            
            // 对通信接口初始化
            initCommication();
        }
        
        // 通过构造函数,将端口和文本显示区传递给myServer对象
        public void initCommication(){
            if (isServer) {
                myServer=new Server(nPort,area);
            }else{
                myClient=new Client(ipAddress,nPort,area);
            }
        }
    
        public void initComponents() {
            // 添加一个文本区
            area = new JTextArea();
            area.setBounds(10, 20, 260, 200);
            add(area);
            // 添加一个文本框
            final JTextField text = new JTextField();
            text.setBounds(10, 240, 260, 30);
            add(text);
            // 添加一个发送按钮
            JButton button = new JButton("发送");
            button.setBounds(10, 290, 80, 30);
            
            button.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    if (isServer) {
                        myServer.sendServerMsg(text.getText());
                    }else{
                        myClient.sendMsg(text.getText());
                    }
                }
            });
            add(button);
        }
    }

    1.3 服务器类

    服务器类比较复杂,这里用到了内部类,内部类的好处就是能随时访问外部类的成员和方法,而无需通过传参数的方法达到目的。

    // 监听主线程
    public class Server extends Thread {
        // 服务器Socket
        private ServerSocket serverSocket = null;
        private ArrayList<ServerThread> clientList = null;
    
        // 显示区
        private JTextArea jTextArea = null;
    
        // 构造函数
        public Server(int port, JTextArea area) {
            jTextArea = area;
            try {
                // 开始绑定端口
                serverSocket = new ServerSocket(port);
                // 初始化客户端连接的列表
                clientList = new ArrayList<ServerThread>();
            } catch (IOException e) {
                System.err.println("服务器端口初始化失败!
    ");
            }
            jTextArea.setText("服务器成功启动,等待客户连接!
    ");
            start(); // 启动线程
        }
    
        public void sendServerMsg(String msg) {
            jTextArea.append(msg + "
    ");
            for (int i = clientList.size() - 1; i >= 0; i--) {
                clientList.get(i).getWriter().println("服务器:" + msg);
                clientList.get(i).getWriter().flush();
            }
        }
    
        // 线程程序
        public void run() {
            while (true) {
                try {
                    // 用阻塞的方式,等待用户连接请求
                    Socket socket = serverSocket.accept();
                    // 启动一条为客户端服务的线程
                    ServerThread svthread = new ServerThread(socket);
                    svthread.start();
                    // 将该客户加入列表中
                    clientList.add(svthread);
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        // 服务线程(内部类),用于处理客户端的服务线程
        class ServerThread extends Thread {
            // 当前正在连接的Socket
            Socket socket = null;
            // 当前连接的Socket的输入和输出流(数据出入口)
            private PrintWriter writer = null;
            private BufferedReader reader = null;
    
            // 构造函数
            public ServerThread(Socket s) {
                socket = s;
                try {
                    // 获取输入输出流
                    reader = new BufferedReader(new InputStreamReader(
                            s.getInputStream()));
                    writer = new PrintWriter(s.getOutputStream());
                    // 在此可以写接收用户端的信息,解析出来(IP地址)
    
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
    
            // 获得输入流,供外界调用
            public BufferedReader getReader() {
                return reader;
            }
    
            // 获得输出流,供外界调用
            public PrintWriter getWriter() {
                return writer;
            }
    
            // 获得socket
            public Socket getSocket() {
                return socket;
            }
    
            // 线程服务程序
            public void run() {
                // 创建一个变量,用于接收客户端发来的信息
                String message = null;
                while (true) {
                    try {
                        // 读取输入流
                        message = reader.readLine();
                        // 如果是下线命令
                        if (message.equals("Bye")) {
                            // 在客户端列表上删除该用户
                            ServerThread temp = null;
                            for (int i = clientList.size() - 1; i >= 0; i--) {
                                temp = clientList.get(i);
                                if (temp.getSocket().equals(socket)) {
                                    clientList.remove(i);
                                }
                                temp.stop();
                            }
                            // 断开连接释放资源
                            reader.close();
                            writer.close();
                            socket.close();
                            return;
                        } else {
                            // 在文本区显示该消息
                            jTextArea.append(message + "
    ");
                            // 将该消息广播给其他用户
                            broadcastMsg(message);
                        }
    
                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                }
            }
    
            // 广播消息
            public void broadcastMsg(String msg) {
                for (int i = clientList.size() - 1; i >= 0; i--) {
                    clientList.get(i).getWriter().println(msg);
                    clientList.get(i).getWriter().flush();
                }
            }
        }
    }

    1.4 客户端类

    客户端类比较简单,只要创建一个线程,与服务器连接即可。

    public class Client extends Thread {
        // 写该类的成员变量
        private Socket socket=null;
        private PrintWriter out=null;
        private BufferedReader in=null;
        
        private JTextArea area;
        
        // 写构造函数,完成初始化
        public Client(String ip,int nPort,JTextArea area){
            try {
                socket=new Socket(ip, nPort);
                
                
            } catch (UnknownHostException e) {
                System.err.println("初始化失败!");
            } catch (IOException e) {
                System.err.println("初始化失败!");
            }
            start();  // 启动线程,让线程开始工作
            this.area=area;
            this.area.setText("初始化成功,连接上服务器!
    ");
        }
        
        // 发送消息
        public void sendMsg(String msg){
            if(socket.isConnected()==true){
                out.println("客户端:"+msg);
                out.flush();// 将消息推送给客户端
            }
        }
        
        
        public void run(){
            while(true){
                try {
                    // 获取客户端的输入流
                    InputStream is = socket.getInputStream();
                    InputStreamReader isr = new InputStreamReader(is);
                    in = new BufferedReader(isr);
                    // 获取客户端的输出流
                    out = new PrintWriter(socket.getOutputStream(), true);
                
                    while(true){
                        // 不停的读取从服务器发来的信息
                        String info = in.readLine();
                        area.append(info + "
    ");
                    }
                } catch (Exception e) {
                    System.err.println("发生数据流读取错误,程序退出!");
                    System.exit(1);
                }
                
                finally{
                    try {
                        // 结束,扫尾工作
                        out.close();
                        in.close();
                        socket.close();
                    } catch (Exception e2) {
                    }
                }
            }
        }
    }

    1.5 运行结果

    运行结果如下所示:

  • 相关阅读:
    警示
    【拒绝挂分】盘点蒟蒻ghy的各种sb错误
    牛客NOIPtg day5 B-demo的gcd
    数字校园APP——视频分享
    数字校园APP——软件需求规格说明书
    数字校园APP——可行性报告分析
    数字校园APP开发与应用
    结对编程第二次作业——四则运算自动生成器
    软件工程第四次作业
    软件工程第三次作业
  • 原文地址:https://www.cnblogs.com/StanLong/p/6781132.html
Copyright © 2011-2022 走看看