锁定老帖子 主题:帮哥们做的聊天软件(源码+思路)
精华帖 (0) :: 良好帖 (0) :: 新手帖 (2) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-05-13
最后修改:2011-05-13
把做的思路及代码发上来 一是理清自己的思路 二是大家有什么好的建议 三是等待牛人给好的思路修正一些问题 为了存储注册用户信息,好友之间的关系,需要建立两张表 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层代码有些问题,明天再发出来吧。 希望大家给给些意见 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-05-13
.......貌似在山寨 韩顺平在QQ项目。。。。。。。
|
|
返回顶楼 | |
发表时间:2011-05-13
这登录界面看得泪流满面,你们都是自己搞的ui?求入门
|
|
返回顶楼 | |
发表时间:2011-05-13
mzpyljx 写道 .......貌似在山寨 韩顺平在QQ项目。。。。。。。
哥们眼力很好,果然高手 就是加了个数据库 写了个DAO,然后改了些东西 相当于二次开发 |
|
返回顶楼 | |
发表时间:2011-05-13
ak121077313 写道 这登录界面看得泪流满面,你们都是自己搞的ui?求入门
UI肯定水平不行,直接用的人家的代码 |
|
返回顶楼 | |
发表时间:2011-05-14
这东西,也就ui技术比较难点,其他的都浮云。
|
|
返回顶楼 | |
发表时间:2011-05-14
中国人民的习惯开场白“时间比较紧,技术能力有限,现在代码中肯定存在很多问题”
中国人民的习惯性回复 “UI肯定水平不行,直接用的人家的代码 ” |
|
返回顶楼 | |
发表时间:2011-05-14
分离的北极熊 写道 中国人民的习惯开场白“时间比较紧,技术能力有限,现在代码中肯定存在很多问题”
中国人民的习惯性回复 “UI肯定水平不行,直接用的人家的代码 ” 真相哥,没办法么,小时候新闻联播看多了,上学时候思想政治课给洗脑了,还在努力反洗脑过程中。。。。 |
|
返回顶楼 | |
发表时间:2011-05-14
beykery 写道 这东西,也就ui技术比较难点,其他的都浮云。
看来哥们是搞云方面的,开口就浮云,说之前先说说你好的想法,再说浮云 一个QQ要能做好事多么不容易的事情,我这样觉得的,里面要涉及到很多东西,个人理解:相当于搞web开发,自己要写服务器,没有tomcat之类的给你,前面表现层更没有浏览器给你用,面对多个socke请求你怎么处理,你还要考虑好的效率,多对多聊天你怎么弄? 这不是什么浮云下就OK的事情,当然你的水平可能比较高 |
|
返回顶楼 | |
发表时间:2011-05-14
悲剧了 写道 分离的北极熊 写道 中国人民的习惯开场白“时间比较紧,技术能力有限,现在代码中肯定存在很多问题”
中国人民的习惯性回复 “UI肯定水平不行,直接用的人家的代码 ” 真相哥,没办法么,小时候新闻联播看多了,上学时候思想政治课给洗脑了,还在努力反洗脑过程中。。。。 第一句是谦虚,第二句是人之常理,你弱他就强,人嘛,都要装一下滴 |
|
返回顶楼 | |