MyChat--山寨QQ

14 篇文章 0 订阅


最近做了一个聊天软件的项目,界面高仿QQ,使用的协议是xml。

通信项目,感觉协议是整个项目的灵魂,告诉计算机该怎样处理消息,同时,也指导着我们该怎样处理消息。因此,协议要事先定好,尽可能的全面,不要写一步,才订立下一步。大概是经验不足的原因,一开始,我们感觉定的协议都差不多了。然而,写着写着,却发现这个地方还需要协议,所以我们后来感觉写的有点茫然了,这也是个教训。

这次的项目,核心是是用了ServerThread这个线程类,来不断地接收并且处理客户端发送给服务器的消息。客户端那边也是用来类似的线程类来接收并且处理消息。下面,贴上部分代码:

package com.chat.server;

import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Vector;

import javax.swing.JTable;
import javax.swing.RowFilter;

/**
 * 服务器控制类(相当于服务员,每个顾客对应一个服务员)
 * 
 * @author zhangtao
 *
 */
public class ServerThread extends Thread implements IMS {
	private Socket socket;
	private OutputStream ops;
	private InputStream ips;
	private ObjectOutputStream oos;
	private ObjectInputStream ois;
	private DataOutputStream dos;
	private DataInputStream dis;
	public String currentUserId;// 当前的用户Id
	public String currentUserName;// 当前的用户名
	// 定义分组ID的基数
	private static int groupNumber = 200000;
	// 定义好友QQ号码的基数
	private static int idNumber = 1000;
	public static String idString;
	public Vector<Vector<String>> value = ServerFrame.value;
	public JTable jTable = ServerFrame.table;
	Vector<String> row;
	UserDao userDao;

	/**
	 * 构造方法
	 * 
	 * @param socket
	 */
	public ServerThread(Socket socket) {
		super();
		this.socket = socket;
	}

	public void run() {
		super.run();
		initServerThread();
	}

	private void initServerThread() {
		try {
			// 拿到输入输出流
			ops = socket.getOutputStream();
			ips = socket.getInputStream();
			oos = new ObjectOutputStream(ops);
			ois = new ObjectInputStream(ips);

			// 接收消息
			String msg = readMsg();
			// 解析消息类型
			String type = getXMLValue(msg, "type");
			System.out.println("type======="+type);
			if ("login".equals(type)) {
				// 获取用户名和密码
				String userId = getXMLValue(msg, "name");
				String psw = getXMLValue(msg, "pwd");
				System.out.println(userId+"    "+psw);
				String login_msg = "";
				if (checkLogin(userId, psw)) {// 如果登陆成功
					UserInfo currentUser = MyServer.userDB.get(userId);
					// 得到当前用户的id name
					currentUserId = currentUser.getUserId();
					currentUserName = currentUser.getUerName();
					// 更新服务器显示信息
					row = new Vector<String>();// 行向量
					row.add(currentUserName);
					row.add(userId);
					row.add(socket.getRemoteSocketAddress().toString());// IP地址
					row.add("在线");
					value.add(row);
					jTable.updateUI();// 刷新界面

					// 反馈登录成功消息
					login_msg = "<msg><type>loginResp</type><state>LOGIN_TRUE</state></msg>";
					// 反馈登陆应答消息
					sendMsg(login_msg);// 这里有问题?????不需要接收对象吗?

					// 发送好友列表
					// <msg><type>budyList</type><users>用户1,用户2...</users></msg>
					String idList = "";
					for (int i = 0; i < MyServer.serverList.size(); i++) {
						idList = idList
								+ MyServer.serverList.get(i).currentUserId
								+ ",";
					}
					String friend_Msg = "<msg><type>budyList</type><users>"
							+ idList + "</users></msg>";
					// 发送上线消息
					String online_msg = "<msg>g<type>online</type><content>"
							+ currentUserName + "用户上线了</content></msg>";
					// 把消息发给所有在线用户
					for (int i = 0; i < MyServer.serverList.size(); i++) {
						ServerThread st = MyServer.serverList.get(i);
						// 除了自己
						if ((st.currentUserId).equals(currentUserId)) {// 如果是自己

						} else {
							st.sendMsg(friend_Msg);
							st.sendMsg(online_msg);
						}
					}
					// 接收并且处理消息
					// 消息转发
					castMsg();// 没有消息过来,就阻塞在read,读取到消息,就进行转发
				} else {// 登陆失败
					login_msg = "<msg><type>loginResp</type><state>LOGIN_FALSE</state></msg>";
					// 发送登录应答消息
					sendMsg(login_msg);
					MyServer.serverList.remove(this);
					oos.flush();
				}
			} else if ("reg".equals(type)) {
				// 解析用户名和密码
				String name = getXMLValue(msg, "name");
				String pwd = getXMLValue(msg, "pwd");
				//将用户信息保存到本地的map
//				UserDao.saveDB();
				System.out.println("name: " + name + "" + "  psw: " + pwd);
				// 验证用户名密码,并执行注册
				String msg_reg = "";
				if (checkReg(name, pwd)) {
					msg_reg = "<msg><type>regResp</type><userId>"+idString+"</userId><state>REGIST_TRUE</state></msg>";
				} else {
					msg_reg = "<msg><type>regResp</type><state>REGIST_FALSE</state></msg>";
				}
				// 发送注册应答消息
				sendMsg(msg_reg);
				System.out.println("消息发送完成");
				// 删除当前线程
				MyServer.serverList.remove(this);
				oos.flush();
				oos.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 验证注册是否成功,成功的同时把用户添加到map
	 * 
	 * @param name
	 *            用户名
	 * @param pwd
	 *            密码
	 * @return boolean
	 */
	private boolean checkReg(String name, String pwd) {
		// 验证用户是否存在
		// if (MyServer.userDB.containsKey(name)) {
		// return false;
		// }
		// 创建新的user对象,并且加入到map中
		UserInfo userInfo = new UserInfo();
		userInfo.setUerName(name);
		userInfo.setPsw(pwd);
		idString = idNumber + MyServer.userDB.size() + "";// 真正的id要加上map的大小,才能保证正确
		MyServer.userDB.put(idString, userInfo);
		System.out.println("idString"+idString);
		userInfo.setUserId(idString);
		return true;
	}

	/**
	 * 转发消息
	 * 
	 * @return 消息
	 * 
	 */
	private String castMsg() {
		try {
			while (true) {
				String src_id;
				String des_id;
				String msg = readMsg();
				// 解析消息类型
				String type = getXMLValue(msg, "type");
				System.out.println("处理的type:   " + type + "=============");
				if (type.equals("chat")) {// 如果是聊天消息
					// 解析消息
					src_id = getXMLValue(msg, "sender");
					UserInfo user_src = MyServer.userDB.get(src_id);
					String src_name = user_src.getUerName();
					des_id = getXMLValue(msg, "reciver");// 获取目标id
					UserInfo user_des = MyServer.userDB.get(des_id);
					String des_name = user_des.getUerName();
					String msg_content = getXMLValue(msg, "content");// 获取消息内容
					System.out.println("user_id: "+src_id +"des_id: "+des_id+"  消息:"+msg_content);
					// 寻找目标线程
					for (int i = 0; i < MyServer.serverList.size(); i++) {
						ServerThread st = MyServer.serverList.get(i);
						// 如果是目标Id
						if (des_id.equals(st.currentUserId)) {
							// 把消息内容发过去
							String chat_msg = "<msg><type>chat</type><sender>"+src_name+"</sender><reciver>"+des_name+"</reciver><content>"+msg_content+"</content></msg>";
							st.sendMsg(chat_msg);
							System.out.println("chat_msg:"+chat_msg);
						}
					}
				} else if (type.equals("addGroup")) {
					// 解析消息
					src_id = getXMLValue(msg, "sender");
					int groupName = Integer.parseInt(getXMLValue(msg,
							"GroupName"));
					userDao = new UserDao(groupName, idNumber);
					idNumber++;
				} else if (type.equals("addFriends")) {
					// 解析消息
					// 得到收发者的id
					src_id = getXMLValue(msg, "srcId");
					des_id = getXMLValue(msg, "reciver");
					System.out.println("srcId:" + src_id + "        "
							+ "receiver:" + des_id);
					// 得到验证的内容
					String checkString = getXMLValue(msg, "content");
					// 进行转发
					// 先找到对应的线程
					for (int i = 0; i < MyServer.serverList.size(); i++) {
						// 得到对应下标的线程对象
						ServerThread st = MyServer.serverList.get(i);
						// 如果是目标id
						if ((st.currentUserId).equals(des_id)) {
							// 得到源用户的用户名
							UserInfo user = MyServer.userDB.get(src_id);
							String src_name = user.getUerName();
							String add_msg = src_name + "想添加你为好友:"
									+ checkString;
							String ims_String = "<msg><type>addFriends_checkMsg</type><content>"
									+ add_msg + "</content></msg>";
							System.out.println(add_msg);
							sendMsg(ims_String);
						}
//						else  {
//							
//							sendMsg(msg);
//						}
					}
				} else if (type.equals("addResp")) {// 如果是添加反馈消息
					System.out.println("进入addResp!!!!");
					// 解析消息
					// 得到发送者的id
					// src_id = currentUserId;
					src_id = getXMLValue(msg, "srcId");
					// System.out.println(msg);
					// 得到接受者的id
					des_id = getXMLValue(msg, "reciver");
					System.out.println("srcId: " + src_id + "  des_id:"
							+ des_id);
					System.out.println("srcId:" + src_id + "  addResp  "
							+ "receiver:" + des_id);
					// 得到添加状态
					String state = getXMLValue(msg, "state");
					// *******后面可以改
					// 暂时默认如果state为true,服务器帮助添加好友
					// 1.先要得到双方的grouplist ----就要先得到user对象
					// 2.在list中添加user
					if (state.equals("ADD_TRUE")) {
						// 添加好友
						UserInfo user_src = MyServer.userDB.get(src_id);
						UserInfo user_des = MyServer.userDB.get(des_id);
						//把好友添加到group中
						user_src.group.groupAddUser(user_des);
						user_des.group.groupAddUser(user_src);
						// 给客户端发送用户对象,交互发送对象
						// 查找对应线程
						for (int i = 0; i < MyServer.serverList.size(); i++) {
							ServerThread st = MyServer.serverList.get(i);
							if ((st.currentUserId).equals(des_id)) {
								// 如果是目标线程就发送源id
								String string = "<msg><type>friend</type><content>"
										+ src_id + "</content></msg>";
								st.sendMsg(string);// st是目标线程。没有st,就是当前线程对象

							} else if ((st.currentUserId).equals(src_id)) {
								// 如果是源线程,就发送目标对象
								String string = "<msg><type>friend</type><content>"
										+ des_id + "</content></msg>";
								st.sendMsg(string);
							}
						}
					}
					// else if (state.equals("ADD_False")) {
					// //添加失败
					//
					// }
				}
			}
		} catch (Exception e) {// 有异常时,进入catch
			// 客户下线了,移出当前线程
			MyServer.serverList.remove(this);
			// 发送下线消息
			String offLine_msg = "<msg><type>offline</type><content>"
					+ currentUserName + "用户下线了</content></msg>";
			// 把消息发给所有在线用户
			for (int i = 0; i < MyServer.serverList.size(); i++) {
				ServerThread st = MyServer.serverList.get(i);
				st.sendMsg(offLine_msg);
			}
			// 刷新界面
			row.remove(3);
			row.add("离线");
			jTable.updateUI();
		}
		return null;
	}

	/**
	 * 发送消息
	 * 
	 * @param msg
	 *            消息内容
	 */
	private void sendMsg(String msg) {
		try {
			// ops.write(msg.getBytes());
			// ops.flush();
			// dos.writeUTF(msg);
			// dos.flush();
			oos.writeObject(msg);
			oos.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * 验证用户名和密码的方法
	 * 
	 * @param name
	 *            用户名
	 * @param psw
	 *            密码
	 * @return true或false
	 */
	private boolean checkLogin(String id, String psw) {
		if (MyServer.userDB.containsKey(id)) {// 如果包含用户名
			// 验证密码
			UserInfo userInfo = MyServer.userDB.get(id);// 得到对应的用户
			String userPsw = userInfo.getPsw();
			if (psw.equals(userPsw)) {// 密码正确
				return true;
			}
		}
		return false;
	}

	/**
	 * 读取消息的方法
	 * 
	 * @return 消息(一整条)
	 * @throws IOException
	 * @throws ClassNotFoundException 
	 */
	private String readMsg() throws ClassNotFoundException, IOException {// 抛给调用它的那个
		String str = "";
		// 先读取一个字节
//		int value = ips.read();
//		StringBuffer sb = new StringBuffer();
//		while (value != 13) {// 当不是回车时,循环读取
//			sb.append((char) value);
//			// 判断是否遇到</msg>,如果遇到,则视为一条消息返回
//			str = sb.toString().trim();
//			str = new String(str.getBytes("ISO-8859-1"), "GBK").trim();
//			if (str.contains("</msg>")) {
//				return str;
//			}
//			value = ips.read();
//		}
//		// 先读取一个字节
//		return null;
		
		while(true){
			String string = (String) ois.readObject();
			if (string.contains("</msg>")) {
				return string;
			}
		}
	}

	/**
	 * 解析消息的类型
	 * 
	 * @param msg
	 *            消息
	 * @param xmlFlag
	 *            目标的标签对
	 * @return 消息类型
	 */
	private String getXMLValue(String msg, String xmlFlag) {
		// 找到开始位置
		int start = msg.indexOf("<" + xmlFlag + ">") + xmlFlag.length() + 2;
		int end = msg.indexOf("</" + xmlFlag + ">");
		String result = msg.substring(start, end);
		return result;
	}

	// public ServerThread(){}
	// public static void main(String[] args) {
	// //<msg><type>login</type><name>用户名</name><pwd>密码</pwd></msg>
	// String msg =
	// "<msg><type>login</type><name>用户名</name><pwd>密码</pwd></msg>";
	// ServerThread st = new ServerThread();
	// System.out.println(st.getXMLValue(msg, "pwd"));
	// }
}
这次项目,我最大的问题也是迄今仍未实现的,是客户端那边。不知道如何将消息在聊天窗口展示出来,后台已经转发成功了。虽然我知道,要拿到聊天窗口的对象,可是不知道怎么回事,就是没办法拿到,哎!

作为通信阶段的最后一个项目,我做的不够完整,有两个主要原因:

1.时间的原因,我和小伙伴由于时间不是很多,总共才花了三天时间,做的可以说是相当的匆忙;

2.也是最主要的原因,一开始没有足够的重视,以为相对来说比较的简单,就没有太放在心上。

通过这次项目,我觉得无论难易,都要有一颗平常心,不畏难,不轻易,打好基础才能走的稳!!!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值