1、數據庫用的mysql,一共有3張表,一張用戶表user、一張朋友列表friend和一張消息表message。
1 User table 用戶表 uid 主鍵自動生成 userName 昵稱 userPwd 密碼 userSex 性別 userPho 用戶頭像,有默認頭像 2 Friend table 好友列表 fid 主鍵自動生成 uid --> fk 用戶id,外鍵 fuid --> 朋友的id fName 好友名稱 3 Messages table 消息表 mid 消息id,主鍵自動生成 fromid --> fk from id 發送者id tofid --> fk to id 接收者id msg 消息內容 mtime 發送時間
2、服務器端架構

3、model包解析
public class User{//1 用戶類 private String userName;//用戶名 private String userPwd;//用戶密碼 private String userSex;//用戶性別 private String userPho;//用戶照片 ... public class Friend {//2 朋友類 private int uid;//用戶id private int fuid;//friend id private String fName;//用戶的朋友名字 ... public class FriendList extends ArrayList<Friend>{//3 朋友列表類 /** * */ private static final long serialVersionUID = 1L; private FriendList friendlist; public FriendList() { friendlist = null; } }
public class Messages{//4 消息類 private int fromId; private int toId; private String msg; private String mtime; public Messages() { }
4、db包解析
主要作用是加載類,獲取mysql數據庫的連接
public class DBConnection {
public static final String DBURL = "jdbc:mysql://localhost:3306/qq";
public static final String DBUSER = "root";
public static final String DBPASS = "root";
public static final String DBDRIVER = "com.mysql.jdbc.Driver";
static {
try {
Class.forName(DBDRIVER);//加載類
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
//返回Connection
public Connection getConnect() throws SQLException{
return DriverManager.getConnection(DBURL,DBUSER,DBPASS);
}
//關閉資源
public void close(Connection con, Statement sta, ResultSet rs) {
try {
rs.close();
if(con != null) {
con.close();
}
if(sta != null) {
sta.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
5、util包解析
第一個類Packager
public class Packager {
//用於登錄數據包的分析
public String loginPackager(String operate,String friends,String result) {
StringBuffer mes = new StringBuffer("");
mes.append("operate:" + operate + "\n");
mes.append("content:" + friends);
mes.append("result:" + result + "\n");
return mes.toString();
}
//用於發送數據包的分析
public String sendPackager(String operate,String msg,String result) {
StringBuffer mes = new StringBuffer("");
mes.append("operate:" + operate + "\n");
mes.append("content:" + "\n");
mes.append("result:" + result + "\n");
return mes.toString();
}
}
第二個類Parser
public class Parser {
//解析類
//用於解析獲取的請求類型是什么,比如登錄、發送消息
public String getOperate(String request) {
String[] message = request.split("\n");
String operate = message[0].substring(8,message[0].length());
return operate;
}
//獲取內容
public String getContent(String request) {
String[] message = request.split("\n");
String content = message[1].substring(8,message[1].length());
return content;
}
//獲取發送的消息
public Messages parseMessages(String content) {
String[] mes = content.split("#");
int toId = Integer.parseInt(mes[0]);
String message = mes[1];
Messages msg = new Messages();
msg.setToId(toId);
msg.setMsg(message);
return msg;
}
//將字符串解析為用戶類,返回用戶類
public User parseUser(String content) {
//分隔符為#
String[] mes = content.split("#");
//第一個是用戶名,第二個是密碼
String userName = mes[0];
String userPwd = mes[1];
User user = new User();
user.setUserName(userName);
user.setUserPwd(userPwd);
return user;
}
}
6、controll包解析
controller類解析
//控制器
public class Controller {
//無參構造函數
public Controller() {
}
//相應請求
public String doResponse(String request,String ip) {
Parser parser = new Parser();
String operate = parser.getOperate(request);//用於解析用戶的請求操作是什么
String content = parser.getContent(request);//獲取請求的內容
String response = null;//相應字符串
String friends = null;//朋友
if("login".equals(operate)) {//判斷是否是登錄請求
int uid = login(content);//返回該用戶的id,如果沒有該用戶返回0
String result = null;
if(uid != 0) {
//將該用戶的主鍵和ip地址加入管理客戶端類中
ManageClients.addIps(new Integer(uid), ip);
result = "success";//如果登陸成功,那么獲得該用戶的所有朋友列表
friends = getFriends(uid);
}else{
result = "fail";//登錄失敗
}
Packager packager = new Packager();
response = packager.loginPackager(operate,friends,result);
}else if("sendMessage".equals(operate)) {//如果是發送消息
Messages msg = parser.parseMessages(content);
int toId = msg.getToId();//找到要接受者的id
String ipaddress = ManageClients.ips.get(new Integer(toId)).toString();//通過id 獲得ip地址
ServerThread sendto = (ServerThread)ManageClients.clients.get(ipaddress);//通過ip地址獲取接受者的線程
sendto.send(msg.getMsg());//向發送端發送信息
Packager packager = new Packager();
String result = "success";
response = packager.sendPackager(operate,null,result);
}
return response;
}
public String getFriends(int uid) {
String sql = "select fid,uid,fuid,fName from friend where uid=" + uid;
DBConnection dbc = new DBConnection();
Connection con = null;
Statement sta = null;
ResultSet rs = null;
StringBuilder sb = new StringBuilder("");
try {
con = dbc.getConnect();
con = dbc.getConnect();
sta = con.createStatement();
rs = sta.executeQuery(sql);
while(rs.next()) {
sb.append(rs.getInt(1)+"#" + rs.getInt(2) + "#" + rs.getInt(3) + "#" + rs.getString(4) + "\n");
}
} catch (SQLException e) {
e.printStackTrace();
}
return sb.toString();
}
public int login(String content) {//登錄操作,登陸成功,返回朋友列表,否則返回錯誤原因
Parser parser = new Parser();
User user = parser.parseUser(content);//獲取用戶類
//然后去數據庫中查詢是否有該用戶,如果有該用戶,那么返回該用戶的主鍵id
String sql = "select uid,userPwd from user where userName='" + user.getUserName() +"'";
DBConnection dbc = new DBConnection();
Connection con = null;
Statement sta = null;
ResultSet rs = null;
String dbpwd = null;
int uid = 0;
try {
con = dbc.getConnect();
sta = con.createStatement();
rs = sta.executeQuery(sql);
while(rs.next()) {
uid = rs.getInt(1);
dbpwd = rs.getString(2);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
dbc.close(con, sta, rs);
}
if(dbpwd != null && dbpwd.equals(user.getUserPwd()) && uid != 0) {
return uid;
}else{
uid = 0;
return uid;
}
}
7、server包解析
7.1 Server類 開啟監聽端口,不斷監聽請求的用戶
public class Server {
public static void main(String[] args) {
new Server();
}
public Server()
{
ServerSocket serversocket = null;
Socket socket = null;
try {
serversocket = new ServerSocket(5000);
System.out.println("服務器已經啟動,正在監聽5000端口");
while(true) {
socket = serversocket.accept();
String ip = socket.getLocalAddress().getHostAddress();
ServerThread st = new ServerThread(socket,ip);//服務器端線程
st.start();
ManageClients.addClients(ip, st);//將該線程添加到ManageClients類中
int port = socket.getPort();
System.out.println("連接上的客戶端ip:" + ip + ",端口號:" + port);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
7.2 ServerThread類 ,用於轉發消息
public class ServerThread extends Thread{
private Socket socket;
private String ip;
private InputStream in;
private OutputStream out;
private static final int SIZE = 1024;
public Socket getSocket(){
return socket;
}
public ServerThread(Socket socket,String ip) {
this.socket = socket;
this.ip = ip;
try {
in = socket.getInputStream();
out = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
}
public void send(String mes) {
try {
out.write(mes.getBytes());
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
public void close() {
try {
if(in != null){
in.close();
in = null;
}
if(out != null) {
out.close();
out = null;
}
if(socket != null) {
socket.close();
socket = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
while(true) {
try {
byte[] buffer = new byte[SIZE];
int index = in.read(buffer);
String message = new String(buffer,0,index);//獲取客戶端發送過來的請求
Parser parser = new Parser();
String operate = parser.getOperate(message);
Controller controller = new Controller();
if(operate.equals("exit")) {
break;
}
String response = controller.doResponse(message,ip);//服務器處理發送過來的消息,如果不是收獲
//的操作不是exit
if(response != null)
send(response);//向客戶端發送請求操作的結果
} catch (IOException e) {
e.printStackTrace();
}finally{
close();
}
}
}
7.3 ManageClients
public class ManageClients {
//管理客戶端 ,用HashMap存儲用戶ip和線程的鍵值對
public static HashMap<String,ServerThread> clients = new HashMap<String,ServerThread>();//string sender
//用於用戶主鍵 和ip之間的鍵值對
public static HashMap<Integer,String> ips = new HashMap<Integer,String>();
public static void addClients(String senderip, ServerThread st) {
clients.put(senderip, st);
}
public static void addIps(Integer i, String ip) {
ips.put(i, ip);
}
}
服務器端結束 ,啟動Server中的main 函數,監聽是否有客戶端請求
