Java实现Socket网络编程(四)
2016-04-09 本文已影响362人
Louis_陆
在看到本文之前,如果读者没看过笔者的前文Java实现Socket网络编程(三) ,请先翻阅。
下面,我们来实现服务器单体发送和广播发送:
我们为JList客户端列表设置监听,在每次点击触发时,先默认设置所有Socket未选中,避免错乱,然后获取全部已选中下标,并做好标记。笔者此处采用HashMap存储Socket,如Socket被选中,其对应Value为true,反之为false。
// 添加监听
clientList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
// 先默认把所有Socket未选中
resetSocket(ListenThread.clientSockets);
// 获取全部已选中下标
int[] selecteds = clientList.getSelectedIndices();
for (int i = 0; i < selecteds.length; i++) {
// 获取所选中项的HashMap
HashMap<Socket, Boolean> map = ListenThread.clientSockets
.get(selecteds[i]);
// 用迭代器获取HashMap的Key,即所选中的Socket
Iterator iter = map.entrySet().iterator();
Map.Entry<Socket, Boolean> entry = (Entry<Socket, Boolean>) iter
.next();
Socket key = (Socket) entry.getKey();
// 把所选中的Socket设置为true
map.replace(key, true);
}
}
});
然后,在点击发送消息的按钮时,根据之前在JList中标记的Socket,向指定客户端发送消息。
// 设置发送消息监听
jbSendMessage.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
if (jtaSendMessage.getText().equals("")) {
JOptionPane.showMessageDialog(null, "发送内容不能为空!");
return;
}
if (jtaSendMessage.getText().length() > 5000) {
JOptionPane.showMessageDialog(null, "发送数据过大,最大不能超过5000字!");
return;
}
// 取得要发送的消息
// 代表服务器正常连接
String message = Common.OK;
String t = "server " + Common.IP + ":" + Common.PORT + " "
+ jtaSendMessage.getText();
OutputStreamWriter outstream = null;
// 将信息发送给每个选中的客户端
for (int i = 0; i < ListenThread.clientSockets.size(); i++) {
try {
HashMap<Socket, Boolean> map = ListenThread.clientSockets
.get(i);
// 用迭代器获取HashMap的Key,即所选中的Socket
Iterator iter = map.entrySet().iterator();
Map.Entry<Socket, Boolean> entry = (Entry<Socket, Boolean>) iter
.next();
// 如果Socket已选中
if ((Boolean) entry.getValue()) {
Socket key = (Socket) entry.getKey();
outstream = new OutputStreamWriter(key
.getOutputStream(), "GBK");
outstream.write(message);
outstream.flush();
}
} catch (IOException e1) {
if (outstream != null)
try {
outstream.close();
} catch (IOException e2) {
e2.printStackTrace();
}
e1.printStackTrace();
}
}
// 清空文本
jtaSendMessage.setText(null);
}
});
为了实现客户端自动连接服务器,我们要为客户端实现一个子线程,用于轮询检测服务器是否已启动:
public void run() {
while (true) {
// 窗口关闭则直接退出循环,避免在关闭过程中再次连接服务器
if (ClientMain.isWindowClosing)
break;
try {
if (ClientMain.mSocket == null || ClientMain.mSocket.isClosed()) {
ClientMain.isConnected = false;
ClientMain.jlConnect.setText("Out Of Connect.");
}
if (!ClientMain.isConnected) {
Socket socket = new Socket(Common.IP, Common.PORT);
ClientMain.mSocket = socket;
// 启动客户端连接子线程
new Thread(new ClientReceivedThread(socket)).start();
ClientMain.isConnected = true;
ClientMain.jlConnect.setText("Success Connect.");
}
} catch (Exception e) {
// 无法连接服务器
ClientMain.isConnected = false;
ClientMain.jlConnect.setText("Out Of Connect.");
e.printStackTrace();
}
}
}
在这里,读者可能会遇到一个问题,客户端是如何检测到服务器断开,而服务器又如何检测到客户端断开呢?
笔者为读者提供两种方法:
1、检测读写错误,在BufferedReader的read()方法中,如果发生读写错误,便能捕获到。
2、发送心跳包检测
对于方法1,服务器和客户端实现的方法一样
对于方法2,服务器应发送心跳包0,客户端应发送心跳到0xFF
mSocket.sendUrgentData(0);//服务器检测客户端断开
mSocket.sendUrgentData(0xFF);//客户端检测服务器断开
当捕获到异常时,便能得知对方断开。