本应用是基于socket通信机制,在客户端和服务器端进行消息交互时都会在消息中添加一条标识行,用来标识消息的类型(注册、登录、退出、文字消息、语音消息),根据不同的消息类型分别做不同的数据处理,因为是一对多聊天,因此在服务器端缓存了所有在线用户信息,包括每个用户的头像数据,这里用了一个普通的xml文件来充当数据库保存用户的注册信息,下面列举了客户端和服务器端对消息处理的大致流程。
服务器端:服务器端开启后,一旦有个socket请求发过来,就会开启一个线程处理该请求。首先读取标识行,判断消息的类型,然后根据消息的类型做不同的处理。同时在服务器端用集合的方式缓存了所有开启的线程,因此对于需要一对多发送的消息会循环遍历该集合,从而保证所有在线客户端都能接收到消息。
public class ChatServer {
private ExecutorService executorService;// 线程池
private int port;// 监听端口
private boolean quit = false;// 退出
private ServerSocket server;
private List<SocketTask> taskList = new ArrayList<SocketTask>();// 保存所有启动的socket集合
public ChatServer(int port) {
this.port = port;
// 创建线程池,池中具有(cpu个数*50)条线程
executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
.availableProcessors() * 50);
}
/**
* 服务器终止,关闭所有线程
*/
public void quit() {
this.quit = true;
try {
for (SocketTask tast : taskList) {
tast.input.close();
}
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 服务器启动
*
* @throws Exception
*/
public void start() throws Exception {
server = new ServerSocket(port);
new Thread(new Runnable() {
public void run() {
while (!quit) {
try {
System.out.println("等待用户的socket请求");
Socket socket = server.accept();
// 为支持多用户并发访问,采用线程池管理每一个用户的连接请求
SocketTask newTask = new SocketTask(socket);
taskList.add(newTask);
executorService.execute(newTask);
System.out.println("启动一个线程开始处理socket请求");
} catch (Exception e) {
System.out.println("服务器终止!关闭所有线程");
}
}
}
}).start();
}
/**
* 内部线程类,负责与每个客户端的数据通信
*
* @author Administrator
*/
private final class SocketTask implements Runnable {
private Socket s;
private DataInputStream in