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

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

把做的思路及代码发上来

一是理清自己的思路

二是大家有什么好的建议

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

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

qq注册用户的信息表


[img]http://dl.iteye.com/upload/attachment/480810/2adf1d91-4403-324f-9036-187853f71e75.jpg[/img]




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


[img]http://dl.iteye.com/upload/attachment/480812/6500c1ab-1c25-3a3e-9ace-b16c05a5e64a.jpg[/img]


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


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

[img]http://dl.iteye.com/upload/attachment/480818/a84cb8dc-fef3-36b9-8d5e-832c67aac8cd.jpg[/img]

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

这个发送信息,服务器接受信息处理,再加上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不需要制定端口号

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

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


服务端要面对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层代码有些问题,明天再发出来吧。

希望大家给给些意见
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值