`
悲剧了
  • 浏览: 145235 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

帮哥们做的聊天软件(源码+思路)

阅读更多
时间比较紧,技术能力有限,现在代码中肯定存在很多问题

把做的思路及代码发上来

一是理清自己的思路

二是大家有什么好的建议

三是等待牛人给好的思路修正一些问题

为了存储注册用户信息,好友之间的关系,需要建立两张表

qq注册用户的信息表








qq_friend表 这个表是关联qqinfo的id,两个字段都是外键







表建立好了,那么根据一般方式
写个数据库操作的工具类吧

public class ConnectionUtil {
	private final static String  url="jdbc:mysql://localhost:3306/qq?useUnicode=true&characterEncoding=UTF-8";
	private final static String  username="root";
	private final static String  password="root";
	private  static Connection   instance=null;
	
	private ConnectionUtil(){
		if(instance==null){
			try {
				Class.forName("org.gjt.mm.mysql.Driver");
				instance=DriverManager.getConnection(url, username, password);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			}
			
		}
	}
	
	public static Connection getConnection(){
		if(instance==null){
			new ConnectionUtil();
		}
		return instance;
		
	}
	
	
	public static void close(Connection con,PreparedStatement ps,ResultSet rs){
		if(con!=null){
			try {
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally{
				con=null;
			}
		}
		if(ps!=null){
			try {
				ps.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally{
				ps=null;
			}
		}
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally{
				rs=null;
			}
		}
		
	}
	
	public static void close(Connection con,PreparedStatement ps){
		if(con!=null){
			try {
				con.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally{
				con=null;
			}
		}
		if(ps!=null){
			try {
				ps.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}finally{
				ps=null;
			}
		}
		
		
	}
	
	public static void close(Connection con){
		if(con!=null){
			try {
				con.close();
			} catch (SQLException e) {
				
				e.printStackTrace();
			}finally{
				con=null;
			}
		}
	}

}



工具类写完后,实体相应写出,省略getter setter方法
QQinfo表的实体
public class QQInfo {
	
	private Integer id;
	private String name;
	private String password;
	private String status;

}


public class QQFriend {

	private Integer  myId;
	private List<Integer> friendId;

}


下来就是DAO了
public class QQFriendDAO {
	/**
	 * 添加好友
	 */

	public static void addFriend(int myId,int friendId ){
		
		Connection con=ConnectionUtil.getConnection();
		PreparedStatement ps=null;
		
		String sql="insert into id_friendid(myId,friendId) value(?,?)";
		try {
			ps=con.prepareStatement(sql);
			ps.setInt(1, myId);
			ps.setInt(2, friendId);
			ps.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			ConnectionUtil.close(con,ps);
		}
		
	}
	/**
	 * 移除好友
	 */

	public static void removeFriend(int myId,int friendId){

		Connection con=ConnectionUtil.getConnection();
		PreparedStatement ps=null;
		String sql="delete from id_friendid where myId=? and friendId=? ";
		try {
			ps=con.prepareStatement(sql);
			ps.setInt(1, myId);
			ps.setInt(2, friendId);
			ps.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			ConnectionUtil.close(con,ps);
		}
		
	}
	/**
	 * 查找好友
	 */

	public static List<Integer> getFriends(int myId){
		List<Integer> list=new ArrayList<Integer>();
		Connection con=ConnectionUtil.getConnection();
		PreparedStatement ps=null;
		ResultSet rs=null;
		String sql="select friendId from id_friendid where myId=? ";
		try {
			ps=con.prepareStatement(sql);
			ps.setInt(1, myId);
			rs=ps.executeQuery();
			while(rs.next()){
				list.add(rs.getInt("friendId"));
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			ConnectionUtil.close(con,ps);
		}
		
		return list;
	}
}




public class QQInfoDAO {
	/**
	 * 根据id查找相应的qq信息
	 * 
	 */
	public QQInfo getQQInfo(int id){
		QQInfo qqInfo=null;
		Connection con=ConnectionUtil.getConnection();
		String sql="select * from qqinfo where id=?";
		PreparedStatement ps=null;
		ResultSet rs=null;
		try {
			ps=con.prepareStatement(sql);
			ps.setInt(1, id);
			rs=ps.executeQuery();
			if(rs.next()){
				qqInfo=new QQInfo();
				qqInfo.setId(id);
				qqInfo.setName(rs.getString("name"));
				qqInfo.setPassword(rs.getString("password"));
				qqInfo.setStatus(rs.getString("status"));
			}
				
			
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			ConnectionUtil.close(con,ps,rs);
		}
		
		
		return qqInfo;
	}
	
	/**
	 * 更新个人信息
	 */
	
	public void update(QQInfo qqInfo){
		Connection con=ConnectionUtil.getConnection();
		PreparedStatement ps=null;
		
		String sql="update qqinfo set name=?,password=?,status=? where id=?";
		try {
			ps=con.prepareStatement(sql);
			ps.setString(1, qqInfo.getName());
			ps.setString(2, qqInfo.getPassword());
			ps.setString(3, qqInfo.getStatus());
			ps.setInt(4, qqInfo.getId());
			ps.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			ConnectionUtil.close(con,ps);
		}
		
	}
		
		
	
	/**
	 * 注册新号码
	 */
	
	public void save(QQInfo qqInfo){
		Connection con=ConnectionUtil.getConnection();
		PreparedStatement ps=null;
		
		String sql="insert into qqinfo(name,password,status) value(?,?,?)";
		try {
			ps=con.prepareStatement(sql);
			ps.setString(1, qqInfo.getName());
			ps.setString(2, qqInfo.getPassword());
			ps.setString(3, qqInfo.getStatus());
			ps.executeUpdate();
			
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			ConnectionUtil.close(con,ps);
		}
		
	}
	
}





然后写Test测试DAO是否有问题,这里就省略了,测试都通过


前面的结束后,复杂的东西就开始了,一般我有点晕就开始按流程来理思路

那么用户现在已经通过web注册了一个QQ号(写个jsp页面,通过上面的DAO,写个servlet,让用户注册)

他开始登陆自己的QQ



那么这个登陆的时候填写的信息,发送到服务器,然后给他验证

这个发送信息,服务器接受信息处理,再加上socket接受

服务器处理完信息后就关闭吗?肯定要连着,如果这会再有其它QQ连上来,就需要多线程

发送到服务器的信息有很多种类型,有的是用户登陆信息,有的是发送消息信息, 有的是线好友的信息

这个信息跑过来,跑过去的,需要给定义个类

public interface MessageType {

	String message_succeed="1";//表明是登陆成功
	String message_login_fail="2";//表明登录失败
	String message_comm_mes="3";//普通信息包
	String message_get_onLineFriend="4";//要求在线好友的包
	String message_ret_onLineFriend="5";//返回在线好友的包
}




public class Message implements java.io.Serializable{
	
	private static final long serialVersionUID = 1L;
	/**
	 * 信息是哪种类型
	 */
	private String mesType;
	/**
	 * 发送者名字
	 */
	private String sender;
	/**
	 * 接受者名字
	 */
	private String getter;
	/**
	 * 信息内容
	 */
	private String con;
	/**
	 * 信息发送时间
	 */
	private String sendTime;
}


项目为了清晰,分为两个ServerQQ ,ClientQQ,以上的两个类两个项目里面都会用到

下面就是处理连接了,连接上来读取到客户端的发来的信息,然后处理完成后再发送过去,又让我想到了web的MVC
只不过这个发送的方式不是通过jsp页面发送表现的,需要自己画界面,从节目里面获取值,然后进行封装
web是写好jsp,只等着在web容器里面处理前台做好标记的数据,而且还有struts之类的东东,实在是爽翻了。这CS开发都要自己写,还要处理多线程,客户端没有浏览器,要自己写个软件,相当于浏览器,后台也要自己来接受,没有web容器前期就给你很多处理。

public class QqClientConServer {

	
	public Socket s;
	
	//发送第一次请求
	public boolean sendLoginInfoToServer(Object o)
	{
		boolean b=false;
		try {
			s=new Socket("127.0.0.1",9999);
			ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());
			oos.writeObject(o);
			
			ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
			
			Message ms=(Message)ois.readObject();
			//判断服务器发送过来的消息
			if(ms.getMesType().equals("1"))
			{
				//就创建一个该qq号和服务器端保持通讯连接得线程
				ClientConServerThread ccst=new ClientConServerThread(s);
				//启动该通讯线程
				ccst.start();
				ManageClientConServerThread.addClientConServerThread
				(((QQInfo)o).getId(), ccst);
				b=true;
			}else{
				s.close();
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		}finally{
			
		}
		return b;
	}
	
	
}




里面涉及到一个ManageClientConServerThread
public class ManageClientConServerThread {

	private static HashMap hm=new HashMap<String, ClientConServerThread>();
	
	//把创建好的ClientConServerThread放入到hm
	public static void addClientConServerThread(int qqId,ClientConServerThread ccst)
	{
		hm.put(qqId, ccst);
	}
	
	//可以通过qqId取得该线程 
	public static ClientConServerThread getClientConServerThread(String qqId)
	{
		return (ClientConServerThread)hm.get(qqId);
	}
}



当你连接上了服务器,然后你跟服务器保持通讯,那么还能一机登录多个QQ吗?

这个manager就是为了实现这个

以前看java的时候,在想为什么客户端socket不需要制定端口号

百科->>
引用

如何开发一个Server-Client模型的程序
  开发原理:
  服务器,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。
  客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个1024以上的端口。


客户端已经好了,就等服务器了


服务端要面对N个客户端连接上来,压力啊
一个server等待连接客户端得连接,N个客户端线程连接请求上来后你怎么处理
先考虑一个连接上来吧,上来后要这个实例要面对N中请求处理,多线程来执行里面的处理可能会更好,毕竟不能让socket闲着,这个IO还是很昂贵的

为了面对N个客户端的连接,采用manger,跟客户端一样

public class MyQqServer {

	public MyQqServer() {

		try {
			// 在9999监听
			System.out.println("服务器在9999监听");
			ServerSocket ss = new ServerSocket(9999);
			// 阻塞,等待连接
			while (true) {
				Socket s = ss.accept();
				// 接收客户端发来的信息.
				ObjectInputStream ois = new ObjectInputStream(s
						.getInputStream());
				QQInfo qqInfoClient = (QQInfo) ois.readObject();
				Message m = new Message();
				ObjectOutputStream oos = new ObjectOutputStream(s
						.getOutputStream());
				QQInfoDAO qqInfoDAO = new QQInfoDAO();
				QQInfo qqInfoServer = qqInfoDAO.getQQInfo(qqInfoClient.getId());
				if (qqInfoClient.getPassword().equals(qqInfoServer.getPassword())) {
					// 返回一个成功登陆的信息报
					m.setMesType("1");
					oos.writeObject(m);
					// 这里就单开一个线程,让该线程与该客户端保持通讯.
					SerConClientThread scct = new SerConClientThread(s);
					ManageClientThread.addClientThread(qqInfoServer.getName(), scct);
					// 启动与该客户端通信的线程.
					scct.start();
					// 并通知其它在线用户.
					scct.notifyOther(qqInfoServer.getId());
				} else {
					m.setMesType("2");
					oos.writeObject(m);
					// 关闭Socket
					s.close();
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			// TODO: handle exception
		} finally {

		}

	}

}




public class SerConClientThread  extends Thread{
	 Socket s;
	public SerConClientThread(Socket s)
	{
		//把服务器和该客户端的连接赋给s
		this.s=s;
	}
	//让该线程去通知其它用户
	public void notifyOther(int iam)
	{
		//得到所有在线的人的线程
		HashMap hm=ManageClientThread.hm;
		Iterator it=hm.keySet().iterator();
		
		while(it.hasNext())
		{
			Message m=new Message();
			m.setCon(""+iam);
			m.setMesType(MessageType.message_ret_onLineFriend);
			//取出在线人的id
			String onLineUserId=it.next().toString();
			try {
				ObjectOutputStream oos=new ObjectOutputStream(ManageClientThread.getClientThread(onLineUserId).s.getOutputStream());
				m.setGetter(onLineUserId);
				oos.writeObject(m);
			} catch (Exception e) {
				e.printStackTrace();
				// TODO: handle exception
			}
			
		}
	}
	public void run()
	{
		while(true)
		{
			//这里该线程就可以接收客户端的信息.
			try {
				ObjectInputStream ois=new ObjectInputStream(s.getInputStream());
				Message m=(Message)ois.readObject();
				//对从客户端取得的消息进行类型判断,然后做相应的处理
				if(m.getMesType().equals(MessageType.message_comm_mes))
				{
					//一会完成转发.
					//取得接收人的通信线程
					SerConClientThread sc=ManageClientThread.getClientThread(m.getGetter());
					ObjectOutputStream oos=new ObjectOutputStream(sc.s.getOutputStream());
					oos.writeObject(m);
				}else if(m.getMesType().equals(MessageType.message_get_onLineFriend))
				{
					System.out.println(m.getSender()+" 要他的好友");
					//把在服务器的好友给该客户端返回.
					String res=ManageClientThread.getAllOnLineUserid();
					Message m2=new Message();
					m2.setMesType(MessageType.message_ret_onLineFriend);
					m2.setCon(res);
					m2.setGetter(m.getSender());
					ObjectOutputStream oos=new ObjectOutputStream(s.getOutputStream());
					oos.writeObject(m2);
				}
				
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}





public class ManageClientThread {

	public static HashMap hm=new HashMap<String, SerConClientThread>();
	
	//向hm中添加一个客户端通讯线程
	public static  void addClientThread(String uid,SerConClientThread ct)
	{
		hm.put(uid, ct);
	}
	
	public static SerConClientThread getClientThread(String uid)
	{
		return (SerConClientThread)hm.get(uid);
	}
	
	//返回当前在线的人的情况
	public static String getAllOnLineUserid()
	{
		//使用迭代器完成
		Iterator it=hm.keySet().iterator();
		String res="";
		while(it.hasNext())
		{
			res+=it.next().toString()+" ";
		}
		return res;
	}
}





---------------截止到现在,基本解决了登录问题,登录后管理连接问题,那登录后要好友显示什么的

根据号码搜索好友,加好友--->>这个功能暂时不实现

客户A--发送查询信息
服务器--接受到消息类型,查询相关信息出来,发送到客户端,客户端A看到信息决定是否加

如果加--发送次信息到服务器
服务器--悲剧,又要判断加的好友在不在线,不在线还得把这个存起来,然后,他上线再通知。

先放着吧,以后再实现
------------------------------------

view层代码有些问题,明天再发出来吧。

希望大家给给些意见










  • 大小: 22.2 KB
  • 大小: 17.9 KB
  • 大小: 26.2 KB
分享到:
评论
24 楼 ydwcn 2011-06-02  
分离的北极熊 写道
中国人民的习惯开场白“时间比较紧,技术能力有限,现在代码中肯定存在很多问题”

中国人民的习惯性回复 “UI肯定水平不行,直接用的人家的代码 ”


奶牛满面
23 楼 wahyd4 2011-05-17  
这个界面很闪,代码解释还比较清楚吧。正在慢慢看
22 楼 xiaorui1119 2011-05-16  
分离的北极熊 写道
中国人民的习惯开场白“时间比较紧,技术能力有限,现在代码中肯定存在很多问题”

中国人民的习惯性回复 “UI肯定水平不行,直接用的人家的代码 ”


中国人的习惯,没法改。不过lz已经挺不错了。
21 楼 Caedmon 2011-05-16  
要开N个端口,TCP负责指令类的通信,UDP负责数据类的通信。
20 楼 youjianbo_han_87 2011-05-16  
几个人用估计没问题。。。。
19 楼 ahopedog2 2011-05-16  
应该可以使用UDP,压力主要在服务器上,服务器上开启一个UDP端口,负责接收客户发送上来的数据,和一个UDP端口,负责向客户端发送数据。

接收数据的UDP端口只负责接收数据,接收到的数据立即放到一个队列中,队列后面有一个任务处理线程池,这个线程池对队列里的指令进行处理。由于有了队列,接收线程的UDP端口就只负责接收数据,很快会完成操作。

线程池处理完客户端的任务后,在将结果通过下行UDP发送出去。


这样上行UDP,下行UDP只完成数据接收、发送,这需要依赖JAVA的网络处理能力。线程池是处理业务的关键。


如果想继续扩大,上行、下行UDP也可以存在多个端口,形成一个次,有某种并行机制来处理。


用TCP恐怕服务器的资源会不够用。
18 楼 zhyuan 2011-05-15  
核心的东西都没实现,在线消息、离线消息的存储和显示是怎么实现的,以及不同用户状态变化的监听,用户数据量大以及海量在线用户时的并发问题都没有考虑
17 楼 joliny 2011-05-15  
ui使用什么做的呢?swing吗??
16 楼 chinaagan 2011-05-15  
没有建模吗?
15 楼 mtnt2008 2011-05-14  
szcs10138456 写道
“扮家家”,鉴定完毕~@


+1
14 楼 skzr.org 2011-05-14  
这个应该只是试验性质的。
1. 只能做玩具
我设想的:
2. 后端使用spring运行在web容器中
3. 通讯可以直接使用spring http invoker 或者通过json
3. 前端做一个内部消息中心,面向消息处理数据和与服务器交互。
4. 安全性直接使用https
13 楼 szcs10138456 2011-05-14  
“扮家家”,鉴定完毕~@
12 楼 悲剧了 2011-05-14  
qlqllu 写道
啥目的啊?



发出来,让大家说些思路,有什么问题什么的

结果都关注UI了
11 楼 qlqllu 2011-05-14  
啥目的啊?
10 楼 悲剧了 2011-05-14  
心悦君兮君不知 写道
悲剧了 写道
分离的北极熊 写道
中国人民的习惯开场白“时间比较紧,技术能力有限,现在代码中肯定存在很多问题”

中国人民的习惯性回复 “UI肯定水平不行,直接用的人家的代码 ”


真相哥,没办法么,小时候新闻联播看多了,上学时候思想政治课给洗脑了,还在努力反洗脑过程中。。。。



第一句是谦虚,第二句是人之常理,你弱他就强,人嘛,都要装一下滴



哥们一眼就看透了
9 楼 心悦君兮君不知 2011-05-14  
悲剧了 写道
分离的北极熊 写道
中国人民的习惯开场白“时间比较紧,技术能力有限,现在代码中肯定存在很多问题”

中国人民的习惯性回复 “UI肯定水平不行,直接用的人家的代码 ”


真相哥,没办法么,小时候新闻联播看多了,上学时候思想政治课给洗脑了,还在努力反洗脑过程中。。。。



第一句是谦虚,第二句是人之常理,你弱他就强,人嘛,都要装一下滴
8 楼 悲剧了 2011-05-14  
beykery 写道
这东西,也就ui技术比较难点,其他的都浮云。



看来哥们是搞云方面的,开口就浮云,说之前先说说你好的想法,再说浮云
一个QQ要能做好事多么不容易的事情,我这样觉得的,里面要涉及到很多东西,个人理解:相当于搞web开发,自己要写服务器,没有tomcat之类的给你,前面表现层更没有浏览器给你用,面对多个socke请求你怎么处理,你还要考虑好的效率,多对多聊天你怎么弄?
这不是什么浮云下就OK的事情,当然你的水平可能比较高
7 楼 悲剧了 2011-05-14  
分离的北极熊 写道
中国人民的习惯开场白“时间比较紧,技术能力有限,现在代码中肯定存在很多问题”

中国人民的习惯性回复 “UI肯定水平不行,直接用的人家的代码 ”


真相哥,没办法么,小时候新闻联播看多了,上学时候思想政治课给洗脑了,还在努力反洗脑过程中。。。。

6 楼 分离的北极熊 2011-05-14  
中国人民的习惯开场白“时间比较紧,技术能力有限,现在代码中肯定存在很多问题”

中国人民的习惯性回复 “UI肯定水平不行,直接用的人家的代码 ”

5 楼 beykery 2011-05-14  
这东西,也就ui技术比较难点,其他的都浮云。

相关推荐

Global site tag (gtag.js) - Google Analytics