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

Java基于Socket实现简单QQ聊天详细教程 【java|Java基于Socket实现简单QQ聊天详细教程】一.引言

  1. 技术框架:
    1.1 Java编程
    1.2 Socket实现通信
    1.3 多线程编程
    1.4 JFrame 设计界面
  2. 实现思路
    如图,基本架构就是设计一个服务端(端口确定)以及多个客户端(可随机可指定)。客户端之间并不直接通信,而是通过同一个服务端进行消息传输。
    java|Java基于Socket实现简单QQ聊天详细教程
    文章图片

    服务端:响应客户端连接,接受客户端消息,消息群发
    客户端:通过服务器ip以及port连接对应服务器,发送消息,接受服务端消息,将消息通过界面展示
    二.环境
    操作系统:WindowsXP
    工具:IDEA,JDK13
    三.具体过程
    1.QQServer(服务端)
    1.1 成员变量
```java /*标志服务端是否开启*/ private boolean isStart; /** * 服务端使用ServerSocket,port作为监听的端口 * 客户端使用Socket */ private ServerSocket serverSocket; /** * 客户端集群 */ private List 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|Java基于Socket实现简单QQ聊天详细教程
文章图片

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|Java基于Socket实现简单QQ聊天详细教程
文章图片

总结
1.SocketAPI调用过程
java|Java基于Socket实现简单QQ聊天详细教程
文章图片

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
持续更新,拓展功能。

    推荐阅读