使用udp创建一个简单聊天工具

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
package com.diedline.socket;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.Date;

import static javax.swing.SwingConstants.SOUTH;

public class Demo4_GUIChat extends Frame {

private TextField tf;
private Button send;
private Button log;
private Button clear;
private Button shake;
private TextArea viewText;
private TextArea sendText;
private DatagramSocket socket;
private BufferedWriter bw;
private String str;

/**
* GUI聊天
*
* @param
*/
public Demo4_GUIChat() throws InterruptedException, IOException {
init();
// Thread.sleep(10);
centerPanel();
// Thread.sleep(10);
southPanel();
event();

}

public void event() {
this.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
socket.close(); //在退出虚拟机之前把socket关闭
try {
bw.close(); // 将文件关闭
} catch (IOException e1) {
e1.printStackTrace();
}
System.exit(0);
}
});
send.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
send();
} catch (SocketException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
log.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
logFile();
} catch (IOException e1) {
e1.printStackTrace();
}
}
});

clear.addActionListener(new ActionListener() { //清屏
@Override
public void actionPerformed(ActionEvent e) {
viewText.setText("");
}
});
//震动方法
shake.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
send(new byte[]{-1}, tf.getText()); //发送-1字节
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
//快捷键功能
sendText.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
// if(e.getKeyCode() == KeyEvent.VK_ENTER && e.isControlDown() ){ //回车和ctrl同时按下的情况
if (e.getKeyCode() == KeyEvent.VK_ENTER) { //回车按下的情况
try {
send();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
});
}

//震动功能
private void shake() throws InterruptedException {
int x = this.getLocation().x; //获取横坐标位置
int y = this.getLocation().y; //获取纵坐标位置
for (int i = 0; i < 10; i++) {
this.setLocation(x + 20, y + 20);
Thread.sleep(20);
this.setLocation(x + 20, y - 20);
Thread.sleep(20);
this.setLocation(x - 20, y + 20);
Thread.sleep(20);
this.setLocation(x - 20, y - 20);
Thread.sleep(20);
this.setLocation(x, y);
}

}


//历史记录功能
private void logFile() throws IOException {
bw.flush(); //刷新缓存区
FileInputStream fis = new FileInputStream("config.txt");
ByteArrayOutputStream baos = new ByteArrayOutputStream(); //在内存中创建缓冲区
int len;
byte[] arr = new byte[8192];
while ((len = fis.read(arr)) != -1) {
baos.write(arr, 0, len);
}
String str = baos.toString(); //将内存中的内容转换成字符串
viewText.setText(str); //设置聊天记录
fis.close(); //关闭内存输出流
}

//重载的发送方法
private void send(byte[] arr, String ip) throws IOException {
DatagramPacket packet = //创建数据包 设定数据,大小,和端口号
new DatagramPacket(arr, arr.length, InetAddress.getByName(ip), 9999);
socket.send(packet); //将数据发送出去
}


//发送功能
private void send() throws IOException {
String message = sendText.getText(); //获取发送区域的内容
String ip = tf.getText(); //获取 ip
//如果没有ip写着就将默认ip设置为255.255.255.255
ip = ip.trim().length() == 0 ? "255.255.255.255" : ip;
send(message.getBytes(), ip); //调用 重载的send方法
String time = getCurrentTime(); //获取当前时间

str = time + "我对" + (ip.equals("255.255.255.255")?"所有人":ip) + "说:" + "\r\n" + message + "\r\n";
bw.write(str); //将信息写到数据库中 (现在是文本文档模拟)
viewText.append(str); //将信息添加到显示区域中
sendText.setText(null); //清除输入
}

private String getCurrentTime() {
Date d = new Date(); //创建当前日期对象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
return sdf.format(d); //将时间格式化
}


public void centerPanel() {
Panel center = new Panel(); //创建中间的panel
//显示的文本区域
viewText = new TextArea();
viewText.setEditable(false); //设置成不可编辑
viewText.setBackground(Color.white); //设置背景颜色
//发送的文本区域
sendText = new TextArea(5, 1);
center.setLayout(new BorderLayout()); //设置边界布局管理器
center.add(sendText, BorderLayout.SOUTH); //发送文本区域放在南边
center.add(viewText, BorderLayout.CENTER); //显示区域放在中间
this.add(center, BorderLayout.CENTER);
sendText.setFont(new Font("xxx", Font.PLAIN, 15));
viewText.setFont(new Font("xxx", Font.PLAIN, 15));
this.setVisible(true);
}

public void southPanel() {
Panel south = new Panel(); //创建南边的panel
//创建文本字段 存储ip地址
tf = new TextField(15);
tf.setText("127.0.0.1");
//创建发送按钮
send = new Button("发送");
//创建记录
log = new Button("记录");
//清屏
clear = new Button("清屏");
//创建震动按钮
shake = new Button("震动");
south.add(tf);
south.add(send);
south.add(log);
south.add(clear);
south.add(shake);
this.add(south, BorderLayout.SOUTH); //将Panel放在Frame的南边
this.setVisible(true);
}

public void init() throws IOException {
this.setLocation(500, 50);
this.setSize(400, 600);
socket = new DatagramSocket(); //创建socket 使用随机端口号
bw = new BufferedWriter(new FileWriter("config.txt", true)); //需要在尾部追加
new Receive().start(); //开启接收线程
this.setVisible(true);
}

//接收和发送需要同时执行 所以定义成多线程的
private class Receive extends Thread {
private String str;

@Override
public void run() {
try {
DatagramSocket socket = new DatagramSocket(9999);
DatagramPacket packet = new DatagramPacket(new byte[8192], 8192);
while (true) {
socket.receive(packet); //接收信息
byte[] arr = packet.getData(); //获取字节数据
int len = packet.getLength(); //获取有效的字节数据
if (arr[0] == -1 && len == 1) { //如果发过来的数组的第一个存贮的值是-1并且数组长度是-1
shake(); //调用震动方法
continue; //中止本次循环 进行下次循环 因为震动不需要使用下面的方法
}
String message = new String(arr, 0, len); //转换成字符串
String time = getCurrentTime(); //获取当前时间
String ip = packet.getAddress().getHostAddress(); //获取ip地址
str = time + " " + ip + " 对我说:\r\n" + message + "\r\n";
viewText.append(str);
bw.write(str); //将信息写入数据库
}
} catch (SocketException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}

}
}


//主方法 调用构造方法
public static void main(String[] args) throws InterruptedException, IOException {
new Demo4_GUIChat();

}
}