• 超详细带你用Java实现QQ的聊天功能


    38051ee198e84322bd29f5d555caaf31.png

    f0b5ea346cd149e48ceec5cb44cedd9b.gif#pic_center

    个人名片:

    🐼作者简介:一名在校生
    🐻‍❄️个人主页:hmm.
    🕊️系列专栏:零基础学java ----- 重识c语言
    🐓每日一句:只要还有明天,今天就永远是起跑点。

     

     

    第一步:完成界面搭建🐓

    6091766aa0904e5fbe51cc24bead6491.png

    要求:

    • 创建两个窗口:一个客户端,一个服务端,完成代码书写
      步骤:

    1.定义JFrame窗体中的组件:文本域,滚动条,面板,文本框,按钮

    1. //属性
    2. //文本域
    3. private JTextArea jta;
    4. //滚动条
    5. private JScrollPane jsp;
    6. //面板
    7. private JPanel jp;
    8. //文本框
    9. private JTextField jtf;
    10. //按钮
    11. private JButton jb;
    2.在构造方法中初始化窗口的组件:
    
    1. //构造方法
    2. public ServerChatMain(){
    3. //初始化组件
    4. jta = new JTextArea();
    5. //设置文本域默认不可编辑
    6. jta.setEditable(false);
    7. //注意:需要将文本域添加到滚动条中,实现滚动效果
    8. jsp =new JScrollPane(jta);
    9. //初始化面板
    10. jp = new JPanel();
    11. jtf = new JTextField(10);
    12. jb=new JButton("发送");
    13. //注意:需要将文本框与按钮添加到面板中
    14. jp.add(jtf);
    15. jp.add(jb);
    1. 注意:需要将滚动条与面板全部添加到窗体中,继承了窗体的属性,这里this就是窗体
    1. this.add(jsp, BorderLayout.CENTER);//BorderLayout--边框布局
    2. this.add(jp,BorderLayout.SOUTH);

    4.设置设置”标题“,大小,位置,关闭,是否可见

    1. //注意:需要设置”标题“,大小,位置,关闭,是否可见
    2. this.setTitle("QQ聊天服务端");
    3. this.setSize(400,300);
    4. this.setLocation(700,300);
    5. this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序就退出
    6. this.setVisible(true);//设置窗体可见
    7. }

    这样我们就完成了服务端的QQ聊天界面窗口搭建:
    注意:JTextArea文本域是不可以书写的
    a6cf00c315d1499dbcaa704c96a8d38f.png
    客户端与服务端代码类似,这里就不一一展示了

    书写完毕代码,运行效果如下图:

    32bf5b06843c4c34964daa68ddb44033.png


    afdc987b6bef4dfbb31fc38f805cb23d.png

    第二步:TPC通信的思路与步骤🐻‍❄️

    使用网络编程完成数据点的传输(TCP,UDP协议)

    TCP协议🐶

    TCP 是面向连接的运输层协议。应用程序在使用 TCP 协议之前,必须先建立 TCP 连接。在传送数据完毕后,必须释放已经建立的 TCP 连接
    每一条 TCP 连接只能有两个端点,每一条 TCP 连接只能是点对点的(一对一)
    TCP 提供可靠交付的服务。通过 TCP 连接传送的数据,无差错、不丢失、不重复,并且按序到达
    TCP 提供全双工通信。TCP 允许通信双方的应用进程在任何时候都能发送数据。TCP 连接的两端都设有发送缓存和接受缓存,用来临时存放双向通信的数据
    面向字节流。TCP 中的“流”指的是流入到进程或从进程流出的字节序列
    4a76e73a354a4b75bca59c0eb13b5782.png
    b1ee168f384d4d419db72cd683b00bc8.png

    TCP 服务端 具体步骤(客户端类似)🐮

    具体步骤:

    •    1.创建一个服务端的套接字
      
    •     2.等待客户端连接
      
    •    3.获取socket通道的输入流(输入六是实现读取数据的,一行一行读取)BufferedReader->readLine();
      
    •     4.获取socket 通道的输出流(输出流实现写出数据,也是写一行换一行,刷新)BufferedWriter->newLine();
      
    •     5.关闭socket 通道
      

    TCP通信步骤代码实现:🐸

    1. try {
    2. //1.创建一个服务端的套接字
    3. ServerSocket serverSocket = new ServerSocket(8888);
    4. //2.等待客户端连接
    5. Socket socket = serverSocket.accept();
    6. //3.获取socket通道的输入流(输入六是实现读取数据的,一行一行读取)BufferedReader->readLine();
    7. //InputStream in = socket.getInputStream();
    8. BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
    9. //4.获取socket 通道的输出流(输出流实现写出数据,也是写一行换一行,刷新)BufferedWriter->newLine();
    10. //当用户点击发送按钮的时候写出数据
    11. BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    12. //循环读取数据并拼接到文本域
    13. String line = null;
    14. while((line = br.readLine())!=null){
    15. //将读取的数据拼接到文本域中显示
    16. jta.append(line+System.lineSeparator());
    17. }
    18. //5.关闭socket 通道
    19. serverSocket.close();
    20. }catch (IOException e) {
    21. e.printStackTrace();
    22. }

    点击发送按钮实现数据的传输🐺

    1. @Override
    2. public void actionPerformed(ActionEvent actionEvent) {
    3. System.out.println("发送按钮被点击了");
    4. }

    步骤:

    1.获取文本框中发送的内容
    2.拼接需要发送的数据内容
    3.自己也要显示
    4.发送
    5.清空文本框内容

    1. @Override
    2. public void actionPerformed(ActionEvent actionEvent) {
    3. //System.out.println("发送按钮被点击了");
    4. //1.获取文本框中发送的内容
    5. String text = jtf.getText();
    6. //2.拼接需要发送的数据内容
    7. text = "服务端对客户端说:"+text;
    8. //3.自己也要显示
    9. jta.append(text);
    10. //4.发送
    11. try {
    12. bw.write(text);
    13. bw.newLine();//换行刷新
    14. bw.flush();
    15. //5.清空文本框内容
    16. jtf.setText("");
    17. } catch (IOException e) {
    18. e.printStackTrace();
    19. }
    20. }

    680886e93e04472bbf1732e71b4cff82.gif#pic_center

    第三步:实现回车键发送数据(客户端类似)🐳

    首先要实现一个接口

    public class ClientChatMain extends JFrame implements ActionListener, KeyListener {
    
    1. @Override
    2. public void keyPressed(KeyEvent e) {
    3. //回车键
    4. // System.out.println(e);
    5. //发送数据到socket 同道中
    6. if(e.getKeyCode()==KeyEvent.VK_ENTER) {
    7. sendDataToSocket();
    8. }

    e1f9fe1f69bd4d3b94c348a124630b49.gif#pic_center

    全部代码:🐲

    服务端:

    1. package com.ithmm.chat;
    2. import javax.swing.*;
    3. import java.awt.*;
    4. import java.awt.event.ActionEvent;
    5. import java.awt.event.ActionListener;
    6. import java.awt.event.KeyEvent;
    7. import java.awt.event.KeyListener;
    8. import java.io.*;
    9. import java.net.ServerSocket;
    10. import java.net.Socket;
    11. import java.util.Properties;
    12. //说明:如果一个类,需要有界面的显示,那么这个类就需要继承JFrame,此时,该类可以被成为一个窗体类
    13. /*步骤:
    14. 1.定义JFrame窗体中的组件
    15. 2.在构造方法中初始化窗口的组件
    16. 3.使用网络编程完成数据的链接(TPC,UDP 协议)
    17. 4.实现"发送”按钮的监听点击事件
    18. 5.实现“回车键”发送数据
    19. */
    20. public class ServerChatMain extends JFrame implements ActionListener, KeyListener {
    21. public static void main(String[] args) {
    22. //调用构造方法
    23. new ServerChatMain();
    24. }
    25. //属性
    26. //文本域
    27. private JTextArea jta;
    28. //滚动条
    29. private JScrollPane jsp;
    30. //面板
    31. private JPanel jp;
    32. //文本框
    33. private JTextField jtf;
    34. //按钮
    35. private JButton jb;
    36. //输出流(成员变量)
    37. private BufferedWriter bw = null;
    38. //服务端的端口号
    39. //private static int serverPort;
    40. //使用static静态方法读取外部京台文件
    41. //static代码块特点:1.在类加载的时候自动执行
    42. //特点2:一个类会被加载一次,因此静态代码块在程序中仅会被执行一次
    43. /*static{
    44. Properties prop = new Properties();
    45. try {
    46. //加载
    47. prop.load(new FileReader("chat.properties"));
    48. //给属性赋值
    49. Integer.parseInt(prop.getProperty("serverPort"));
    50. } catch (IOException e) {
    51. e.printStackTrace();
    52. }
    53. }*/
    54. //构造方法
    55. public ServerChatMain(){
    56. //初始化组件
    57. jta = new JTextArea();
    58. //设置文本域默认不可编辑
    59. jta.setEditable(false);
    60. //注意:需要将文本域添加到滚动条中,实现滚动效果
    61. jsp =new JScrollPane(jta);
    62. //初始化面板
    63. jp = new JPanel();
    64. jtf = new JTextField(10);
    65. jb=new JButton("发送");
    66. //注意:需要将文本框与按钮添加到面板中
    67. jp.add(jtf);
    68. jp.add(jb);
    69. //注意:需要将滚动条与面板全部添加到窗体中,继承了窗体的属性,这里this就是窗体
    70. this.add(jsp, BorderLayout.CENTER);//BorderLayout--边框布局
    71. this.add(jp,BorderLayout.SOUTH);
    72. //注意:需要设置”标题“,大小,位置,关闭,是否可见
    73. this.setTitle("QQ聊天服务端");
    74. this.setSize(400,300);
    75. this.setLocation(700,300);
    76. this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序就退出
    77. this.setVisible(true);//设置窗体可见
    78. /**************** TCP 服务端 Start *********************/
    79. //给发送按钮绑定一个监听点击事件
    80. jb.addActionListener(this);
    81. //给文本框绑定一个键盘点击事件
    82. jtf.addKeyListener(this);
    83. try {
    84. //1.创建一个服务端的套接字
    85. ServerSocket serverSocket = new ServerSocket(8888);
    86. //2.等待客户端连接
    87. Socket socket = serverSocket.accept();
    88. //3.获取socket通道的输入流(输入六是实现读取数据的,一行一行读取)BufferedReader->readLine();
    89. //InputStream in = socket.getInputStream();
    90. BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
    91. //4.获取socket 通道的输出流(输出流实现写出数据,也是写一行换一行,刷新)BufferedWriter->newLine();
    92. //当用户点击发送按钮的时候写出数据
    93. bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    94. //循环读取数据并拼接到文本域
    95. String line = null;
    96. while((line = br.readLine())!=null){
    97. //将读取的数据拼接到文本域中显示
    98. jta.append(line+System.lineSeparator());
    99. }
    100. //5.关闭socket 通道
    101. serverSocket.close();
    102. }catch (IOException e) {
    103. e.printStackTrace();
    104. }
    105. /**************** TCP 服务端 end *********************/
    106. }
    107. @Override
    108. public void actionPerformed(ActionEvent actionEvent) {
    109. //System.out.println("发送按钮被点击了");
    110. sendDataToSocket();
    111. }
    112. //行为
    113. @Override
    114. public void keyPressed(KeyEvent e) {
    115. //回车键
    116. // System.out.println(e);
    117. //发送数据到socket 同道中
    118. if(e.getKeyCode()==KeyEvent.VK_ENTER) {
    119. sendDataToSocket();
    120. }
    121. }
    122. //定义一个方法,实现将数据发送到socket通道中
    123. private void sendDataToSocket(){
    124. //1.获取文本框中发送的内容
    125. String text = jtf.getText();
    126. //2.拼接需要发送的数据内容
    127. text = "服务端对客户端说:"+text;
    128. //3.自己也要显示
    129. jta.append(text+System.lineSeparator());
    130. //4.发送
    131. try {
    132. bw.write(text);
    133. bw.newLine();//换行刷新
    134. bw.flush();
    135. //5.清空文本框内容
    136. jtf.setText("");
    137. } catch (IOException e) {
    138. e.printStackTrace();
    139. }
    140. }
    141. @Override
    142. public void keyTyped(KeyEvent keyEvent) {
    143. }
    144. @Override
    145. public void keyReleased(KeyEvent keyEvent) {
    146. }
    147. }

    客户端:

    1. import javax.swing.JButton;
    2. import javax.swing.JFrame;
    3. import javax.swing.JPanel;
    4. import javax.swing.JScrollPane;
    5. import javax.swing.JTextArea;
    6. import javax.swing.JTextField;
    7. import java.awt.BorderLayout;
    8. import java.awt.event.ActionEvent;
    9. import java.awt.event.ActionListener;
    10. import java.awt.event.KeyEvent;
    11. import java.awt.event.KeyListener;
    12. import java.io.BufferedReader;
    13. import java.io.BufferedWriter;
    14. import java.io.IOException;
    15. import java.io.InputStreamReader;
    16. import java.io.OutputStreamWriter;
    17. import java.net.Socket;
    18. import java.util.Properties;
    19. //说明:如果一个类,需要有界面的显示,那么这个类就需要继承JFrame,此时,该类可以被成为一个窗体类
    20. /*步骤:
    21. 1.定义JFrame窗体中的组件
    22. 2.在构造方法中初始化窗口的组件
    23. */
    24. public class ClientChatMain extends JFrame implements ActionListener, KeyListener {
    25. public static void main(String[] args) {
    26. //调用构造方法
    27. new ClientChatMain();
    28. }
    29. //属性
    30. //文本域
    31. private JTextArea jta;
    32. //滚动条
    33. private JScrollPane jsp;
    34. //面板
    35. private JPanel jp;
    36. //文本框
    37. private JTextField jtf;
    38. //按钮
    39. private JButton jb;
    40. //s输出流
    41. private BufferedWriter bw =null;
    42. //客户端的IP地址
    43. // private static String clientIp;
    44. //客户端的port端口号
    45. // private static int clientPort;
    46. //静态代码块加载外部配置文件
    47. /* static{
    48. Properties prop = new Properties();
    49. try {
    50. prop.load(new FileReader("chat.properties"));
    51. clientIp = prop.getProperty("clientIp");
    52. clientPort =Integer.parseInt( prop.getProperty("clientPort"));
    53. } catch (IOException e) {
    54. e.printStackTrace();
    55. }
    56. }*/
    57. //构造方法
    58. public ClientChatMain(){
    59. //初始化组件
    60. jta = new JTextArea();
    61. //设置文本域默认不可编辑
    62. jta.setEditable(false);
    63. //注意:需要将文本域添加到滚动条中,实现滚动效果
    64. jsp =new JScrollPane(jta);
    65. //初始化面板
    66. jp = new JPanel();
    67. jtf = new JTextField(10);
    68. jb=new JButton("发送");
    69. //注意:需要将文本框与按钮添加到面板中
    70. jp.add(jtf);
    71. jp.add(jb);
    72. //注意:需要将滚动条与面板全部添加到窗体中,继承了窗体的属性,这里this就是窗体
    73. this.add(jsp, BorderLayout.CENTER);//BorderLayout--边框布局
    74. this.add(jp,BorderLayout.SOUTH);
    75. //注意:需要设置”标题“,大小,位置,关闭,是否可见
    76. this.setTitle("QQ聊天客户端");
    77. this.setSize(400,300);
    78. this.setLocation(700,300);
    79. this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序就退出
    80. this.setVisible(true);//设置窗体可见
    81. /**************** TCP 客户端 Start *********************/
    82. //给发送按钮绑定一个监听点击事件
    83. jb.addActionListener(this);
    84. //给文本框绑定一个键盘键
    85. jtf.addKeyListener(this);
    86. try {
    87. //1.创建一个客户端的套接字(尝试连接)
    88. Socket socket = new Socket("127.0.0.1",8888);
    89. //2.获取socket通道的输入流
    90. BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    91. //3.获取socket 通道的输出流
    92. bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    93. //循环读取数据,并拼接到文本域
    94. String line = null;
    95. while((line = br.readLine())!=null){
    96. jta.append(line + System.lineSeparator());
    97. }
    98. //4.关闭socket 通道
    99. socket.close();
    100. }catch (Exception e){
    101. e.printStackTrace();
    102. }
    103. /**************** TCP 客户端 end *********************/
    104. }
    105. @Override
    106. public void actionPerformed(ActionEvent e) {
    107. sendDataToSocket();
    108. }
    109. //行为
    110. @Override
    111. public void keyPressed(KeyEvent e) {
    112. //回车键
    113. if(e.getKeyCode() == KeyEvent.VK_ENTER){
    114. //发送数据
    115. sendDataToSocket();
    116. }
    117. }
    118. private void sendDataToSocket(){
    119. //1.获取文本框需要发送内容
    120. String text = jtf.getText();
    121. //2.拼接内容
    122. text = "客户端对服务端说"+text;
    123. //3.自己显示
    124. jta.append(text+System.lineSeparator());
    125. try {
    126. //4.发送
    127. bw.write(text);
    128. bw.newLine();//换行加刷新
    129. bw.flush();
    130. bw.write(text);
    131. //5.清空
    132. jtf.setText("");
    133. } catch (IOException e) {
    134. e.printStackTrace();
    135. }
    136. }
    137. @Override
    138. public void keyTyped(KeyEvent e) {
    139. }
    140. @Override
    141. public void keyReleased(KeyEvent e) {
    142. }
    143. }

    接口类:

    1. public interface addActistener {
    2. }
    1. public interface addActionListener {
    2. }

    90918e157acf4f61bbb7f4f478223ed9.gif#pic_center
    兄弟们,三连支持一下!!!

     

  • 相关阅读:
    Leetcode 2099. Find Subsequence of Length K With the Largest Sum [Python]
    Android监听应用切换到后台和前台
    Boost ASIO:Network programming
    Vivado使用记录(未完待续)
    【Docker】从零开始:4.为什么Docker会比VM虚拟机快
    1095 Cars on Campus
    栈题目:有效括号的嵌套深度
    C:vprintf/vfprintf/vdprintf/vsprintf/vsnprintf,对可变参数的支持
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java影院售票系统6fg71
    什么是枚举类型?如何定义和使用枚举?
  • 原文地址:https://blog.csdn.net/m0_68089732/article/details/124897209