Java聊天软件 | Project
本文由发表于6年前 | Java基础 | 暂无评论 |  被围观 5,566 views+
1、项目基本描述:1.1项目名称:1.2开发工具:1.3项目描述:1.4项目技术要点:2、项目功能说明:3、项目工程(1) 客户端程序文件说明:(2) 服务器端程序文件说明:(3) 数据库文件结构4、项目运行截图:5、关键代码:
1、项目基本描述:
1.1项目名称:

Java聊天工具

1.2开发工具:

相关技术:MySQL + Swing + Socket
开发工具:Eclipse

1.3项目描述:

该软件能快速的注册一个账号,简单几步就可以查找好友并添加好友,以及系统处理结果的合理的提示,发送软件自带的一套兔斯基的gif图片,震屏效果,和传送任何类型的文件,基本上符合了现在人们对于即时聊天的需求。

1.4项目技术要点:
Java IO的操作
Java基于TCP的网络编程
MySQL在项目中的使用
Java Swing界面的设计
2、项目功能说明:

基本数据流图:

系统总体结构图:

层次功能图:

3、项目工程
(1) 客户端程序文件说明:

类名 功能
FriendData 保存每个好友的数据类
FriendList 保存好友列表类
FriendMap 保存好友图类
PersonalData 保存自己的数据类
UserManager 管理数据库处理相关函数的类
ChatMainMap 管理主界面的图类
ChatWinMap 管理聊天窗口的图类
ClientConnServerThread 客户端连接到服务器的线程类
ClientThreadMap 管理客户端的线程类
FileClient 接收文件的客户端类
FileServer 发送文件的服务器类
LoginCheck 客户端登录检验类
ConnMySQL 建立连接数据库类
ChatLogin 登录界面类
ChatMain_center 主界面中间部分面板类
ChatMain 主界面类
ChatWindow_right 聊天窗口右侧面板类
ChatWindow 聊天窗口类
ConfirmRequestMessage 确认消息框
MessageBox 自定义普通消息框
Onli① 用户表ne_message 上线消息框
ReceiverFileTransMessage 接收文件确认消息框
ReceiverMessage 消息管理器类
ReceiverRequestMessage 好友请求显示框
Register 注册面板
TransportFile 发送文件的文件选择器
FileTranMes 发送文件携带信息类
Message 发送信息类
MessageType 信息类型
User 用户
ColorTools 自定义颜色工具
FontTools 自定义字体工具
ImagePanel 图片面板

(2) 服务器端程序文件说明:
类名 功能
ChatServer 服务器处理类
ServerConnClientThread 服务器接收线程类
ThreadMap 管理客户端连接到服务器的线程类
ServerWindow 服务器窗口类
Message 发送信息类
MessageType 信息类型
User 用户
(3) 数据库文件结构

① 用户表

+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| userId | int(11) | NO | PRI | NULL | |
| username | varchar(50) | YES | | NULL | |
| password | varchar(20) | YES | | NULL | |
| picNo | varchar(10) | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+

② 心情表

+-------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| moodId | int(11) | NO | PRI | NULL | |
| moodcontent | varchar(280) | YES | | NULL | |
| pid | int(11) | YES | | NULL | |
| moodtime | varchar(20) | YES | | NULL | |
+-------------+--------------+------+-----+---------+-------+

③ 好友关系表

+------------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+---------+------+-----+---------+-------+
| relationid | int(11) | NO | PRI | NULL | |
| pid | int(11) | YES | | NULL | |
| friendid | int(11) | YES | | NULL | |
+------------+---------+------+-----+---------+-------+

④ 主键维护表

+------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+-------+
| gen_name | varchar(20) | YES | | NULL | |
| gen_number | int(11) | YES | | NULL | |
+------------+-------------+------+-----+---------+-------+
4、项目运行截图:

程序登录,注册和使用界面

5、关键代码:

(1) 数据库处理源代码:

/**
 * 在数据库中查询用户的资料,保存在本地的PersonalData中
 * @return
 */
    public void checkPersonalData(String id){
        PersonalData pd = PersonalData.getInstance();
        Connection conn = null;
        ResultSet rs = null;
        PreparedStatement pstmt = null;
        String sql = "select * from user where userid=?";
        try {
            conn = ConnMySQL.getConnection();
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, id);
            rs = pstmt.executeQuery();
            if (rs.next()) {
                pd.setUserId(rs.getString("userId"));
                pd.setUserName(rs.getString("username"));
                pd.setPicNo(rs.getString("picNo"));

            }
        }catch(SQLException e) {
            e.printStackTrace();
        }finally {
            ConnMySQL.close(rs);
            ConnMySQL.close(pstmt);
            ConnMySQL.close(conn);
        }
    }

(2) 其他数据处理关键语句:

/**
 * 在数据库中查询用户的资料,保存在本地的PersonalData中
 * @return
 */
    String sql = "select * from user where userid=?";

    /**
   * 在数据库中查询最新的心情
   * @return
   */
    String sql = "select moodcontent from moodlist where moodid = (select max(moodid)from moodlist where pid=?)";

    /**
   * 查找用户的所有好友,保存在本地程序中
   * @param id
   * @return
   */
    String sql = "select * from user where userId in (select friendid from relationlist where pid=?)";

    /**
   * 修改用户的昵称
   * @return
   */
    String sql = "update user set username=? where userId=?";

    /**
   * 修改用户的密码
   * @return
   */
    String sql = "update user set password=? where userId=?";

    /**
   * 获取自动编号ID
   * @param gen_name
   * @return
   */
    String sql = "select gen_number from generator where gen_name =?";

    /**
   * 更新Id维护列表,此方法给此类别的函数调用
   * @param gen_name
   * @param current_id
   * @param conn
   */
    String sql = "update generator set gen_number =? where gen_name = ?";

    /**
   * 添加心情
   * @param userId
   * @param mood
   * @return
   */
    String sql = "insert into moodlist values(?,?,?,?);";

    String sql = "select * from moodlist order by moodId desc limit 20";

    /**
   * 加好友
   * @param args
   */
    String sql = "insert into relationlist values(?,?,?);";

    String sql = "insert into user values(?,?,?,?);";

(3) 客户端接收线程的run()函数:

public void run() {

    while(run1){
        try {
            ObjectInputStream ois = new ObjectInputStream(sk.getInputStream());
            Message mes = (Message)ois.readObject();
            String messageType = mes.getMessageType();
            String sender = mes.getSender();
            String receiver = mes.getReceiver();
            if(messageType.equals(MessageType.CONTENT) || messageType.equals(MessageType.EXPRESSION)) {
// System.out.println(receiver);
                ChatWindow cw = ChatWinMap.getChatWindow(sender);
// System.out.println("来自用户"+cw);
                if(null == cw){
                    if(null != FriendMap.getFriendData(mes.getSender())) {
                        ReceiverMessage rm = new ReceiverMessage(mes);
                        Thread th = new Thread(rm);
                        th.start();
                    }
                } else {
                    cw.showMessage(mes);
                }

            } else if(messageType.equals(MessageType.ON_LINE)) {
                System.out.println("更新在线好友");
                String []all_online_ip = mes.getAll_online_id().split(",");
                ChatMainMap.getChatMain(mes.getReceiver()).getJp_center().changeOnlineState(all_online_ip);
                Online_message on_mes = new Online_message(mes.getSender());
                Thread th = new Thread(on_mes);
                th.start();
            } else if(messageType.equals(MessageType.SHAKE_WIN)) {
                ChatWindow cw = ChatWinMap.getChatWindow(sender);
                Thread th = new Thread(cw);
                th.start();
                cw.showMessage(mes);
            } else if(messageType.equals(MessageType.REQUEST)){
                //把该好友的资料丛数据库中取出并保存在客户端程序中

                ReceiverRequestMessage rsm = new ReceiverRequestMessage(mes);
                Thread th = new Thread(rsm);
                th.start();
            } else if(messageType.equals(MessageType.REQUEST_CONFIRM)) {

                //把双边的关系加入数据库
                UserManager.getInstance().addFriend(mes.getSender(), mes.getReceiver());
                System.out.println("保存好友:"+mes.getSender());
                System.out.println(mes.getReceiver());
                UserManager.getInstance().addFriend(mes.getReceiver(),mes.getSender());
                //把该好友资料从数据库中取得并保存在客户端程序中
                String []fds = UserManager.getInstance().checkUser(mes.getSender());
                FriendData fd = new FriendData();
                fd.setFriendId(fds[0]);
                fd.setFriendName(fds[1]);
                fd.setFriendPic(fds[2]);
                FriendList.getFriendList().add(fd);
                FriendMap.addFriendData(fds[0], fd);
                //更新好友列表
                String all_ip = mes.getAll_online_id();
                ChatMainMap.getChatMain(PersonalData.getUserId()).updataCenter(all_ip);

                ConfirmRequestMessage rm = new ConfirmRequestMessage(mes);
                Thread th = new Thread(rm);
                th.start();
            } else if(messageType.equals(MessageType.REQUEST_REFUSE)){
                ConfirmRequestMessage rm = new ConfirmRequestMessage(mes);
                Thread th = new Thread(rm);
                th.start();
            } else if(messageType.equals(MessageType.NOT_ONLINE)) {
                MessageBox msb = new MessageBox("对方不在线");
                Thread th = new Thread(msb);
                th.start();
            } else if(messageType.equals(MessageType.OFF_LINE)) {
                System.out.println("有人下线了");
                //更新好友列表
                if(sender.equals(PersonalData.getUserId())) {
                    break;
                }
                String all_ip = mes.getAll_online_id();
                System.out.println("当前在线好友:"+mes.getAll_online_id());
                ChatMainMap.getChatMain(PersonalData.getUserId()).updataCenter(all_ip);
            } else if(messageType.equals(MessageType.FILE_TRAN)) {
                ReceiverFileTransMessage rftm = new ReceiverFileTransMessage(mes);
                Thread th = new Thread(rftm);
                th.start();
            } else if(messageType.equals(MessageType.FILE_TRAN_CONFIRM)) {
                ConfirmRequestMessage rm = new ConfirmRequestMessage(mes);
                Thread th = new Thread(rm);
                th.start();
            } else if(messageType.equals(MessageType.FILE_TRAN_REFUSE)) {
                ConfirmRequestMessage rm = new ConfirmRequestMessage(mes);
                Thread th = new Thread(rm);
                th.start();
            }
        }catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(4) 接收文件的客户端run()函数

public void run() {
    Socket server;
    try {
        server = new Socket(msg.getFileTranMes().getServerIp(), Integer.parseInt(msg.getFileTranMes().getServerPort()));
        //创建网络接受流接受服务器文件数据
        InputStream netIn = server.getInputStream();

        InputStream in = new DataInputStream(new BufferedInputStream(netIn));
        //创建缓冲区缓冲网络数据
// System.out.println("传输文件的长度:"+in.available());
        byte[] buf = new byte[2048];
        int num = in.read(buf);
        while (num != (-1)) {
            //是否读完所有数据
            raf.write(buf, 0, num);//将数据写往文件
            raf.skipBytes(num);//顺序写文件字节
            num = in.read(buf);//继续从网络中读取文件
            width = width+1;
            width_1 = width/size * 180;
            jl2.setBounds(10,40,(int)(width_1),20);
            jl2.setText(" "+(int)(width/size*100)+"%");
        }
        jl2.setText("传送完成");
        in.close();
        raf.close();
    } catch (UnknownHostException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

(5) 发送文件的服务器run()函数

public void run() {
    //创建网络服务器接受客户请求
    ServerSocket ss;
    Socket client = null;
    try {
        ss = new ServerSocket(5678);
        client = ss.accept();
    } catch (IOException e) {
        // TODO Auto-generated catch block
// e.printStackTrace();
    }

    //创建网络输出流并提供数据包装器
    OutputStream netOut;
    try {
        netOut = client.getOutputStream();
        OutputStream doc = new DataOutputStream(
        new BufferedOutputStream(netOut));

        //创建文件读取缓冲区
        byte[] buf = new byte[2048];
        int num = fos.read(buf);

        while (num != (-1)) {//是否读完文件
            doc.write(buf, 0, num);//把文件数据写出网络缓冲区
            doc.flush();//刷新缓冲区把数据写往客户端
            num = fos.read(buf);//继续从文件中读取数据
            width = width+1;
            width_1 = width/size * 180;
            jl2.setBounds(10,40,(int)(width_1),20);
            jl2.setText(" "+(int)(width/size*100)+"%");
        }
        jl2.setText("传送完成");
        fos.close();
        doc.close();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

(6) 客户端登录验证的函数

public boolean CheckUser (Message msg) {
    boolean check = false;
    try {
        sk = new Socket(ip,port);
        System.out.println("获取socket");
    } catch (UnknownHostException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

    try {
        ObjectOutputStream oos = new ObjectOutputStream(sk.getOutputStream());
        oos.writeObject(msg);
        ObjectInputStream ois=new ObjectInputStream(sk.getInputStream());
        Message mss=(Message)ois.readObject();
        if(mss.getMessageType().equals(MessageType.ON_LINE)){
            ClientConnServerThread ccst = new ClientConnServerThread(sk);
            System.out.println("赋给ClietThreadMap的id:" + mss.getUser().getUserId());
            ClientThreadMap.setClientThreadHm(mss.getUser().getUserId(), ccst);
            ccst.start();
            User u = mss.getUser();

            //在这里查询数据库中用户的信息,并保存在客户端程序中
            UserManager.getInstance().checkPersonalData(u.getUserId());
            PersonalData.setMood(UserManager.getInstance().checkMood(u.getUserId()));
            UserManager.getInstance().checkAllFriend(u.getUserId());

            ChatMain cm = new ChatMain(u,mss);
            ChatMainMap.addChatMain(u.getUserId(), cm);
            check = true;

        } else if(mss.getMessageType().equals(MessageType.ON_LINE_FAIL)) {
// JOptionPane.showMessageDialog(null, "用户名或密码错误");
            MessageBox msgb = new MessageBox("用户名或密码错误!");
            Thread th = new Thread(msgb);
            th.start();
            ois.close();
            oos.close();
            sk.close();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return check;

}

(7) 连接数据库的函数

public static Connection getConnection() {
    Connection conn = null;
    try {
        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection("jdbc:mysql://localhost/chatdata?user=user1&password=m123");

    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return conn;
}

(8) 发送消息的响应事件:

if(e.getSource() == jb_send) {
    if(cardNo == 0) {
        Date time = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("hh:mm:ss");
        String sendtime = sdf.format(time);
        Document doc = jta.getDocument();
        try {
            doc.insertString(jta.getDocument().getLength(),PersonalData.getUserName()+"("+this.userId+") "+sendtime+"\n", null);
            doc.insertString(jta.getDocument().getLength()," "+jta_send.getText()+"\n", null);
        } catch (BadLocationException e2) {
            // TODO Auto-generated catch block
            e2.printStackTrace();
        }                

        String sendMes = jta_send.getText();
        jta_send.setText("");

        Message mes = new Message();
        mes.setSender(this.userId);
        mes.setReceiver(this.receiveId);
// System.out.println("发送前receiverId的值:"+receiveId);
        mes.setContent(sendMes);
        mes.setSendtime(sendtime);
        mes.setPic_no(PersonalData.getPicNo());
        mes.setMessageType(MessageType.CONTENT);
        try {
            ObjectOutputStream oos = new ObjectOutputStream(ClientThreadMap.getClientThreadHm(this.userId).getSocket().getOutputStream());
            oos.writeObject(mes);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    } else {
        Date time = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("MM-DD hh:mm:ss");
        String sendtime = sdf.format(time);
        Document doc = jta.getDocument();
        try {
            doc.insertString(jta.getDocument().getLength(),PersonalData.getUserName()+"("+this.userId+") "+sendtime+"\n", null);
            doc.insertString(jta.getDocument().getLength(), " ", null);
            jta.setCaretPosition(doc.getLength());
            jta.insertIcon(new ImageIcon("images/"+faceNo+".gif"));
            doc.insertString(jta.getDocument().getLength(), "\n", null);
        } catch (BadLocationException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        System.out.println("send picture:"+faceNo);
        String sendMes = faceNo;

        Message mes = new Message();
        mes.setSender(this.userId);
        mes.setReceiver(this.receiveId);
        mes.setContent(sendMes);
        mes.setSendtime(sendtime);
        mes.setMessageType(MessageType.EXPRESSION);
        try {
// System.out.println("==============准备发送消息================");
            ObjectOutputStream oos = new ObjectOutputStream(ClientThreadMap.getClientThreadHm(this.userId).getSocket().getOutputStream());
// System.out.println(oos+":"+mes.getMessageType());
            oos.writeObject(mes);
        } catch (Exception e1) {
            e1.printStackTrace();
        }
    }

}

(9) 显示消息的函数:

public void showMessage(Message ms) {
    try {
        Document doc = chat_window.jta.getDocument();
        doc.insertString(chat_window.jta.getDocument().getLength(),FriendMap.getFriendData(this.receiveId).getFriendName()+"("+ms.getSender()+") "+ms.getSendtime()+"\n", null);
        if(ms.getMessageType().equals(MessageType.CONTENT)){
            doc.insertString(chat_window.jta.getDocument().getLength()," "+ms.getContent()+"\n", null);
        } else if(ms.getMessageType().equals(MessageType.EXPRESSION)){
            //插入图片
            doc.insertString(chat_window.jta.getDocument().getLength(), " ", null);
            chat_window.jta.setCaretPosition(doc.getLength());
            chat_window.jta.insertIcon(new ImageIcon("images/"+ms.getContent()+".gif"));
            doc.insertString(chat_window.jta.getDocument().getLength(), "\n", null);
        } else if(ms.getMessageType().equals(MessageType.SHAKE_WIN)){
            doc.insertString(chat_window.jta.getDocument().getLength()," "+ms.getContent()+"\n", null);
        }
    } catch (BadLocationException e) {
        e.printStackTrace();
    }
}

(10) 服务器端等待和验证客户端信息函数

public void run() {
    while (true) {
        try {
            System.out.println("服务器正在等待客户端的连接");
            Socket sk = ssk.accept();
            ObjectInputStream ois=new ObjectInputStream(sk.getInputStream());
            Message ms = (Message)ois.readObject();
            User u = ms.getUser();
            String password = UserManager.getInstance().checkPersonalData(u.getUserId());
            if(null != password){
                if(u.getPassword().equals(password)) {
                    System.out.println("验证密码");
                    ServerConnClientThread sct = new ServerConnClientThread(sk);
                    sct.start();
                    ThreadMap.addThread(u.getUserId(), sct);

                    /*验证成功后,把所有在线用户返回给所有的用户*/
                    Iterator it = ThreadMap.hm.entrySet().iterator();
                    String all_id = "";
                    while(it.hasNext()){
                        Entry entry = (Entry) it.next();
                        all_id+=entry.getKey();
                        if(it.hasNext()){
                            all_id+=",";
                        }
// System.out.println(all_id);
                    }
                    ms.setAll_online_id(all_id);
                    Iterator it2 = ThreadMap.hm.entrySet().iterator();
                    while(it2.hasNext()) {
                        Entry entry = (Entry) it2.next();
                        ServerConnClientThread st = (ServerConnClientThread) entry.getValue();
                        ObjectOutputStream oos = new ObjectOutputStream(st.sk.getOutputStream());
                        ms.setReceiver(entry.getKey()+"");
                        oos.writeObject(ms);
                    }
                } else {
                    //密码错误
                    Message mss = new Message();
                    mss.setMessageType(MessageType.ON_LINE_FAIL);
                    mss.setContent("用户密码错误!");
                    ObjectOutputStream oos=new ObjectOutputStream(sk.getOutputStream());
                    oos.writeObject(mss);
                    sk.close();
                }
            } else {
                //用户不存在
                Message mss = new Message();
                mss.setMessageType(MessageType.ON_LINE_FAIL);
                mss.setContent("该用户不存在");
                ObjectOutputStream oos=new ObjectOutputStream(sk.getOutputStream());
                oos.writeObject(mss);
                sk.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

(11) 服务器转发消息函数

public void run() {
    boolean run = true;
    Message m = null;
    while(true){
        try {
            ObjectInputStream ois = new ObjectInputStream(sk.getInputStream());
            m=(Message)ois.readObject();
            System.out.println("开始接收数据");
            String messageType = m.getMessageType();
            String sender = m.getSender();
            String receiver = m.getReceiver();

            if(m.getMessageType().equals(MessageType.OFF_LINE)) {

                //重新获取在线ID
                Iterator it = ThreadMap.hm.entrySet().iterator();
                String all_id = "";
                while(it.hasNext()){
                    Entry entry = (Entry) it.next();
                    if(!(entry.getKey().toString().equals(sender))){
                        all_id+=entry.getKey();
                        if(it.hasNext()){
                            all_id+=",";
                        }
                    }
                }
                String all_id2 = all_id.substring(0, all_id.length());
                System.out.println(all_id2);
                //把消息传给每个人
                Iterator it2 = ThreadMap.hm.entrySet().iterator();
                ObjectOutputStream oos = null;
                ServerConnClientThread scct = null;
                while(it2.hasNext()){
                    Entry entry = (Entry) it2.next();
                    scct = (ServerConnClientThread) entry.getValue();
                    oos = new ObjectOutputStream(scct.sk.getOutputStream());
                    m.setAll_online_id(all_id2);
                    oos.writeObject(m);
                }
                ThreadMap.hm.remove(sender);
                break;

            } else {
                if(messageType.equals(MessageType.REQUEST)||messageType.equals(MessageType.REQUEST_CONFIRM)) {
                    Iterator it = ThreadMap.hm.entrySet().iterator();
                    String all_id = "";
                    while(it.hasNext()){
                        Entry entry = (Entry) it.next();
                        all_id+=entry.getKey();
                        if(it.hasNext()){
                            all_id+=",";
                        }
// System.out.println(all_id);
                    }
                    m.setAll_online_id(all_id);
                }
                ObjectOutputStream oos = new ObjectOutputStream(ThreadMap.getThread(receiver).sk.getOutputStream());
                System.out.println("开始转发数据");
                oos.writeObject(m);
            }                

        }catch(Exception ex){
            ex.printStackTrace();
        }
    }

}
除了文章中有特别说明,均为IT宅原创文章,转载请以链接形式注明出处。
本文链接:http://www.itzhai.com/java-chat-software-project.html
关键字: ,
arthinking Java技术交流群:280755654,入门群:428693174 more
分享到:
 
2011 11/14
文章评论
    没有评论

抱歉!评论已关闭.

给我留言

有人回复时邮件通知我
Java基础的相关文章
随机文章 本月热门 热评
1 JVM笔记 – 走近Java 2014/11/1
2 Java基础笔记 – 枚举类型的使用介绍和静态导入 2011/10/31
3 JVM笔记 – 程序编译与代码优化(早期(编译期)优化) 2015/3/1
4 Java基础笔记 – 线程的生命周期 线程的状态 优先级和常用方法 2011/10/24
5 jQuery插件的编写相关技术 设计总结和最佳实践 2012/8/8
6 Chrome插件开发 – 浏览器交互 2013/6/4
友情推荐 更多
破博客 文官洗碗安天下,武将打怪定乾坤。多么美好的年代,思之令人泪落。
Mr.5's Life 白天是一名程序员,晚上就是个有抱负的探索者
行知-追寻技术之美 关注大数据,分布式系统
我爱编程 编程成长轨迹
Cynthia's Blog 学习笔记 知识总结 思考感悟
 
猜您喜欢
欢迎关注我的公众号 IT宅
关于IT宅 文章归档

IT宅中的文章除了标题注明转载或有特别说明的文章,均为IT宅的技术知识总结,学习笔记或随笔。如果喜欢,请使用文章下面提供的分享组件。转载请注明出处并加入文章的原链接。 感谢大家的支持。

联系我们:admin@itzhai.com

Theme by arthinking. Copyright © 2011-2015 IT宅.com 保留所有权利.