Java基于Socket实现简单QQ聊天详细教程

Java基于Socket实现简单QQ聊天详细教程

一.引言

  1. 技术框架:
    1.1 Java编程
    1.2 Socket实现通信
    1.3 多线程编程
    1.4 JFrame 设计界面
  2. 实现思路
    如图,基本架构就是设计一个服务端(端口确定)以及多个客户端(可随机可指定)。客户端之间并不直接通信,而是通过同一个服务端进行消息传输。
    Java基于Socket实现简单QQ聊天详细教程_第1张图片
    服务端:响应客户端连接,接受客户端消息,消息群发
    客户端:通过服务器ip以及port连接对应服务器,发送消息,接受服务端消息,将消息通过界面展示
    二.环境
    操作系统:WindowsXP
    工具:IDEA,JDK13
    三.具体过程
    1.QQServer(服务端)
    1.1 成员变量

```java
    /*标志服务端是否开启*/
    private boolean isStart;
 	/**
     * 服务端使用ServerSocket,port作为监听的端口
     * 客户端使用Socket
     */
    private ServerSocket serverSocket;
    /**
     * 客户端集群
     */
    private List<Client> clients;

1.2 构造函数

    public QQServer(String host,int port){
        try {
            serverSocket=new ServerSocket();
            SocketAddress socketAddress=new InetSocketAddress(host,port);
            serverSocket.bind(socketAddress,port);
            isStart=true;
            clients=new ArrayList<>();
            System.out.println("服务器启动成功!"+serverSocket.getLocalSocketAddress());
        }catch (Exception e){
            System.out.println("服务器启动异常: "+e.getMessage());
            System.exit(0);
        }
    }

主要是进行变量初始化以及绑定服务器ip以及端口
1.3 实现多线程
由于服务端要一直处理客户端连接,消息的接受与发送,于是我们需要将其设计为多线程。实现多线程的方式主要有四种:继承Thread类,实现Runnable接口,实现Callable接口,匿名内部类。这里,选择第一种。

 public class QQServer extends Thread

覆写 start方法,这里是处理客户端的连接,Client是服务端内部的客户端存储类,目的是为了将远程客户端抽象为内部Socket对象存储,存储后便启动该对象线程来接受消息,显然,client也应当是多线程的。详细之后会介绍。

    @Override
    public synchronized void start() {
        try {
            System.out.println("等待连接中...");
            while (isStart){
                /*循环等待*/
                Socket socket=serverSocket.accept();
                System.out.println("连接成功! Address:  "+socket.getRemoteSocketAddress());
                Client client=new Client(socket);
                clients.add(client);
                System.out.println("当前在线人数: "+(clients.isEmpty()?0:clients.size()));
                /*这里只能用start,用run会阻塞*/
                new Thread(client).start();
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

1.4 Client实现 (服务端内部存储对象)

 class Client implements Runnable

1.4.1 成员变量

        private DataInputStream dataInputStream;
        private DataOutputStream dataOutputStream;
        private boolean isConnect;
        private Socket client;

主要是输入输出流以及socket实例
1.4.2 发送消息
利用DataOutputStream的writeUTF方法发送

       private void sendMessage(String message){
            try {
                dataOutputStream.writeUTF(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

1.4.3 覆写run函数
这里主要是用于接受该client中socket实例的消息,利用DataInputStream的readUTF函数,并将该消息发送给客户端队列中的所有client,实现消息群发。

        @Override
        public void run() {

            try {
                while(isConnect){
                    String message=dataInputStream.readUTF();
                    //为每个客户端发送消息
                    for (Client client : clients) {
                        client.sendMessage(message);
                    }
                }
            }catch (Exception e) {
                e.printStackTrace();
            }finally {
                try {
                    if (dataOutputStream != null) {
                        dataOutputStream.close();
                    }
                    if (serverSocket != null) {
                        serverSocket.close();
                        serverSocket = null;
                    }
                    isConnect=false;
                    clients.remove(client);
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

  1. QQClient(客户端)
    2.1 成员变量
    其中最重要的便是receive,是客户端用来监听服务端消息的线程。
    private JTextArea outArea;   //输出区域
    private JTextArea inputArea;  //输入区域
    private DataInputStream dataInputStream;
    private DataOutputStream dataOutputStream;
    private Socket socket;
    private boolean isConnect;
    private Thread receive;//消息监听线程
    private int name;  //客户端名字,目前以端口做名字

2.2 界面设计

public class QQClient extends JFrame

客户端是供用户使用,所以需要实现一个简单的界面。于是,我采用JFrame进行界面设计。JFrame功能也很强大,组件也相当齐全,而且使用简单,但缺点就是没有合适的可视化界面,无法立即呈现效果。如果想有图形化界面,可以使用JavaFX。

public void createFrame(){
        /*窗口面板设置*/
        this.setLocation(250,100);

        this.setTitle("中间件虚拟群");
        this.setPreferredSize(new Dimension(550,600));
        /*输出区域*/

        outArea.setBackground(Color.LIGHT_GRAY);
        outArea.setEditable(false);
        outArea.setPreferredSize(new Dimension(550,400));

        /*输入区域*/

        inputArea.setBackground(Color.WHITE);
        inputArea.setPreferredSize(new Dimension(550,50));
        inputArea.setLineWrap(true);

        /*名字*/
        JTextArea nameArea=new JTextArea();
        nameArea.setText("name:  "+name);
        nameArea.setEditable(false);
        nameArea.setBackground(Color.gray);

        /*发送按钮*/
        JButton send=new JButton();
        send.setText("发送");

        add(outArea,BorderLayout.NORTH);
        add(inputArea,BorderLayout.CENTER);
        add(nameArea,BorderLayout.SOUTH);
        add(send,BorderLayout.EAST);

        //窗口关闭监听
        this.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
                disConnect();
            }
        });
        //发送按钮响应
        send.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                String text = inputArea.getText().trim();
                // 清空输入区域信息
                inputArea.setText("");
                // 回车后发送数据到服务器
                sendMessage(text);
            }
        });

        pack();
        setVisible(true);

    }

注意,最后要加上pack,以及设置窗口可视化。
Java基于Socket实现简单QQ聊天详细教程_第2张图片
2.3 发送消息
这里同服务端设计,比较简单

    private void sendMessage(String text) {

        try{
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            dataOutputStream.writeUTF(formatter.format(new Date())+"   " +name+": \n"+"     "+text);
            dataOutputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2.4 消息监听线程
当服务端消息到达时,该线程会立即接收并显示在界面中

    private class ReceiverThread implements Runnable{
        @Override
        public void run() {
            try {
                System.out.println(name+" 的消息线程启动,开始监听...");
                while (isConnect){
                    String message=dataInputStream.readUTF();
                    outArea.setText(outArea.getText() + "\n" + message);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

2.5 断开连接
当客户端断开连接后,需要释放相应资源以及让其消息线程让出cpu占有权,这里使用join指令。

    private void disConnect() {
        try {
            isConnect=false;
            //消息线程释放cpu
            receive.join();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            try {
                if (dataOutputStream != null) {
                    dataOutputStream.close();
                }
                if (socket != null) {
                    socket.close();
                    socket = null;
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
  1. 启动
    先启动服务端
   public class QQChat {
    	public static void main(String[] args) {
       	 	QQServer qqServer=new QQServer("127.0.0.1",10000);
        	qqServer.start();
   		}
    }

再启动多个客户端

    public class QQClientChat {
    	public static void main(String[] args) {
        	QQClient qqClient=new QQClient("127.0.0.1",10000);
        	qqClient.createFrame();
    	}
	}

效果
Java基于Socket实现简单QQ聊天详细教程_第3张图片

总结
1.SocketAPI调用过程
Java基于Socket实现简单QQ聊天详细教程_第4张图片
2. 多线程编程
详情可以参考:https://m.runoob.com/java/java-multithreading.html
3. JFrame
详情可以参考:https://docs.oracle.com/javase/8/docs/api/javax/swing/JFrame.html
4. Github
https://github.com/XMU-YG/Lab1_MiniQQ
持续更新,拓展功能。

你可能感兴趣的:(java,socket)