java程序实现聊天互动(二)
解决 java程序实现聊天互动(一) 硬编码问题
将服务端和客户端的端口号和ip放置在配置文件中,同时在ServerChat.java和ClientChat.java使用static代码块
编写两个java文件:
ServerChat.java文件
ClientChat.java文件
windows + R 开启cmd黑窗口
在cmd黑窗口中运行:注意开启两个cmd窗口,称第一个为serverCMD,第二个为clientCMD
serverCMD:
跳转到ServerChat.java文件所在的文件夹
cd D:\otest
编译ServerChat.java文件,编译后会新增一个ServerChat.class文件
javac ServerChat.java
运行ServerChat.class文件,注意运行时不要有 .class
java ServerChat
clientCMD:
跳转到ClientChat.java文件所在的文件夹
cd D:\otest
编译ClientChat.java文件,编译后会新增一个ClientChat.class文件
javac ClientChat.java
运行ClientChat.class文件,注意运行时不要有 .class
java ClientChat
chat.properties文件
serverPort=8888 clientIp=127.0.0.1 clientPort=8888
ServerChat.java文件
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.实现"发送"按钮的监听点击事件 //需要注意:文本域拼接数据时,实现自己换行,不要在发送数据中使用System.lineSeparator()换行 //这样会导致bw.newLine()与System.lineSeparator()两个换行导致使用BufferedReader读取时,无法正确的辨别需要的换行 //5.实现"回车键"发送数据 public class ServerChat extends JFrame implements ActionListener, KeyListener { public static void main(String[] args) { //调用构造方法 new ServerChat(); } //属性 //文本域 private JTextArea jta; //滚动条 private JScrollPane jsp; //面板 private JPanel jp; //文本框 private JTextField jtf; //按钮 private JButton jb; //输出流 private BufferedWriter bw = null; //服务端的端口号 private static int serverPort; //使用static 静态代码块读取外部配置文件 static { Properties p = new Properties(); try { //加载 p.load(new FileReader("chat.properties")); //赋值属性值 serverPort = Integer.parseInt(p.getProperty("serverPort")); } catch (IOException e) { e.printStackTrace(); } } //构造方法 public ServerChat() { //初始化组件 //文本域 jta = new JTextArea(); //注意:需要将文本域添加到滚动条中,实现滚动效果 //滚动条 jsp = new JScrollPane(jta); //面板 jp = new JPanel(); //文本框 jtf = new JTextField(10);//文本框大小 //按钮 jb = new JButton("发送");//按钮名称 //注意:需要将文本框与按钮添加到面板中 jp.add(jtf); jp.add(jb); //注意:需要将滚动条和面板添加到窗体中 this.add(jsp, BorderLayout.CENTER); this.add(jp,BorderLayout.SOUTH);//上北下南 //注意:需要设置 标题、大小、位置、关闭、是否可见 this.setTitle("聊天框 服务");//标题 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.创建服务端套接字 ServerSocket serverSocket = new ServerSocket(serverPort); //2.等待客户端连接 Socket socket = serverSocket.accept(); //3.获取socket 通道的输入流(输入流是实现读取数据,一行一行读取) InputStream in = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); //4.获取socket 通道的输出流(输出流实现写出数据,也是写一行,换一行,刷新) OutputStream out = socket.getOutputStream(); bw = new BufferedWriter(new OutputStreamWriter(out)); //循环读取数据,并拼接到文本域中 String line = null; while((line = br.readLine()) != null) { //将读取的数据拼接到文本域中显示 jta.append(line + System.lineSeparator());//换行 } //5.关闭serverSocket 通道 serverSocket.close(); } catch(IOException e) { e.printStackTrace(); } /******************* TCP 服务端 end ****************************/ } @Override public void actionPerformed(ActionEvent event) { //发送数据到socket通道中 sendDataToSocket(); } @Override public void keyPressed(KeyEvent e) { if(e.getKeyCode() == KeyEvent.VK_ENTER) { //发送数据到socket通道中 sendDataToSocket(); } } @Override public void keyTyped(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { } //将数据发送到socket通道中 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 (IOException el) { el.printStackTrace(); } } }
ClientChat.java文件
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.在构造方法中初始化窗体的组件 //3.使用网络编程完成数据的传输(TCP,UDP 协议) //4.实现"发送"按钮的监听点击事件 //5.实现"回车键"发送数据 public class ClientChat extends JFrame implements ActionListener, KeyListener { public static void main(String[] args) { //调用构造方法 new ClientChat(); } //属性 //文本域 private JTextArea jta; //滚动条 private JScrollPane jsp; //面板 private JPanel jp; //文本框 private JTextField jtf; //按钮 private JButton jb; //输出流 private BufferedWriter bw = null; //客户端IP private static String clientIp; //客户端端口号 private static int clientPort; //使用static 静态代码块读取外部配置文件 static { Properties p = new Properties(); try { //加载 p.load(new FileReader("chat.properties")); //赋值属性值 clientPort = Integer.parseInt(p.getProperty("clientPort")); clientIp = p.getProperty("clientIp"); } catch (IOException e) { e.printStackTrace(); } } //构造方法 public ClientChat() { //初始化组件 //文本域 jta = new JTextArea(); //注意:需要将文本域添加到滚动条中,实现滚动效果 //滚动条 jsp = new JScrollPane(jta); //面板 jp = new JPanel(); //文本框 jtf = new JTextField(10);//文本框大小 //按钮 jb = new JButton("发送");//按钮名称 //注意:需要将文本框与按钮添加到面板中 jp.add(jtf); jp.add(jb); //注意:需要将滚动条和面板添加到窗体中 this.add(jsp, BorderLayout.CENTER); this.add(jp,BorderLayout.SOUTH);//上北下南 //注意:需要设置 标题、大小、位置、关闭、是否可见 this.setTitle("聊天框 服务");//标题 this.setSize(300,300);// 宽 高 this.setLocation(700,300);// 水平 垂直 this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//窗体关闭 程序退出 this.setVisible(true);//是否可见 /******************* TCP 服务端 start ****************************/ //给"发送"按钮绑定一个监听点击事件 jb.addActionListener(this);//让当前对象监听 //给文本框绑定一个键盘点击事件 jtf.addKeyListener(this); try { //1.创建服务端套接字 Socket socket = new Socket(clientIp,clientPort); //2.获取socket 通道的输入流(输入流是实现读取数据,一行一行读取) InputStream in = socket.getInputStream(); BufferedReader br = new BufferedReader(new InputStreamReader(in)); //3.获取socket 通道的输出流(输出流实现写出数据,也是写一行,换一行,刷新) OutputStream out = socket.getOutputStream(); bw = new BufferedWriter(new OutputStreamWriter(out)); //循环读取数据,并拼接到文本域中 String line = null; while((line = br.readLine()) != null) { //将读取的数据拼接到文本域中显示 jta.append(line + System.lineSeparator());//换行 } //4.关闭serverSocket 通道 socket.close(); } catch(IOException e) { e.printStackTrace(); } /******************* TCP 服务端 end ****************************/ } @Override public void actionPerformed(ActionEvent event) { //发送数据到socket通道中 sendDataToSocket(); } @Override public void keyPressed(KeyEvent e) { if(e.getKeyCode() == KeyEvent.VK_ENTER) { //发送数据到socket通道中 sendDataToSocket(); } } @Override public void keyTyped(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { } //将数据发送到socket通道中 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 (IOException el) { el.printStackTrace(); } } }