利用sorket实现聊天功能-服务端实现

工具包

package loaderman.im.util;

public class Constants {
    public static final String SERVER_IP = "192.168.0.195";
    public static final int SERVER_PORT = 8080;

    public static final int CLIENT_SERVER_PORT = 8081;
    public static final int CLIENT_FILE_TRANSPORT_PORT = 8082;
    public static final String REGISTER_FAIL = 0+"";
}
package loaderman.im.util;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

/**
 * 数据库工具类
 *
 *
 */
public class DButil {
    /**
     * 连接数据库
     *
     * @return 数据库连接对象
     */
    public static Connection connect() {

        try {

            Class.forName("com.mysql.jdbc.Driver");
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/qq?useUnicode=true&characterEncoding=utf-8", "root",
                    "root");
            return conn;
        }  catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 关闭数据库
     *
     * @param conn
     *            传入数据库连接对象
     */
    public static void close(Connection con) {
        if (con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


}
package loaderman.im.util;

import java.text.SimpleDateFormat;
import java.util.Date;

public class MyDate {
    public static String getDateCN() {
        SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日  HH:mm:ss");
        String date = format.format(new Date(System.currentTimeMillis()));
        return date;// 2018年10月03日 23:41:31
    }

    public static String getDateEN() {
        SimpleDateFormat format1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date1 = format1.format(new Date(System.currentTimeMillis()));
        return date1;// 2018-10-03 23:41:31
    }
}

传输对象建立

packageloaderman.im.tran;

import java.io.Serializable;

/**
 * 传输的对象,直接通过Socket传输的最大对象
 *
 * @author xjyt
 */
public class TranObject<T> implements Serializable {
    /**
     *
     */
    private static final long serialVersionUID = 1L;

    private TranObjectType type;// 发送的消息类型

    private String fromUser;// 来自哪个用户
    private String toUser;// 发往哪个用户

    private T object;// 传输的对象

    public TranObject(TranObjectType type) {
        this.type = type;
    }

    public String getFromUser() {
        return fromUser;
    }

    public void setFromUser(String fromUser) {
        this.fromUser = fromUser;
    }

    public String getToUser() {
        return toUser;
    }

    public void setToUser(String toUser) {
        this.toUser = toUser;
    }

    public T getObject() {
        return object;
    }

    public void setObject(T object) {
        this.object = object;
    }

    public TranObjectType getType() {
        return type;
    }

    @Override
    public String toString() {
        return "TranObject [type=" + type + ", fromUser=" + fromUser
                + ", toUser=" + toUser + ", object=" + object + "]";
    }
}
packageloaderman.im.tran;

/**
 * 传输对象类型
 *

 *
 */
public enum TranObjectType {
    REGISTER, // 注册
    LOGIN, // 用户登录
    LOGOUT, // 用户退出登录
    FRIENDLOGIN, // 好友上线
    FRIENDLOGOUT, // 好友下线
    MESSAGE, // 用户发送消息
    UNCONNECTED, // 无法连接
    FILE, // 传输文件
    REFRESH,//刷新好友列表
    OFFLINEMESSAGE//离线消息
}

实体类

package loaderman.im.bean;

import java.io.Serializable;
//离线消息实体类
public class OffLineMessage implements Serializable {
    private String fromUser;
    private String toUser;
    private String textMsg;
    private int msgType;
    private String msgTime;



    public OffLineMessage() {
    }


    public OffLineMessage(String fromUser, String toUser, String textMsg, int msgType, String msgTime) {
        this.fromUser = fromUser;
        this.toUser = toUser;
        this.textMsg = textMsg;
        this.msgType = msgType;
        this.msgTime = msgTime;
    }

    public String getFromUser() {
        return fromUser;
    }

    public void setFromUser(String fromUser) {
        this.fromUser = fromUser;
    }

    public String getToUser() {
        return toUser;
    }

    public void setToUser(String toUser) {
        this.toUser = toUser;
    }

    public String getTextMsg() {
        return textMsg;
    }

    public void setTextMsg(String textMsg) {
        this.textMsg = textMsg;
    }

    public int getMsgType() {
        return msgType;
    }

    public void setMsgType(int msgType) {
        this.msgType = msgType;
    }
    public String getMsgTime() {
        return msgTime;
    }

    public void setMsgTime(String msgTime) {
        this.msgTime = msgTime;
    }
}
package loaderman.im.bean;

import java.io.Serializable;

/**
 * 文本消息

 */
public class TextMessage implements Serializable {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private String message;

    public TextMessage() {

    }

    public TextMessage(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
package loaderman.im.bean;

import java.io.Serializable;

/**
 * 用户对象
 *
 */
public class User implements Serializable {

    private String userId;
    private String userName;

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    private String userHeadPhoto;

    private String  password;
    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getUserHeadPhoto() {
        return userHeadPhoto;
    }

    public void setUserHeadPhoto(String userHeadPhoto) {
        this.userHeadPhoto = userHeadPhoto;
    }
    @Override
    public String toString() {
        return "User{" +
                "userId='" + userId + '\'' +
                ", userName='" + userName + '\'' +
                ", userHeadPhoto='" + userHeadPhoto + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

数据库建立

packageloaderman.dao;

import java.util.ArrayList;

import loaderman.im.bean.OffLineMessage;
import loaderman.im.bean.User;

public interface UserDao {
    //注册成功返回用户id
    public String register(User u);

    public boolean login(User u);

    public ArrayList<User> refresh(String id);

    public ArrayList<OffLineMessage> getOffLineMessage(String id);

    public void logout(String id);
    boolean isHasUser(String userId);


   public boolean  saveOffLineMessage(OffLineMessage offLineMessage);

}
package loaderman.dao.impl;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.lang.String;


import loaderman.im.bean.OffLineMessage;
import loaderman.im.bean.User;
import loaderman.im.util.Constants;
import loaderman.im.util.DButil;
import loaderman.im.util.MyDate;
import loaderman.dao.UserDao;

public class UserDaoImpl implements UserDao {

    @Override
    public String register(User u) {
        String userID;
        Connection con = DButil.connect();
        String sql1 = "insert into user(userID,userName,password,userHeadPhoto,time) values(?,?,?,?,?)";
        String sql2 = "select userID from user";
        UserDao dao = UserDaoFactory.getInstance();
        boolean hasUser = dao.isHasUser(u.getUserId());
        if (!hasUser) {
            try {
                PreparedStatement ps = con.prepareStatement(sql1);
                ps.setString(1, u.getUserId());
                ps.setString(2, u.getUserName());
                ps.setString(3, u.getPassword());
                ps.setString(4, u.getUserHeadPhoto());
                ps.setString(5, MyDate.getDateCN());
                int res = ps.executeUpdate();
                if (res > 0) {
                    PreparedStatement ps2 = con.prepareStatement(sql2);
                    ResultSet rs = ps2.executeQuery();
                    if (rs.last()) {

                        userID = rs.getString("userID");
                        createFriendtable(userID);// 注册成功后,创建一个已用户id为表名的表,用于存放好友信息
                        return userID;
                    }
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                DButil.close(con);
            }
        }

        return Constants.REGISTER_FAIL;
    }

    @Override
    public boolean login(User u) {
        Connection con = DButil.connect();
        String sql = "select * from user where userID=? and password=?";
        try {
            PreparedStatement ps = con.prepareStatement(sql);
            ps.setString(1, u.getUserId());
            ps.setString(2, u.getPassword());
            ResultSet rs = ps.executeQuery();
            if (rs.first()) {

                return true;
            }
        } catch (SQLException e) {
            return false;
        } finally {
            DButil.close(con);
        }
         return false;
    }


    /**
     * 查找自己
     */
    public User findMe(String UserId) {
        User me = new User();
        Connection con = DButil.connect();
        String sql = "select * from user where userID=?";
        PreparedStatement ps;
        try {
            ps = con.prepareStatement(sql);
            ps.setString(1, UserId);
            ResultSet rs = ps.executeQuery();
            if (rs.first()) {
                me.setUserId(rs.getString("userID"));
                me.setUserName(rs.getString("userName"));
                me.setUserHeadPhoto(rs.getString("userHeadPhoto"));

            }
            return me;
        } catch (SQLException e) {
            // e.printStackTrace();
        } finally {
            DButil.close(con);
        }
        return null;
    }

    public boolean isHasUser(String userId) {
        Connection con = DButil.connect();
        String sql = "select * from user where userID=?";
        PreparedStatement ps;
        try {
            ps = con.prepareStatement(sql);
            ps.setString(1, userId);
            ResultSet rs = ps.executeQuery();
            if (rs.first()) {
                return true;
            } else {
                return false;
            }

        } catch (SQLException e) {
            // e.printStackTrace();
        } finally {
            DButil.close(con);
            return false;
        }

    }
   //保存离线消息
    @Override
    public boolean saveOffLineMessage(OffLineMessage offLineMessage) {
        Connection con = DButil.connect();
        String sql = "insert into offlinemessage(fromUser,toUser,textMsg,msgType,msgTime) values(?,?,?,?,?)";
        PreparedStatement ps = null;
        try {
            ps = con.prepareStatement(sql);
            ps.setString(1, offLineMessage.getFromUser());
            ps.setString(2, offLineMessage.getToUser());
            ps.setString(3, offLineMessage.getTextMsg());
            ps.setInt(4, offLineMessage.getMsgType());
            ps.setString(5, offLineMessage.getMsgTime());
            int res = ps.executeUpdate();
            if (res > 0) {
                return true;
            } else {
                return false;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return false;

    }
    @Override
    public ArrayList<OffLineMessage> getOffLineMessage(String toUser) {
        ArrayList<OffLineMessage> list = new ArrayList<OffLineMessage>();

        Connection con = DButil.connect();
        String sql = "select * from offlinemessage where toUser= ?" ;
        String sql2 = "delete from offlinemessage where toUser= ?" ;

        PreparedStatement ps;
        try {
            ps = con.prepareStatement(sql);

            ps.setString(1, toUser);
            ResultSet rs = ps.executeQuery();
            if (rs.first()) {
                System.out.println("离线消息获取");
                do {
                    OffLineMessage offLineMessage = new OffLineMessage();
                    offLineMessage.setFromUser(rs.getString("fromUser"));
                    offLineMessage.setMsgType(rs.getInt("msgType"));
                    offLineMessage.setTextMsg(rs.getString("textMsg"));
                    offLineMessage.setToUser(rs.getString("toUser"));
                    offLineMessage.setMsgTime(rs.getString("msgTime"));
                    list.add(offLineMessage);
                } while (rs.next());
            }
            ps = con.prepareStatement(sql2);
            ps.setString(1, toUser);
            ps.executeUpdate();
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
            System.out.println("异常" + e.toString());
        } finally {
            DButil.close(con);
        }
        return null;

    }


    /**
     * 刷新好友列表
     */
    @Override
    public ArrayList<User> refresh(String userId) {
        ArrayList<User> list = new ArrayList<User>();
        User me = findMe(userId);
        System.out.println("me -- > " + me);
        list.add(me);// 先添加自己
        Connection con = DButil.connect();
        String sql = "select * from " + userId;

        PreparedStatement ps;
        try {
            ps = con.prepareStatement(sql);
//            ps.setString(1, userId);
            ResultSet rs = ps.executeQuery();
            if (rs.first()) {
                System.out.println("好友消息获取");
                do {
                    User friend = new User();
                    friend.setUserId(rs.getString("userID"));
                    friend.setUserName(rs.getString("userName"));
                    friend.setUserHeadPhoto(rs.getString("userHeadPhoto"));
                    list.add(friend);
                } while (rs.next());
            }
            return list;
        } catch (SQLException e) {
            e.printStackTrace();
            System.out.println("异常" + e.toString());
        } finally {
            DButil.close(con);
        }
        return null;
    }



    /**
     * 注册成功后,创建一个用户表,保存该用户好友
     *
     * @param userID
     */
    public void createFriendtable(String userID) {
        Connection con = DButil.connect();
        try {
            String sql = "create table _" + userID
                    + " (id int auto_increment not null primary key,"
                    + " userID varchar(20) not null,"
                    + " userName varchar(20) not null,"
                    + "userHeadPhoto varchar(50) )";
            PreparedStatement ps = con.prepareStatement(sql);
            int res = ps.executeUpdate();
            System.out.println(res);
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DButil.close(con);
        }
    }


    /**
     * 下线更新状态为离线
     */
    @Override
    public void logout(String userID) {
        Connection con = DButil.connect();
        try {
            String sql = "update user set _isOnline=0 where userID=?";
            PreparedStatement ps = con.prepareStatement(sql);
            ps.setString(1, userID);
            ps.executeUpdate();
//            updateAllOff(userID);
            // System.out.println(res);
        } catch (SQLException e) {
            // e.printStackTrace();
        } finally {
            DButil.close(con);
        }
    }


    public List<String> getAllId() {
        Connection con = DButil.connect();
        List<String> list = new ArrayList<String>();
        try {
            String sql = "select _id from user";
            PreparedStatement ps = con.prepareStatement(sql);
            ResultSet rs = ps.executeQuery();
            if (rs.first()) {
                do {
                    String id = rs.getString("_id");
                    list.add(id);
                } while (rs.next());
            }
            // System.out.println(list);
            return list;
        } catch (SQLException e) {
            // e.printStackTrace();
        } finally {
            DButil.close(con);
        }
        return null;
    }

    public static void main(String[] args) {
//        User u = new User();
        UserDaoImpl dao = new UserDaoImpl();
        ArrayList<OffLineMessage> test = dao.getOffLineMessage("test");
        System.out.println(test.size());
//
//
//        System.out.println(list);

    }

}
package loaderman.dao.impl;

import petrochina.xjyt.fcydyy.dao.UserDao;

public class UserDaoFactory {
    private static UserDao dao;

    public static UserDao getInstance() {
        if (dao == null) {
            dao = new UserDaoImpl();
        }
        return dao;
    }
}

服务器实现

package loaderman.server;

import java.io.*;
import java.net.Socket;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;

import loaderman.im.bean.OffLineMessage;
import loaderman.im.tran.TranObjectType;
import loaderman.dao.UserDao;
import loaderman.im.bean.TextMessage;
import loaderman.im.bean.User;
import loaderman.im.tran.TranObject;
import loaderman.im.util.MyDate;
import loaderman.dao.impl.UserDaoFactory;

/**
 * 读消息线程和处理方法
 *
 */
public class InputThread extends Thread {
    private Socket socket;// socket对象
    private OutputThread out;// 传递进来的写消息线程,因为我们要给用户回复消息啊
    private OutputThreadMap map;// 写消息线程缓存器
    private ObjectInputStream ois;// 对象输入流
    private boolean isStart = true;// 是否循环读消息

    public InputThread(Socket socket, OutputThread out, OutputThreadMap map) {
        this.socket = socket;
        this.out = out;
        this.map = map;
        try {
            ois = new ObjectInputStream(socket.getInputStream());// 实例化对象输入流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void setStart(boolean isStart) {// 提供接口给外部关闭读消息线程
        this.isStart = isStart;
    }

    @Override
    public void run() {
        try {
            while (isStart) {
                // 读取消息

                System.out.println("InputThread读消息开始");
                readMessage();
                System.out.println("InputThread读消息完成");

            }
            if (ois != null)
                ois.close();
            if (socket != null)
                socket.close();
        } catch (ClassNotFoundException e) {
            System.out.println("异常信息1");
            isStart = false;// 结束自己的读循环
            e.printStackTrace();
        } catch (SocketException e){
            System.out.println("InputThread1"+e.toString());

        }catch (IOException e) {
            System.out.println("InputThread2"+e.toString());
            isStart = false;// 结束自己的读循环
            e.printStackTrace();
        }catch (Exception e){
            System.out.println("InputThread3"+e.toString());
            e.printStackTrace();
        }
    }


    /**
     * 读消息以及处理消息,抛出异常
     *
     * @throws IOException
     * @throws ClassNotFoundException
     */
    public void readMessage() throws IOException, ClassNotFoundException {
        System.out.println("readMessage 1 ");


        Object readObject = ois.readObject();// 从流中读取对象
        System.out.println("readMessage 2 ");
        UserDao dao = UserDaoFactory.getInstance();// 通过dao模式管理后台
        if (readObject != null && readObject instanceof TranObject) {
            TranObject read_tranObject = (TranObject) readObject;// 转换成传输对象
            switch (read_tranObject.getType()) {
                case REGISTER:// 如果用户是注册
                    System.out.println("REGISTER");
                    User registerUser = (User) read_tranObject.getObject();

                    String registerResult = dao.register(registerUser);
                    System.out.println(MyDate.getDateCN() + " 新用户注册:"
                            + registerResult);
                    // 给用户回复消息
                    TranObject<User> register2TranObject = new TranObject<User>(
                            TranObjectType.REGISTER);
                    User register2user = new User();
                    register2user.setUserId(registerResult);
                    register2TranObject.setObject(register2user);
                    out.setMessage(register2TranObject);
                    break;
                case LOGIN:
                    System.out.println("LOGIN");

                    User loginUser = (User) read_tranObject.getObject();

                    System.out.println("登录用户 " + loginUser.toString());
                    boolean login = dao.login(loginUser);
                    TranObject<Boolean> login2Object = new TranObject<Boolean>(
                            TranObjectType.LOGIN);
                    User login2User = new User();
                    login2User.setUserId(loginUser.getUserId());

                    if (login) {// 如果登录成功
                        System.out.println("登录成功");
                        TranObject<User> onObject = new TranObject<User>(
                                TranObjectType.LOGIN);

                        onObject.setObject(login2User);
                        for (OutputThread onOut : map.getAll()) {
                            onOut.setMessage(onObject);// 广播一下用户上线
                        }

                        map.add(loginUser.getUserId(), out);// 先广播,再把对应用户id的写线程存入map中,以便转发消息时调用


                        login2Object.setObject(true);// 把好友列表加入回复的对象中
                        System.out.println(MyDate.getDateCN() + " 用户:" + loginUser.getUserId() + " 上线了");

                    } else {
                        login2Object.setObject(false);// 把好友列表加入回复的对象中
                    }
                    out.setMessage(login2Object);// 同时把登录信息回复给用户

                    System.out.println(MyDate.getDateCN() + " 用户:"
                            + loginUser.getUserId() + " 上线了");
                    break;
                case LOGOUT:// 如果是退出,更新数据库在线状态,同时群发告诉所有在线用户
                    System.out.println("LOGOUT");

                    User logoutUser = (User) read_tranObject.getObject();
                    String offId = logoutUser.getUserId();
                    System.out
                            .println(MyDate.getDateCN() + " 用户:" + offId + " 下线了");
                    dao.logout(offId);
                    isStart = false;// 结束自己的读循环
                    map.remove(offId);// 从缓存的线程中移除
                    out.setMessage(null);// 先要设置一个空消息去唤醒写线程
                    out.setStart(false);// 再结束写线程循环

                    TranObject<User> offObject = new TranObject<User>(
                            TranObjectType.LOGOUT);
                    User logout2User = new User();
                    logout2User.setUserId(logoutUser.getUserId());
                    offObject.setObject(logout2User);
                    for (OutputThread offOut : map.getAll()) {// 广播用户下线消息
                        offOut.setMessage(offObject);
                    }
                    break;
                case MESSAGE:// 如果是转发消息(可添加群发)
                    System.out.println("MESSAGE");

                    // 获取消息中要转发的对象id,然后获取缓存的该对象的写线程
                    String id2 = read_tranObject.getToUser();

                    System.out.println("给谁发" + id2);
                    OutputThread toOut = map.getById(id2);
                    if (toOut != null) {// 如果用户在线
                        System.out.println("用户在线");
                        TextMessage tm = (TextMessage) read_tranObject.getObject();
                        System.out.println("消息内容 " + tm.getMessage());
                        toOut.setMessage(read_tranObject);

                    } else {// 如果为空,说明用户已经下线,回复用户
                        TextMessage text = new TextMessage();
                        System.out.println("用户不在线");

                        TranObject<TextMessage> offText = new TranObject<TextMessage>(
                                TranObjectType.MESSAGE);
                        offText.setObject(text);
                        offText.setFromUser(id2);
                        String fromUser = read_tranObject.getFromUser();
                        String toUser = read_tranObject.getToUser();
                        TextMessage tm = (TextMessage) read_tranObject.getObject();
                        //保存离线消息
                        OffLineMessage offLineMessage = new OffLineMessage(fromUser, toUser, tm.getMessage(), 0, MyDate.getDateEN());
                        boolean saveOffLineMessage = dao.saveOffLineMessage(offLineMessage);
                        if (saveOffLineMessage) {
                            text.setMessage("亲!对方不在线哦,您的消息将暂时保存在服务器");
                        } else {
                            text.setMessage("亲!无法发送,请稍后重试");
                        }
                        out.setMessage(offText);
                    }
                    break;
                case REFRESH:
                    System.out.println("REFRESH");
                    System.out.println("当前用户REFRESH" + read_tranObject
                            .getFromUser());

                    List<User> refreshList = dao.refresh(read_tranObject.getFromUser());
                    System.out.println(refreshList.size());
                    TranObject<List<User>> refreshO = new TranObject<List<User>>(
                            TranObjectType.REFRESH);
                    refreshO.setObject(refreshList);
                    out.setMessage(refreshO);
                    break;

                case OFFLINEMESSAGE:

                    System.out.println("客户端要获取离线消息了");

                    ArrayList<OffLineMessage> offLineMessage = dao.getOffLineMessage(read_tranObject.getFromUser());

                    TranObject<List<OffLineMessage>> offMsgObject = new TranObject<List<OffLineMessage>>(
                            TranObjectType.OFFLINEMESSAGE);

                    offMsgObject.setObject(offLineMessage);
                    offMsgObject.setFromUser(read_tranObject.getFromUser());

                    out.setMessage(offMsgObject);

                    break;

                default:
                    break;
            }
        }


    }
    /**
     * 判断是否断开连接,连接返回true,断开返回false
     * @return
     */
    public Boolean isServerConnetion( Socket socket){

        try{
            socket.sendUrgentData(0xFF);//发送1个字节的紧急数据,默认情况下,服务器端没有开启紧急数据处理,不影响正常通信
            return true;
        }catch(Exception se){

            return false;
        }
    }
}
package loaderman.server;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.net.SocketException;

import loaderman.im.tran.TranObject;

/**
 * 写消息线程
 *
 */
public class OutputThread extends Thread {
    private OutputThreadMap map;
    private ObjectOutputStream oos;
    private TranObject object;
    private boolean isStart = true;// 循环标志位
    private Socket socket;

    public OutputThread(Socket socket, OutputThreadMap map) {
        try {
            this.socket = socket;
            this.map = map;
            oos = new ObjectOutputStream(socket.getOutputStream());// 在构造器里面实例化对象输出流
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Socket getSocket() {
        return socket;
    }

    public void setStart(boolean isStart) {
        this.isStart = isStart;
    }

    // 调用写消息线程,设置了消息之后,唤醒run方法,可以节约资源
    public void setMessage(TranObject object) {
        this.object = object;
        synchronized (this) {
            System.out.println("OutputThread唤醒");
            notify();
        }
    }

    @Override
    public void run() {
        try {
            while (isStart) {

                if (object != null) {
                    System.out.println("OutputThread写出开始");
                    oos.writeObject(object);
                    System.out.println("OutputThread写出中");
                    oos.flush();
                    System.out.println("OutputThread写出结束");
                }
                // 没有消息写出的时候,线程等待
                synchronized (this) {
                    System.out.println("OutputThread等待");
                    wait();
                }

            }
            if (oos != null){// 循环结束后,关闭流,释放资源
                oos.close();
            }
            if (socket != null) {
                socket.close();
            }

        } catch (InterruptedException e) {
            System.out.println("OutputThread1" + e.toString());

            e.printStackTrace();
        } catch (SocketException e) {
            System.out.println("OutputThread2" + e.toString());

        } catch (IOException e) {
            System.out.println("OutputThread3" + e.toString());
            e.printStackTrace();
        }
    }
}
package loaderman.server;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 存放写线程的缓存器

 */
public class OutputThreadMap {
    private HashMap<String, OutputThread> map;
    private static OutputThreadMap instance;

    // 私有构造器,防止被外面实例化改对像
    private OutputThreadMap() {
        map = new HashMap<String, OutputThread>();
    }

    // 单例模式像外面提供该对象
    public synchronized static OutputThreadMap getInstance() {
        if (instance == null) {
            instance = new OutputThreadMap();
        }
        return instance;
    }

    // 添加写线程的方法
    public synchronized void add(String id, OutputThread out) {
        OutputThread outputThread = map.get(id);
        System.out.println(id + "之前线程 --> " + outputThread);
        System.out.println(id + "新线程 --> " + out);

        if (outputThread!=null){
            outputThread.setMessage(null);
            outputThread.setStart(false);
        }
        map.put(id, out);
        OutputThread outputThread2 = map.get(id);
        System.out.println(id + "现在运行新线程 --> " + outputThread2);
    }

    // 移除写线程的方法
    public synchronized void remove(String id) {
        map.remove(id);



    }

    // 取出写线程的方法,群聊的话,可以遍历取出对应写线程
    public synchronized OutputThread getById(String id) {
        return map.get(id);
    }

    // 得到所有写线程方法,用于向所有在线用户发送广播
    public synchronized List<OutputThread> getAll() {
        List<OutputThread> list = new ArrayList<OutputThread>();
        for (Map.Entry<String, OutputThread> entry : map.entrySet()) {
            list.add(entry.getValue());
        }
        return list;
    }
}
package loaderman.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import loaderman.im.util.Constants;
import loaderman.im.util.MyDate;

/**
 * 服务器,接受用户登录、离线、转发消息
 * 

 */
public class Server {
    private ExecutorService executorService;// 线程池
    private ServerSocket serverSocket = null;
    private Socket socket = null;
    private boolean isStarted = true;

    public Server() {
        try {
            // 创建线程池,池中具有(cpu个数*50)条线程
            executorService = Executors.newFixedThreadPool(Runtime.getRuntime()
                    .availableProcessors() * 50);
            serverSocket = new ServerSocket(Constants.SERVER_PORT);
        } catch (IOException e) {
            e.printStackTrace();
            quit();
        }
    }

    public void start() {
        try {
            while (isStarted) {
                System.out.println(MyDate.getDateCN() + " 服务器已启动...");
                socket = serverSocket.accept();

                System.out.println("客户端已经连接");
                String ip = socket.getInetAddress().toString();
                System.out.println(MyDate.getDateCN() + " 用户:" + ip + " 已建立连接");
                // 为支持多用户并发访问,采用线程池管理每一个用户的连接请求
                if (socket.isConnected())
                    executorService.execute(new SocketTask(socket));// 添加到线程池
            }
            if (socket != null)
                socket.close();
            if (serverSocket != null)
                serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private final class SocketTask implements Runnable {
        private Socket socket = null;
        private InputThread in;
        private OutputThread out;
        private OutputThreadMap map;

        public SocketTask(Socket socket) {
            this.socket = socket;
            map = OutputThreadMap.getInstance();
        }

        @Override
        public void run() {
            out = new OutputThread(socket, map);//
            // 先实例化写消息线程,(把对应用户的写线程存入map缓存器中)
            in = new InputThread(socket, out, map);// 再实例化读消息线程
            out.setStart(true);
            in.setStart(true);
            in.start();
            out.start();
        }
    }

    /**
     * 退出
     */
    public void quit() {
        System.out.println("退出");


        try {
            this.isStarted = false;
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new Server().start();

    }
}

完成

posted on 2018-12-19 17:15  LoaderMan  阅读(654)  评论(0编辑  收藏  举报

导航