说明:如果一个 类,需要有界面的显示,那么该类就需要继承自JFrame,此时,该类就可以被称为一个“窗体类"。
服务端代码:
package cn.qy.chat;import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Properties;//说明:如果一个类,需要有界面的显示,那么该类就需要继承自JFrame,此时,该类就可以被称为一个“窗体类"
//1.定义JFrame窗体中的组件
//2.在构造方法中初始化组件
//3.使用网络编程完成数据的传输(TCP,UDP协议)
//4.实现“发送”按钮的监听点击事件。需要注意的是,文本域拼接数据时,需要自己换行,不要在发送数据时换行。
public class ServerChatMain extends JFrame implements ActionListener, KeyListener {public static void main(String[] args) {// 调用构造器new ServerChatMain();}//属性//文本域private JTextArea jta;//滚动条private JScrollPane jsp;//面板private JPanel jp;//文本框private JTextField jtf;//按钮private JButton jb;//流BufferedReader br;BufferedWriter bw;//服务端端口号private static int serverPort;//静态代码块加载外部配置文件//特点1:在类加载时,自动执行//特点2:一个类只会加载一次,因此静态代码块只会执行一次static {Properties prop = new Properties();try {prop.load(new FileReader("chat.properties"));serverPort =Integer.parseInt(prop.getProperty("serverPort"));} catch (IOException e) {e.printStackTrace();}}//创建空参构造器public ServerChatMain(){//初始化组件//初始化文本域jta = new JTextArea();//设置文本域不可编辑jta.setEditable(false);//将文本域添加到滚动条中,实现滚动效果jsp = new JScrollPane(jta);//面板jp = new JPanel();jtf = new JTextField(15);jb = new JButton("发送");//将文本框与按钮添加到面板中jp.add(jtf);jp.add(jb);//将滚动条与面板全部添加到窗体中this.add(jsp, BorderLayout.CENTER);this.add(jp, BorderLayout.SOUTH);//设置 标题,大小,位置,关闭,是否可见this.setTitle("QQ 服务端");this.setSize(300,300);this.setLocation(600,300);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序退出this.setVisible(true);/******************TCP 服务端 start*****************///给发送按钮绑定一个监听点击事件jb.addActionListener(this);//给文本框绑定一个键盘点击事件jtf.addKeyListener(this);//接收客户端数据,并回显到自己的文本域try {//1.创建一个服务端的套接字socketServerSocket serverSocket = new ServerSocket(serverPort);//2.等待客户端的链接Socket socket = serverSocket.accept();//客户端对象//3.获取socket 通道的输入流(输入流是实现读取数据的,一行一行读取,因此用BufferedReader-->readLinebr = new BufferedReader(new InputStreamReader(socket.getInputStream()));//4.获取socket 通道的输出流(输出流实现写出数据,也是写一行,换一行,刷新)BufferedWriter-->new Line()//什么时候需要写出数据?当用户点击发送按钮时bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//循环读取数据,并把它拼接到文本域中String line = null;while ((line=br.readLine())!=null){//将读取的数据拼接到文本域中显示jta.append(line+System.lineSeparator());}//5.关闭serverSocket 通道serverSocket.close();} catch (IOException e) {e.printStackTrace();}/******************TCP 服务端 end*****************/}//行为/*** 重新ActionListener中的1个方法,实现点击 发送键 发送* @param e*/@Overridepublic void actionPerformed(ActionEvent e) {sendDataToSocket();}/*** 重写KeyListener中的3个方法,实现Enter键发送* @param e*/@Overridepublic void keyPressed(KeyEvent e) {//回车键if (e.getKeyCode()== KeyEvent.VK_ENTER){//发送数据到socket通道sendDataToSocket();}}@Overridepublic void keyTyped(KeyEvent e) {}@Overridepublic void keyReleased(KeyEvent e) {}//方法功能:发送数据到客户端,并将内容回显到自己的文本域public void sendDataToSocket(){//1.获取文本框中的内容String text = jtf.getText();//2.拼接需要发送的数据内容text = "服务端对客户端说:"+text;//3.自己也需要显示jta.append(text+System.lineSeparator());try {//4.发送bw.write(text);bw.newLine();bw.flush();//5.清空文本框jtf.setText("");} catch (Exception exception) {exception.printStackTrace();}}
}
客户端代码:
package cn.qy.chat;import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.*;
import java.net.Socket;
import java.util.Properties;//说明:如果一个类,需要有界面的显示,那么该类就需要继承自JFrame,此时,该类就可以被称为一个“窗体类
//1.定义JFrame窗体中的组件
//2.在构造方法中初始化组件
public class ClientChatMain extends JFrame implements ActionListener, KeyListener {public static void main(String[] args) {// 调用构造器new ClientChatMain();}//属性//文本域private JTextArea jta;//滚动条private JScrollPane jsp;//面板private JPanel jp;//文本框private JTextField jtf;//按钮private JButton jb;//流BufferedReader br;BufferedWriter bw;//通信的IP和Portprivate static int clientPort;private static String clientIP;//静态代码块加载外部配置文件//特点1:在类加载时,自动执行//特点2:一个类只会加载一次,因此静态代码块只会执行一次static {Properties prop = new Properties();try {prop.load(new FileReader("chat.properties"));clientPort = Integer.parseInt(prop.getProperty("clientPort"));clientIP = prop.getProperty("clientIP");} catch (IOException e) {e.printStackTrace();}}//创建构造器public ClientChatMain(){//初始化组件//初始化文本域jta = new JTextArea();//设置文本域不可编辑jta.setEditable(false);//将文本域添加到滚动条中,实现滚动效果jsp = new JScrollPane(jta);//面板jp = new JPanel();jtf = new JTextField(15);jb = new JButton("发送");//将文本框与按钮添加到面板中jp.add(jtf);jp.add(jb);//将滚动条与面板全部添加到窗体中this.add(jsp, BorderLayout.CENTER);this.add(jp, BorderLayout.SOUTH);//设置 标题 大小,位置,关闭,是否可见this.setTitle("QQ 客户端");this.setSize(300,300);this.setLocation(300,300);this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭,程序退出this.setVisible(true);/******************TCP 客户端 start*****************///给发送按钮绑定一个监听点击事件jb.addActionListener(this);//给键盘绑定单击事件jtf.addKeyListener(this);//接受服务端数据,并回显到自己的文本域try {//1.创建一个客户端的套接字socketSocket socket = new Socket(clientIP, clientPort);//2.获取socket 通道的输入流br = new BufferedReader(new InputStreamReader(socket.getInputStream()));//3.获取socket 通道的输出流bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));//循环读取数据,并把它拼接到文本域中String line =null;while ((line=br.readLine())!=null){jta.append(line+System.lineSeparator());}//4.关闭socket 通道socket.close();} catch (Exception e) {e.printStackTrace();}/******************TCP 客户端 end*****************/}//行为/*** 重新ActionListener中的1个方法,实现点击 发送键 发送* @param e*/@Overridepublic void actionPerformed(ActionEvent e) {sendDataToSocket();}/*** 重写KeyListener中的3个方法,实现Enter键发送* @param e*/@Overridepublic void keyPressed(KeyEvent e) {if (e.getKeyCode()==KeyEvent.VK_ENTER){sendDataToSocket();}}@Overridepublic void keyTyped(KeyEvent e) {}@Overridepublic void keyReleased(KeyEvent e) {}//方法功能:发送数据到服务端,并将内容回显到自己的文本域private void sendDataToSocket(){//1.获取文本框中的内容String text = jtf.getText();//2.拼接需要发送的数据内容text = "客户端对服务端说:"+text;//3.自己也需要显示jta.append(text+System.lineSeparator());try {//4.发送bw.write(text);bw.newLine();bw.flush();//5.清空文本框jtf.setText("");} catch (Exception exception) {exception.printStackTrace();}}
}
启动时,必须先启动服务端,再启动客户端。
最终效果: