您的位置:首页 > 移动开发 > Android开发

基于android开发的聊天室 ChatRoom 1.0 :(二) 消息处理机制

2013-08-09 14:58 381 查看
本应用是基于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 input;
private DataOutputStream output;
private User curUser;

public SocketTask(Socket socket) {
s = socket;
try {
input = new DataInputStream(s.getInputStream());
output = new DataOutputStream(s.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}

/**
* 发送消息
*
* @param msg
* @param datas
*/
public void sendMsg(String msg, byte[] datas) {
try {
if (null != msg) {
output.writeUTF(msg);
}
if (null != datas) {
output.writeInt(datas.length);
output.write(datas, 0, datas.length);
}
output.flush();
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void run() {
try {
while (true) {
String msgCtn = input.readUTF();
if (msgCtn.startsWith(ContentFlag.REGOSTER_FLAG)) { // 处理注册消息
String name = msgCtn.substring(
ContentFlag.REGOSTER_FLAG.length()).trim();
String userId = XmlParser.saveUserInfo(name, input);
System.out.println("生成的用户id" + userId);
this.sendMsg(userId, null);
taskList.remove(this);
break;
} else if (msgCtn.startsWith(ContentFlag.ONLINE_FLAG)) { // 处理登录消息
// 获得用户的唯一标识
String loginId = msgCtn.substring(
ContentFlag.ONLINE_FLAG.length()).trim();
System.out.println("当前登录用户的ID:" + loginId);
curUser = XmlParser.queryUserById(Integer
.parseInt(loginId));
// 将聊天室其它所有在线用户的头像数据缓存到该登录用户客户端
int imgNums = taskList.size() - 1; // 文件的数量
System.out.println("当前在线的人数:" + taskList.size());
this.output.writeInt(imgNums);
if (imgNums > 0) {
for (SocketTask task : taskList) {
if (task != this) {
long userId = task.curUser.getId();
File img = new File("image\\"
+ XmlParser.queryUserById(userId)
.getImg());
FileInputStream flleInput = new FileInputStream(
img);
byte data[] = StreamTool
.readStream(flleInput);
this.sendMsg(String.valueOf(userId), data);
}
}
}
// 加载用户头像资源
File imgFile = new File("image\\" + curUser.getImg());
FileInputStream flleInput = new FileInputStream(imgFile);
byte datas[] = StreamTool.readStream(flleInput);
String send_person = curUser.getName(); // 发送者
String send_ctn = "进入聊天室!"; // 发送内容
String send_date = FormatDate.getCurDate(); // 发送时间
StringBuilder json = new StringBuilder();
json.append("[{");
json.append("id:").append(loginId)
.append(",send_person:\"").append(send_person)
.append("\",send_ctn:\"").append(send_ctn)
.append("\",send_date:\"").append(send_date);
json.append("\"}]");
System.out.println("json:" + json);
// 循环向连接的每个Socket客户端发送登录消息
for (SocketTask tast : taskList) {
System.out.println("循环向客户端发送消息");
tast.sendMsg(
ContentFlag.ONLINE_FLAG
+ this.curUser.getId(), null);
tast.sendMsg(json.toString(), datas);
}
flleInput.close();
} else if (msgCtn.startsWith(ContentFlag.OFFLINE_FLAG)) { // 处理退出消息
taskList.remove(this);
// 获得退出用户的ID
String id = msgCtn.substring(
ContentFlag.OFFLINE_FLAG.length()).trim();
StringBuilder json = new StringBuilder();
json.append("[{");
json.append("id:").append(this.curUser.getId())
.append(",send_person:\"")
.append(this.curUser.getName())
.append("\",send_ctn:\"").append("退出聊天室!")
.append("\",send_date:\"")
.append(FormatDate.getCurDate());
json.append("\"}]");
for (SocketTask tast : taskList) {
if (tast != this) {
tast.sendMsg(ContentFlag.OFFLINE_FLAG + id,
null);
tast.sendMsg(json.toString(), null);
}
}
System.out.println("用户" + curUser.getName() + "退出!,关闭线程"
+ Thread.currentThread().getName());
break;
} else if (msgCtn.startsWith(ContentFlag.RECORD_FLAG)) { // 处理录音消息
String filename = msgCtn
.substring(ContentFlag.RECORD_FLAG.length());
long recordTime = input.readLong();
byte datas[] = StreamTool.readStream(input);
StringBuilder json = new StringBuilder();
json.append("[{");
json.append("id:").append(this.curUser.getId())
.append(",send_person:\"")
.append(this.curUser.getName())
.append("\",send_ctn:\"")
.append(recordTime / 1000 + "\'")
.append("\",send_date:\"")
.append(FormatDate.getCurDate())
.append("\",recordTime:\"").append(recordTime);
json.append("\"}]");
System.out.println("json:" + json);
// 循环向连接的每个Socket客户端发送消息
for (SocketTask tast : taskList) {
tast.sendMsg(ContentFlag.RECORD_FLAG + filename,
null);
tast.sendMsg(json.toString(), datas);
}
} else { // 处理普通消息
StringBuilder json = new StringBuilder();
json.append("[{");
json.append("id:").append(this.curUser.getId())
.append(",send_person:\"")
.append(this.curUser.getName())
.append("\",send_ctn:\"").append(msgCtn)
.append("\",send_date:\"")
.append(FormatDate.getCurDate());
json.append("\"}]");
for (SocketTask tast : taskList) {
tast.sendMsg(json.toString(), null);
}
}
}
} catch (Exception e) {
taskList.remove(this);
System.out.println("关闭线程" + Thread.currentThread().getName());
} finally {
try {
if (null != input)
input.close();
if (null != output)
output.close();
if (null != s)
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}


客户端:一旦建立socket连接后,就开始等待消息的处理,于服务器端类似,首先也要读取数据行,判断消息类型,根据不同类型进行不同的数据处理。

public void receiveMsg(IhandleMessge handle) throws IOException {
try {
while(true){
String msgCtn = input.readUTF();
if(msgCtn.startsWith(ContentFlag.ONLINE_FLAG)){				//处理登录消息
String json = input.readUTF();
Message msg = parseJsonToObject(json);
byte[] datas = StreamTool.readStream(input);
Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length);
msg.setBitmap(bitmap);
handle.handleMsg(msg);
imgMap.put(msg.getId(), bitmap);
}else if(msgCtn.startsWith(ContentFlag.OFFLINE_FLAG)){		//处理退出消息
String json = input.readUTF();
Message msg = parseJsonToObject(json);
msg.setBitmap(imgMap.get(msg.getId()));
handle.handleMsg(msg);
imgMap.remove(msg.getId());
}else if(msgCtn.startsWith(ContentFlag.RECORD_FLAG)){		//处理语音消息
String filename = msgCtn.substring(ContentFlag.RECORD_FLAG.length());
File dir = new File(Environment.getExternalStorageDirectory() + "/recordMsg/");
if(!dir.exists()) dir.mkdirs();
File file = new File(dir, filename);
String json = input.readUTF();
Message msg = parseJsonToObject(json);
msg.setRecord_path(file.getAbsolutePath());
msg.setBitmap(imgMap.get(msg.getId()));
msg.setIfyuyin(true);
handle.handleMsg(msg);
saveRecordFile(file);
}else{														//处理普通消息
Message msg = parseJsonToObject(msgCtn);
msg.setBitmap(imgMap.get(msg.getId()));
handle.handleMsg(msg);
}
}
} catch (Exception e) {
if (!socket.isClosed()) {
throw new IOException("fail connect to the server");
}
}
}


完整资源代码下载地址:http://download.csdn.net/detail/jiangliloveyou/6457969
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐