Java之Socket简单聊天实现(QQ续二)

转载请注明出处,谢谢!

今天跟大家分享一下我那QQ小项目中服务器与客户端的核心代码,并谈谈一些我的建议和看法,希望大家多多支持,你们的支持,就是我继续分享的动力,哈哈!

一、服务器,好了,废话不多说,我们先来看看服务器部分,我这里用到线程池,至于为什么用线程池,不知道的童鞋可以去我的另一篇blog看看:。当一个用户连接上之后,我们马上将该用户的socket丢入已经建好的线程池中去处理,这样可以很快腾出时间来接受下一个用户的连接,而线程池中的这个线程又分支为两个线程,一个是读消息线程,一个是写消息线程,当然,因为我这个聊天是用来转发消息的,所以还以单例模式建了一个Map用来存放每个用户的写消息线程(如果用户多的话,这是相当消耗资源的),以便在转发消息的时候,通过Map的key就可以取出对应用户的写消息线程,从而达到转发消息的目的。具体下面再说

/** * 服务器,接受用户登录、离线、转发消息 * * @author way * */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() {System.out.println(MyDate.getDateCN() + " 服务器已启动…");try {while (isStarted) {socket = serverSocket.accept();String ip = socket.getInetAddress().toString();System.out.println(MyDate.getDateCN() + " 用户:" + ip + " 已建立连接");// 为支持多用户并发访问,采用线程池管理每一个用户的连接请求if (socket.isConnected())executorService.execute(new SocketTask(socket));// 添加到线程池}if (socket != null)//循环结束后,记得关闭socket,释放资源socket.close();if (serverSocket != null)serverSocket.close();} catch (IOException e) {e.printStackTrace();// isStarted = false;}}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();}@Overridepublic 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() {try {this.isStarted = false;serverSocket.close();} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {new Server().start();}}

二、服务器写消息线程,接下来,我们来看看写消息线程,很简单的一段代码,有注释,我就不多说了:

/** * 写消息线程 * * @author way * */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 void setStart(boolean isStart) {//用于外部关闭写线程this.isStart = isStart;}// 调用写消息线程,设置了消息之后,唤醒run方法,可以节约资源public void setMessage(TranObject object) {this.object = object;synchronized (this) {notify();}}@Overridepublic void run() {try {while (isStart) {// 没有消息写出的时候,线程等待synchronized (this) {wait();}if (object != null) {oos.writeObject(object);oos.flush();}}if (oos != null)// 循环结束后,关闭流,释放资源oos.close();if (socket != null)socket.close();} catch (InterruptedException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}}

三、服务器写消息线程缓存器,接下来让我们看一下那个写消息线程缓存器的庐山真面目:

/** * 存放写线程的缓存器 * * @author way */public class OutputThreadMap {private HashMap<Integer, OutputThread> map;private static OutputThreadMap instance;// 私有构造器,防止被外面实例化改对像private OutputThreadMap() {map = new HashMap<Integer, OutputThread>();}// 单例模式像外面提供该对象public synchronized static OutputThreadMap getInstance() {if (instance == null) {instance = new OutputThreadMap();}return instance;}// 添加写线程的方法public synchronized void add(Integer id, OutputThread out) {map.put(id, out);}// 移除写线程的方法public synchronized void remove(Integer id) {map.remove(id);}// 取出写线程的方法,群聊的话,可以遍历取出对应写线程public synchronized OutputThread getById(Integer id) {return map.get(id);}// 得到所有写线程方法,用于向所有在线用户发送广播public synchronized List<OutputThread> getAll() {List<OutputThread> list = new ArrayList<OutputThread>();for (Map.Entry<Integer, OutputThread> entry : map.entrySet()) {list.add(entry.getValue());}return list;}},再回头,便生出无限羁绊。那是彼此的刺在对方心里留下的痕迹,

Java之Socket简单聊天实现(QQ续二)

相关文章:

你感兴趣的文章:

标签云: