//利用GUI和网络编程知识,建立一个简单的聊天程序。
package cn.itcast.udp.exercise;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.TextArea;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;
public class GUI_Chat extends Frame {
private static final long serialVersionUID = 1L;
private Button sendButton;
private Button clearButton;
private Button logButton;
private Button shakeButton;
private TextField textField;
private TextArea viewTextArea;
private TextArea sendTextArea;
private DatagramSocket socket;
private BufferedWriter writer;
/*
* 构造函数
* 初始化
* 生成2个Panel, 以及界面组件
* 处理事件
* 显示窗体
* 启动接收消息的线程
*/
public GUI_Chat() {
init(); // 设置标题, 大小, 位置, 处理关闭事件
generateSouthPanel(); // 生成那边的Panel, 包含TextField和4个Button
generateCenterPanel(); // 生成中间的Panel, 包含2个TextArea
handleChatEvent(); // 各种事件的处理
setVisible(true); // 显示
new ReceiveThread().start(); // 开启接收消息的线程
}
/*
* 初始化
* 设置标题, 大小, 位置
* 创建Socket和Writer
*/
private void init() {
setTitle("GUI聊天室");
setSize(400, 600);
setLocation(600, 50);
try {
socket = new DatagramSocket();
writer = new BufferedWriter(new FileWriter("msg.txt", true));
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 创建南边的Panel
* 其中包含用来输入IP的TextField和4个Button
*/
private void generateSouthPanel() {
textField = new TextField(20);
sendButton = new Button("发送");
clearButton = new Button("清屏");
logButton = new Button("记录");
shakeButton = new Button("震动");
Panel southPanel = new Panel(); // 用来布局的Panel, 默认流式布局. 文本框和按钮都放在Panel中
southPanel.add(textField);
southPanel.add(sendButton);
southPanel.add(clearButton);
southPanel.add(logButton);
southPanel.add(shakeButton);
add(southPanel, BorderLayout.SOUTH); // Panel放在Frame的南边
textField.setText("127.0.0.1");
}
/*
* 创建中间的Panel
* 其中包含2个TextView
*/
private void generateCenterPanel() {
viewTextArea = new TextArea();
sendTextArea = new TextArea(5, 1); // 指定行数和列数, 列数由于是边界布局所以无效
Panel centerPanel = new Panel(); // 把Panel改为边界布局, 1个TextArea在中间, 另一个在南边
centerPanel.setLayout(new BorderLayout());
centerPanel.add(viewTextArea, BorderLayout.CENTER);
centerPanel.add(sendTextArea, BorderLayout.SOUTH);
add(centerPanel, BorderLayout.CENTER); // Panel放在Frame的中间
viewTextArea.setEditable(false); // 用来显示的TextArea不能修改(会修改背景色为灰色)
viewTextArea.setBackground(Color.WHITE); // 背景色改回白色
}
/*
* 处理各种事件
* 窗体关闭
* 4个按钮
* 发送快捷键
*/
private void handleChatEvent() {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
try {
socket.close();
writer.close();
System.exit(0);
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
sendButton.addActionListener(new ActionListener() { // 发送按钮被点击的时候, 执行send()方法
public void actionPerformed(ActionEvent e) {
send();
}
});
clearButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
viewTextArea.setText("");
}
});
sendTextArea.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_ENTER || e.isAltDown() && e.getKeyCode() == KeyEvent.VK_S) {
e.consume(); // 取消当前事件
send();
}
}
});
logButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showLog();
}
});
shakeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sendData(new byte[] { -1 }, textField.getText());
}
});
}
/*
* 把指定的数据发送到指定的IP地址上
*/
private void sendData(byte[] data, String ip) {
try {
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName(ip), 6789); // 创建Packet
socket.send(packet);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 发送消息的方法
* 发送之后会在TextView显示
* 并且会保存聊天记录
*/
private void send() {
String text = sendTextArea.getText(); // 获取文本框中的文本
String ip = textField.getText(); // 获取IP地址
ip = ip.trim().length() == 0 ? "255.255.255.255" : ip; // IP为空时, 群发
try {
String dateStr = getCurrentTime(); // 获取当前时间
synchronized(this) { // 为了让发送消息并显示之后才执行接收端的显示, 所以同步
sendData(text.getBytes(), ip);
String msg = dateStr + " 我对 " + (ip.equals("255.255.255.255") ? "所有人" : ip) + " 说:\r\n" + text + "\r\n\r\n";
viewTextArea.append(msg); // 在TextArea中显示发送的消息
writer.write(msg); // 保存聊天记录
}
sendTextArea.setText(""); // 清空TextArea
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 获取当前时间
*/
private String getCurrentTime() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
/*
* 用来接收数据的线程
* 不停地接收数据, 显示到TextView, 并且保存聊天记录
*/
private class ReceiveThread extends Thread {
public void run() {
try {
DatagramSocket socket = new DatagramSocket(6789); // 接收端Socket
DatagramPacket packet = new DatagramPacket(new byte[1024000], 1024000); // 接收端Packet
while (true) { // 循环接收
socket.receive(packet); // 收到数据
byte[] data = packet.getData(); // 获取数据
int len = packet.getLength(); // 获取长度
if (len == 1 && data[0] == -1) { // 收到特殊的消息, 震动
shake();
continue;
}
String ip = packet.getAddress().getHostAddress(); // 获取IP
String text = new String(data, 0, len); // 把收到的数据转为String
String timeStr = getCurrentTime(); // 获取当前时间
synchronized(GUI_Chat.this) { // 为了确保在发送端显示之后再显示, 所以同步
String msg = timeStr + " " + ip + " 对我说:\r\n" + text + "\r\n\r\n";
viewTextArea.append(msg); // 把收到的数据显示
writer.write(msg); // 保存聊天记录
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* 震动
*/
private void shake() {
int x = getLocation().x; // 获取当前位置
int y = getLocation().y;
try {
setLocation(x + 20, y + 20);
Thread.sleep(20);
setLocation(x - 20, y - 20);
Thread.sleep(20);
setLocation(x - 20, y + 20);
Thread.sleep(20);
setLocation(x + 20, y - 20);
Thread.sleep(20);
setLocation(x, y);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
/*
* 显示聊天记录
* 从文件中读取数据, 显示到TextView
*/
private void showLog() {
try {
viewTextArea.setText(""); // 清空viewTextArea
writer.flush(); // 刷出缓冲区中数据
FileInputStream fis = new FileInputStream("msg.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) != -1) // 从文件读取数据到内存
baos.write(buffer, 0, len);
fis.close();
baos.close();
String log = new String(baos.toByteArray()); // 把内存中数据转为String
viewTextArea.setText(log); // 显示到viewTextArea
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 测试方法, 创建聊天室对象
*/
public static void main(String[] args) {
new GUI_Chat();
}
}