黑马程序员技术交流社区
标题:
UDP协议之简洁GUI聊天室
[打印本页]
作者:
shiweiCao
时间:
2013-6-7 19:34
标题:
UDP协议之简洁GUI聊天室
本帖最后由 曹睿翔 于 2013-6-8 08:30 编辑
<P> package com.itheima.udp.exercise;
import java.awt.BorderLayout;
import java.awt.Button;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Panel;
import java.awt.Point;
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 TextArea viewTextArea = new TextArea(); // 用来显示聊天记录的文本域
private TextArea sendTextArea = new TextArea(5, 1); // 用来输入消息的文本域(指定行数和列数,
// 列数由于是边界布局所以无效)
private TextField textField = new TextField(20); // 用来输入IP的文本框
private Button sendButton = new Button("发送");
private Button clearButton = new Button("清屏");
private Button logButton = new Button("记录");
private Button shakeButton = new Button("震动");
private Panel southPanel = new Panel(); // 放在南边的面板(TextField, 4个Button)
private Panel cenerPanel = new Panel(); // 放在中间的面板(2个TextArea)
private DatagramSocket socket;
private BufferedWriter bw;
/*
* 构造函数
*/
public GUI_Chat() {
init(); // 初始化
generateSouthPanel(); // 构造南边的面板
generateCenterPanel(); // 构造中间的面板
addListener(); // 添加监听器
setVisible(true); // 显示
new ReceiveThread().start(); // 开启接收数据的线程
}
/*
* 初始化 设置Frame的标题, 大小, 位置 创建了Socket对象
*/
private void init() {
setTitle("GUI聊天室");
setSize(400, 600);
setLocation(800, 50);
setMinimumSize(new Dimension(400, 300));
try {
socket = new DatagramSocket(); // 发送端Socket可以不用指定地址(本机)和端口(随机的)
bw = new BufferedWriter(new FileWriter("log.txt", true));
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 构造南边的面板 把TextField和4个Button一起放入Panel 把Panel放在南边
*/
private void generateSouthPanel() {
southPanel.add(textField);
southPanel.add(sendButton);
southPanel.add(clearButton);
southPanel.add(logButton);
southPanel.add(shakeButton);
add(southPanel, BorderLayout.SOUTH); // 把面板放在Frame的南边
}
/*
* 构造中间的面板 把两个TextArea装入Panel 把Panel放在中间
*/
private void generateCenterPanel() {
cenerPanel.setLayout(new BorderLayout()); // 把面板改为边界布局
cenerPanel.add(viewTextArea, BorderLayout.CENTER); // 用来显示的文本域放在中间
cenerPanel.add(sendTextArea, BorderLayout.SOUTH); // 用来发送的文本域放在下面
add(cenerPanel, BorderLayout.CENTER); // 把整个面板放在Frame的中间
viewTextArea.setEditable(false); // 设置为不可编辑, 会改变背景色为灰色
viewTextArea.setBackground(Color.WHITE); // 设置背景色为白色
}
/*
* 给各个组件添加添加监听器
*/
private void addListener() {
addWindowListener(new WindowAdapter() { // 关闭窗体时, 释放资源, 退出程序
public void windowClosing(WindowEvent e) {
try {
bw.close();
socket.close();
System.exit(0);
} catch (IOException ex) {
ex.printStackTrace();
}
}
});
sendButton.addActionListener(new ActionListener() { // 点击发送按钮时
public void actionPerformed(ActionEvent e) {
send();
}
});
clearButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
viewTextArea.setText("");
}
});
logButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
showLog();
}
});
sendTextArea.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
if (e.isControlDown() && e.getKeyCode() == KeyEvent.VK_ENTER
|| e.isAltDown() && e.getKeyCode() == KeyEvent.VK_S) {
send();
e.consume(); // 取消这个事件
}
}
});
shakeButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
sendShake();
}
});
}
private void sendShake() {
try {
String ip = textField.getText().trim();
DatagramPacket packet = new DatagramPacket(new byte[] { -1 }, 1,
InetAddress.getByName(ip), 20000);
socket.send(packet);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 发送消息
*/
private void send() {
String msg = sendTextArea.getText(); // 获取要发送的内容
String ip = textField.getText().trim(); // 获取目标地址
ip = ip.length() == 0 ? "255.255.255.255" : ip; // 如果没填IP, 默认改为群发地址
try {
DatagramPacket packet = new DatagramPacket(msg.getBytes(),
msg.getBytes().length, InetAddress.getByName(ip), 20000); // 创建Packet
String time = getTimeString();
String info = time + " 我对 "
+ (ip.equals("255.255.255.255") ? "所有人" : ip) + " 说:\r\n"
+ msg + "\r\n\r\n";
synchronized (this) {
socket.send(packet); // 发送
viewTextArea.append(info); // 显示发送的内容到TextView
bw.write(info); // 保存聊天记录
}
sendTextArea.setText(""); // 清空文本域
sendTextArea.requestFocus(); // 控制文本域重新获得焦点
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* 获取当前时间字符串
*/
private String getTimeString() {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(date);
}
/*
* 用来接收数据的线程 不停地循环接收数据 收到数据后显示到TextArea中
*/
private class ReceiveThread extends Thread {
public void run() {
try {
DatagramSocket socket = new DatagramSocket(20000);
DatagramPacket packet = new DatagramPacket(
new byte[1024 * 1024], 1024 * 1024);
while (true) {
socket.receive(packet); // 收取数据
byte[] arr = packet.getData(); // 获取packet中的数据
int len = packet.getLength(); // 获取长度
if (len == 1 && arr[0] == -1) {
shake();
continue;
}
String msg = new String(arr, 0, len); // 把字节数据转为String
String ip = packet.getAddress().getHostAddress(); // 从packet中获取发送端的获取IP
String time = getTimeString(); // 获取当前时间
String info = time + " " + ip + " 对我说:\r\n" + msg
+ "\r\n\r\n";
synchronized (GUI_Chat.this) { // 用外部类对象当作锁对象
viewTextArea.append(info); // 显示到TextView
bw.write(info); // 保存聊天记录
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*
* 从文件中读取聊天记录 显示到TextArea中
*/
private void showLog() {
try {
bw.flush(); // 把缓冲区中数据刷出
FileInputStream fis = new FileInputStream("log.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()); // 从内存获取数据转为字符串
viewTextArea.setText(log); // 显示
} catch (Exception e) {
e.printStackTrace();
}
}
private void shake() {
try {
Point p = getLocation();
setLocation(p.x - 20, p.y - 20);
Thread.sleep(20);
setLocation(p.x + 20, p.y + 20);
Thread.sleep(20);
setLocation(p.x + 20, p.y - 20);
Thread.sleep(20);
setLocation(p.x - 20, p.y + 20);
Thread.sleep(20);
setLocation(p);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new GUI_Chat();
}
}
</P>
复制代码
作者:
曹睿翔
时间:
2013-6-7 22:07
用代码块会好看点!
作者:
shiweiCao
时间:
2013-6-8 01:39
曹睿翔 发表于 2013-6-7 22:07
用代码块会好看点!
嗯,谢谢, 以后会注意的.
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/)
黑马程序员IT技术论坛 X3.2