JAVA 即时网络通信我的服务器
JAVA 即时网络通信我的服务器
以前上Java课的时候,老师要求,自行组队来做一个即时网络通信的课程设计。具体要求:使用Socket套接字和ServerSocket来开发一个基于c/s架构的小项目,服务器和客户端的UI采用Swing编程,具体业务逻辑采用多线程开发。现在过去这么久了,想去回忆一下,记录一下当时的点滴,作为一点点积累。我正在努力回忆..
我主要负责,服务器的设计开发,下面是我的部分代码。
一,UI部分是模仿别人写的,可自行设计。
二,业务部分(多线程处理)
1.线程管理类
package com.haoyudian.server.service; import java.io.IOException; import java.net.Inet4Address; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import com.haoyudian.common.util.Constants; import com.haoyudian.common.util.DateHelper; /** * 服务器管理类 接受用户登录、离线、转发消息 * * @author Scherrer * */ public class ServerManager { private ExecutorService executorService; // 线程池 private ServerSocket serverSocket = null ; private Socket socket; private Boolean isStart = true ; public ServerManager() { try { // 创建线程池,池中具有(cpu个数*50)条线程 executorService = Executors.newFixedThreadPool(Runtime.getRuntime() .availableProcessors() * 50 ); serverSocket = new ServerSocket(Constants.SERVER_PORT); System.out.println( "服务器IP=" + Inet4Address.getLocalHost().getHostAddress()); } catch (Exception e) { e.printStackTrace(); exit(); } } /** * 退出方法 */ private void exit() { try { this .isStart = false ; serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } public void start() { System.out.println(DateHelper.getDateByCN() + " 服务器已启动..." ); try { while (isStart) { socket = serverSocket.accept(); String ip = socket.getRemoteSocketAddress().toString(); System.out.println(DateHelper.getDateByCN() + " 用户:" + ip + " 已建立连接" ); // 为支持多用户并发访问,采用线程池管理每一个用户的连接请求 if (socket.isConnected()) { executorService.execute( new SocketTask(socket)); // 添加到线程池 } } } catch (IOException e) { e.printStackTrace(); } finally { try { 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 inThread; private OutputThread outThread; private OutputThreadMap threadMap; public SocketTask(Socket socket) { this .socket = socket; threadMap = OutputThreadMap.getInstance(); } @Override public void run() { outThread = new OutputThread(socket); // // 先实例化写消息线程,(把对应用户的写线程存入map缓存器中) inThread = new InputThread(socket, outThread, threadMap); // 再实例化读消息线程 outThread.setStart( true ); inThread.setStart( true ); inThread.start(); outThread.start(); } } public static void main(String[] args) { new ServerManager().start(); } }
2.发送消息的线程
package com.haoyudian.server.service; import java.io.IOException; import java.io.ObjectOutputStream; import java.net.Socket; import com.haoyudian.common.bean.trans.TransObject; /** * 发送消息的线程 * * @author Scherrer * */ public class OutputThread extends Thread{ private ObjectOutputStream oos; // 对象输出流 private TransObject<?> object; // 传输对象 private boolean isStart = true ; // 循环标志 private Socket socket; // 套接字 // private OutputThreadMap outMap; // 发送现场缓存对象 public OutputThread(Socket s) { try { this .socket = s; // 构造器里实例化对象输出流 oos = new ObjectOutputStream( this .socket.getOutputStream()); } catch (IOException e) { e.printStackTrace(); } } /** * 调用写消息线程,设置了消息之后,唤醒run方法,可以节约资源 * @param object */ public void setMessage(TransObject<?> object) { this .object = object; synchronized ( this ) { notify(); } } public void setStart( boolean isStart) { this .isStart = isStart; } @Override public void run() { try { while (isStart) { // 没有消息写的时候,线程等待 synchronized ( this ) { wait(); } if (object != null ) { oos.writeObject(object); oos.flush(); } } } catch (Exception e) { e.printStackTrace(); } finally { try { if (oos != null ) { oos.close(); } if (socket != null ) { socket.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
3.接受消息的线程
package com.haoyudian.server.service; import java.io.IOException; import java.io.ObjectInputStream; import java.net.Socket; import java.util.List; import com.haoyudian.common.bean.TextMessage; import com.haoyudian.common.bean.User; import com.haoyudian.common.bean.trans.TransObject; import com.haoyudian.common.bean.trans.TransObjectType; import com.haoyudian.common.util.DateHelper; import com.haoyudian.server.dao.UserDao; import com.haoyudian.server.dao.UserDaoFactory; public class InputThread extends Thread{ private ObjectInputStream ois; // 对象读入流 private Socket socket; // socket对象 private OutputThread outThread; // 把接收的消息发送给用户 private OutputThreadMap map; // 写消息的缓存器 private boolean isStart = true ; public InputThread(Socket socket,OutputThread out,OutputThreadMap map) { try { this .socket = socket; this .outThread = out; this .map = map; this .ois = new ObjectInputStream(socket.getInputStream()); } catch (Exception e) { e.printStackTrace(); } } public void setStart( boolean isStart) { // 提供接口给外部关闭读消息线程 this .isStart = isStart; } @Override public void run() { try { while (isStart) { // 读消息 try { readMessage(); } catch (Exception e) { // e.printStackTrace(); break ; } } } catch (Exception e) { e.printStackTrace(); } finally { try { if (ois != null ) { ois.close(); } if (socket != null ) { socket.close(); } } catch (IOException e) { e.printStackTrace(); } } } private void readMessage() throws IOException, ClassNotFoundException { Object readObj = ois.readObject(); // 读取消息对象 UserDao dao = UserDaoFactory.getInstance(); // 通过dao模式管理后台 if (readObj != null && readObj instanceof TransObject) { TransObject <?> transObj = (TransObject<?>) readObj; // 转换成传输对象 switch (transObj.getType()) { case REGISTER: // 注册 User user = (User) transObj.getObject(); int regAccout = dao.register(user); System.out.println(DateHelper.getDateByCN() + "新用户注册: " + regAccout); // 回复用户 TransObject<User> regResult = new TransObject <User> (TransObjectType.REGISTER); User u = new User(); u.setAccount(regAccout); regResult.setObject(u); System.out.println( "验证一下账号: " + u.getAccount()); outThread.setMessage(regResult); break ; case LOGIN: User loginUser = (User) transObj.getObject(); List <User> list = dao.login(loginUser); System.out.println( "好友列表: " + list.size()); // 返回 list TransObject<List<User>> msg = new TransObject<> (TransObjectType.LOGIN); if (list != null ) { // 登陆成功 TransObject<User> userOnlineMsg = new TransObject <User> (TransObjectType.LOGIN); // 此处 new 一个User ,只广播用户账号,如果用loginUser,则可能泄露密码 User tempUser = new User(); tempUser.setAccount(loginUser.getAccount()); // tempUser.setNickname(nickname) // 考虑广播昵称 userOnlineMsg.setObject(tempUser); for (OutputThread out : map.getAll()) { // 拿到所有用户的发送线程 out.setMessage(userOnlineMsg); } // 记录当前用户的发送线程 map.add(loginUser.getAccount(), outThread); // 设置消息 msg.setObject(list); } // 发送 outThread.setMessage(msg); System.out.println(DateHelper.getDateByCN() + " 用户:" + loginUser.getAccount() + " 上线了" ); break ; case MESSAGE: // 如果是转发消息(可添加群发) // 获取消息中要转发的对象id,然后获取缓存的该对象的写线程 int toAccount = transObj.getToUser().getAccount(); // 获取账号 OutputThread ot = map.getById(toAccount); // 获取发送线程 if (ot != null ) { ot.setMessage(transObj); // 把接收到的消息对象直接发送给另一个用户 } else { // 用户的缓存发送线程为空,表示表示用户已下线,回复用户 TextMessage text = new TextMessage(); text.setTextMessage( "对方离线,您的消息暂时保存在服务器" ); TransObject <TextMessage> msgTip = new TransObject<> (TransObjectType.MESSAGE); msgTip.setObject(text); User tempU = new User(); tempU.setAccount( 0 ); msgTip.setFromUser(tempU); outThread.setMessage(msgTip); } break ; case LOGOUT: // 下线处理 // 如果是退出,更新数据库在线状态,同时群发告诉所有在线用户 User logoutUser = (User) transObj.getObject(); System.out.println(DateHelper.getDateByCN() + "用户: " + logoutUser.getNickname() + "下线了哈" ); dao.logout(logoutUser); // 结束自己的读消息的线程 isStart = false ; // 移除自己的缓存线程 map.remove(logoutUser.getAccount()); outThread.setMessage( null ); // 先要设置一个空消息去唤醒写线程 outThread.setStart( false ); // 再结束写线程循环 TransObject <User> offObject = new TransObject<User> ( TransObjectType.LOGOUT); User logout2User = new User(); logout2User.setAccount(logoutUser.getAccount()); offObject.setObject(logout2User); for (OutputThread offOut : map.getAll()) { // 广播用户下线消息 offOut.setMessage(offObject); } break ; case Refresh_FRIEND_LIST: // 更新好友 List<User> refreshList = dao.refreshFriendList(transObj.getFromUser().getAccount()); TransObject <List<User>> refreshMsg = new TransObject<> (TransObjectType.Refresh_FRIEND_LIST); refreshMsg.setObject(refreshList); outThread.setMessage(refreshMsg); break ; } } } }
其中,线程池那部分的使用,我觉得很有用,我要特别留意一下
public ServerManager() { try { // 创建线程池,池中具有(cpu个数*50)条线程 executorService = Executors.newFixedThreadPool(Runtime.getRuntime() .availableProcessors() * 50 ); serverSocket = new ServerSocket(Constants.SERVER_PORT); System.out.println( "服务器IP=" + Inet4Address.getLocalHost().getHostAddress()); } catch (Exception e) { e.printStackTrace(); exit(); } } try { while (isStart) { socket = serverSocket.accept(); String ip = socket.getRemoteSocketAddress().toString(); System.out.println(DateHelper.getDateByCN() + " 用户:" + ip + " 已建立连接" ); // 为支持多用户并发访问,采用线程池管理每一个用户的连接请求 if (socket.isConnected()) { executorService.execute( new SocketTask(socket)); // 添加到线程池 } } }
分类: Java 笔记
标签: Java Swing Socket
作者: Leo_wl
出处: http://www.cnblogs.com/Leo_wl/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
版权信息查看更多关于JAVA 即时网络通信我的服务器的详细内容...
声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did46726