客户端登录功能
在线人数实时更新
离线人数更新
群聊
私聊
@消息
消息用户和消息时间点
服务端代码
package com.learnclub.chat;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import com.learnclub.utils.Constants;
public class ServerChat {
// 用于存储所有的在线客户端socket及用户名
public static Map<Socket, String> onLineSockets = new HashMap<Socket, String>();
public static void main(String[] args) {
try {
// 监听指定端口
ServerSocket ss = new ServerSocket(Constants.PORT);
while(true) {
// 与客户端建立连接
Socket socket = ss.accept();
为每个客户端socket创建一个线程处理
new ServerReaderThread(socket).start();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
线程处理客户端消息的转发与用户在线的实时维护实现代码
package com.learnclub.chat;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Set;
import com.learnclub.utils.Constants;
// 线程实现
public class ServerReaderThread extends Thread {
private Socket socket;
public ServerReaderThread(Socket socket) {
this.socket = socket;
}
public void run() {
DataInputStream dis = null;
try {
// 从socket中获取当前客户端输入流
dis = new DataInputStream(socket.getInputStream());
while(true) {
// 获取标识
int flag = dis.readInt();
// 标识为1标识发送的是用户名
if (flag == 1) {
// 获取用户名
String name = dis.readUTF();
// 输出用户名及其ip地址
System.out.println(name + "------------>" + socket.getRemoteSocketAddress());
// 讲客户端及其socket与用户名进行映射并存储
ServerChat.onLineSockets.put(socket, name);
}
// 发送消息
writeMsg(flag, dis);
}
} catch (IOException e) {
e.printStackTrace();
System.out.println("当前有人下线!");
// 客户端下线时移除其socket及用户名
ServerChat.onLineSockets.remove(socket);
try {
// 发送用户下线消息
writeMsg(1, dis);
} catch (Exception e2) {
e.printStackTrace();
}
}
}
/**
* 发送消息到客户端
* @param flag 消息标识
* @param dis 数据输入流
*/
private void writeMsg(int flag, DataInputStream dis) {
String msg = null;
try {
if (flag == 1) {
StringBuilder rs = new StringBuilder();
// 获取所有在线的用户名
Collection<String> onlineNames = ServerChat.onLineSockets.values();
if (onlineNames != null && onlineNames.size() > 0) {
for (String name : onlineNames) {
// 将用户与用户之间通过分隔符进行分隔
rs.append(name + Constants.SPILIT);
}
// 截取去除最后一个分隔符
msg = rs.substring(0, rs.lastIndexOf(Constants.SPILIT));
// 向所有客户端发送在线客户端用户名
sendMsgToAllClient(flag, msg);
}
// 标识是2是群发,3是私聊
} else if (flag == 2 || flag == 3) {
// 读取新的消息
String newMsg = dis.readUTF();
// 获取当前客户端对应的用户名
String sendName = ServerChat.onLineSockets.get(socket);
StringBuilder msgFinal = new StringBuilder();
// 格式化时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss EEE");
if (flag == 2) {
// 发送者用户名与时间拼接
msgFinal.append(sendName).append(" ").append(sdf.format(System.currentTimeMillis())).append("\r\n");
// 发送的消息拼接
msgFinal.append(" ").append(newMsg).append("\r\n");
// 向所有客户端发送消息
sendMsgToAllClient(flag, msgFinal.toString());
} else {
// 私聊
msgFinal.append(sendName).append(" ").append(sdf.format(System.currentTimeMillis())).append("\r\n");
msgFinal.append(" ").append(newMsg).append("\r\n");
String destName = dis.readUTF();
// 向当个用户发送消息
this.sendMsgToOne(destName, msgFinal.toString());
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送消息给指定用户
* @param destName 目标客户端用户名
* @param msg 消息
* @throws IOException
*/
private void sendMsgToOne(String destName, String msg) throws IOException {
Set<Socket> allOneLineSockets = ServerChat.onLineSockets.keySet();
for (Socket sk: allOneLineSockets) {
// 获取目标客户端的socket
if (ServerChat.onLineSockets.get(sk).trim().equals(destName)) {
// 创建输出流
DataOutputStream dos = new DataOutputStream(sk.getOutputStream());
// 发送标识
dos.writeInt(2);
// 发送消息
dos.writeUTF(msg);
dos.flush();
break;
}
}
}
/**
* 发送消息给所有的客户端用户
* @param flag
* @param msg
*/
private void sendMsgToAllClient(int flag, String msg) {
Set<Socket> allOnLineSockets = ServerChat.onLineSockets.keySet();
for (Socket sk: allOnLineSockets) {
try {
// 创建输出流
DataOutputStream dos = new DataOutputStream(sk.getOutputStream());
// 发送标识
dos.writeInt(flag);
// 发送消息
dos.writeUTF(msg);
dos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
代码中使用的常量定义代码
package com.learnclub.utils;
public class Constants {
// 端口号
public static final int PORT = 7778;
// 分隔符
public static final String SPILIT = "0044666ZZZZXXX@@@";
}
登录界面GUI
聊天界面GUI
多用户在线及多人、私聊聊天效果图