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

本应用是基于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

你可能感兴趣的:(android,socket,语音,聊天室,表情)