写在前面:
昨天在博客记录自己抽空写的一个的初始设计,那是这个程序的整体设计,为了完整性,今天把服务端的设计细化记录一下,首页贴出 Socket 聊天程序的服务端大体设计图,如下图:
功能说明:
服务端主要有两个操作,一是阻塞接收客户端的 socket 并做响应处理,二是检测客户端的心跳,如果客户端一段时间内没有发送心跳则移除该客户端,由 Server 创建 ServerSocket,然后启动两个线程池去处理这两件事(newFixedThreadPool,newScheduledThreadPool),对应的处理类分别是 SocketDispatcher、SocketSchedule,其中 SocketDispatcher 根据 socket 不同的请求分发给不同 SocketHandler 去处理,而 SocketWrapper 则是对 socket 加了一层外壳包装,用 lastAliveTime 记录 socket 最新的交互时间,SocketHolder 存储当前跟服务端交互的 socket 集合。
具体实现:
[Server.java]
Server 是服务端的入口,由 Server 的 start() 方法启动 ServerSocket,然后阻塞接收客户端的请求,交由 SocketDispatcher 去分发,SocketDispatcher 由 newFixedThread 类型的线程池启动,当连接数超过最大数据时将被队列处理,使用 scheduleAtFixedRate 启动 SocketSchedule 定时循环去监听客户端的心跳包,这两个类型都实现了 Runnable 接口,下面给出服务端的代码:
- 1 package yaolin.chat.server;
- 2 3 import java.io.IOException;
- 4 import java.net.ServerSocket;
- 5 import java.util.Date;
- 6 import java.util.concurrent.ExecutorService;
- 7 import java.util.concurrent.Executors;
- 8 import java.util.concurrent.ScheduledExecutorService;
- 9 import java.util.concurrent.TimeUnit;
- 10 11 import yaolin.chat.common.ConstantValue;
- 12 import yaolin.chat.util.LoggerUtil;
- 13 14
- /**
- 15 * 服务器
- 16 * @author yaolin
- 17 */
- 18 public class Server {
- 19 20 private final ServerSocket server;
- 21 private final ExecutorService pool;
- 22 23 public Server() throws IOException {
- 24 server = new ServerSocket(ConstantValue.SERVER_PORT);
- 25 pool = Executors.newFixedThreadPool(ConstantValue.MAX_POOL_SIZE);
- 26
- }
- 27 28 public void start() {
- 29
- try {
- 30 ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1);
- 31 // Watch dog. Exception??
- 32 schedule.scheduleAtFixedRate(new SocketSchedule(), 10, ConstantValue.TIME_OUT, TimeUnit.SECONDS);
- 33 34
- while (true) {
- 35 pool.execute(new SocketDispatcher(server.accept()));
- 36 LoggerUtil.info("ACCEPT A CLIENT AT " + new Date());
- 37
- }
- 38
- } catch(IOException e) {
- 39 pool.shutdown();
- 40
- }
- 41
- }
- 42 43 44 public static void main(String[] args) {
- 45
- try {
- 46 new Server().start();
- 47
- } catch(IOException e) {
- 48 LoggerUtil.error("Server start failed! -> " + e.getMessage(), e);
- 49
- }
- 50
- }
- 51
- }
[SocketDispatcher.java]
Server 只是服务端的入口,并指挥中心,SocketDispatcher 才是服务端的指挥中心,对客户端不同的消息类型请求进行分发,让不同的 SocketHandler 去处理对应的消息请求,这里服务端和客户端的消息交互都是用 JSON 数据,所有消息类都继承 BaseMessage,所以将接收到数据转换成 BaseMessage 类型,再判断其类型,(数据类型模块属于 common 模块),这里需要提一下的是当消息类型是文件类型的时候会睡眠配置执行的间隔时间,这样 FileHandler 才能有时间对文件流进行读取和重新发送给指定的客户端,而不会立即进入下一次循环对消息类型的判断(可能这里设计有点问题,不过暂时先这样做),下面给出 SocketDispatcher 的代码:
- 1
- /**
- 2 * SocketDispatcher
- 3 *
- 4 * @author yaolin
- 5 */
- 6 public class SocketDispatcher implements Runnable {
- 7 8 private final Socket socket;
- 9 10 public SocketDispatcher(Socket socket) {
- 11 this.socket = socket;
- 12
- }
- 13 14@Override 15 public void run() {
- 16
- if (socket != null) {
- 17
- while (!socket.isClosed()) {
- 18
- try {
- 19 InputStream is = socket.getInputStream();
- 20 String line = null;
- 21 StringBuffer sb = null;
- 22 23
- if (is.available() > 0) {
- 24 25 BufferedReader bufr = new BufferedReader(new InputStreamReader(is));
- 26 sb = new StringBuffer();
- 27
- while (is.available() > 0 && (line = bufr.readLine()) != null) {
- 28 sb.append(line);
- 29
- }
- 30 LoggerUtil.trach("RECEIVE [" + sb.toString() + "] AT " + new Date());
- 31 32 BaseMessage message = JSON.parseObject(sb.toString(), BaseMessage.class);
- 33 34
- switch (message.getType()) {
- 35
- case MessageType.ALIVE:
- 36 HandlerFactory.getHandler(MessageType.ALIVE).handle(socket, sb.toString());
- 37
- break;
- 38
- case MessageType.CHAT:
- 39 HandlerFactory.getHandler(MessageType.CHAT).handle(socket, sb.toString());
- 40
- break;
- 41
- case MessageType.FILE:
- 42 HandlerFactory.getHandler(MessageType.FILE).handle(socket, sb.toString());
- 43 LoggerUtil.trach("SEVER:PAUSE TO RECEIVE FILE");
- 44 Thread.sleep(ConstantValue.MESSAGE_PERIOD);
- 45
- break;
- 46
- case MessageType.LOGIN:
- 47 HandlerFactory.getHandler(MessageType.LOGIN).handle(socket, sb.toString());
- 48
- break;
- 49
- case MessageType.LOGOUT:
- 50
- break;
- 51
- case MessageType.REGISTER:
- 52 HandlerFactory.getHandler(MessageType.REGISTER).handle(socket, sb.toString());
- 53
- break;
- 54
- }
- 55
- } else {
- 56 Thread.sleep(ConstantValue.MESSAGE_PERIOD);
- 57
- }
- 58
- } catch(Exception e) { // catch all handler exception
- 59 LoggerUtil.error("SocketDispatcher Error!" + e.getMessage(), e);
- 60
- }
- 61
- }
- 62
- }
- 63
- }
- 64
- }
[SocketSchedule.java]
跟 Server 有直接关系的另一个类(组件)是 SocketSchedule,SocketSchedule 主要负责检测客户端的最新一次跟服务端的交互时间是否超过系统配置允许最大的时间,如果超过了,则将该客户端 socket 从服务端移除,否则更新客户端的最新一次跟服务端的交互时间。下面是具体的实现:
- 1
- /**
- 2 * Remove socket from SocketHolder if lastAliveTime > TIME_OUT
- 3 * @author yaolin
- 4 *
- 5 */
- 6 public class SocketSchedule implements Runnable {
- 7 8@Override 9 public void run() {
- 10
- for (String key: SocketHolder.keySet()) {
- 11 SocketWrapper wrapper = SocketHolder.get(key);
- 12
- if (wrapper != null && wrapper.getLastAliveTime() != null) {
- 13
- if (((new Date().getTime() - wrapper.getLastAliveTime().getTime()) / 1000) > ConstantValue.TIME_OUT) {
- 14 // remove socket if timeout
- 15 SocketHolder.remove(key);
- 16
- }
- 17
- }
- 18
- }
- 19
- }
- 20
- }
[SocketHolder.java、SocketWrapper.java]
从上面的代码可以看出,SocketSchedule#run() 只是简单的对时间进行一次判断,真正有意义的其实是 SocketHolder 和 SocketWrapper,SocketWrapper 则是对 socket 加了一层外壳包装,SocketHolder 的存储了当前有效时间内所有跟服务端有交互的客户端,SocketHolder 以客户端的唯一标识(这里使用用户名),作为 KEY,客户端所在的 socket 作为 VALUE 的键值对形式存储,其中 SocketHolder#flushClientStatus() 的处理逻辑是用于通知其他客户端当前客户端的上线 / 离线状态,下面给出这两个类的具体实现:
- 1
- /**
- 2 * Wrap Socket, SocketSchedule remove socket if lastAliveTime > TIME_OUT
- 3 * @author yaolin
- 4 *
- 5 */
- 6 public class SocketWrapper {
- 7 8 private Socket socket;
- 9 private Date lastAliveTime;
- 10 11 // full constructor
- 12 public SocketWrapper(Socket socket, Date lastAliveTime) {
- 13 this.socket = socket;
- 14 this.lastAliveTime = lastAliveTime;
- 15
- }
- 16 public Socket getSocket() {
- 17
- return socket;
- 18
- }
- 19 public void setSocket(Socket socket) {
- 20 this.socket = socket;
- 21
- }
- 22 public Date getLastAliveTime() {
- 23
- return lastAliveTime;
- 24
- }
- 25 public void setLastAliveTime(Date lastAliveTime) {
- 26 this.lastAliveTime = lastAliveTime;
- 27
- }
- 28
- }
- 1
- /**
- 2 * SocketHolder
- 3 * @author yaolin
- 4 */
- 5 public class SocketHolder {
- 6 7 private static ConcurrentMap listSocketWrap = new ConcurrentHashMap();
- 8 9 public static Set keySet() {
- 10
- return listSocketWrap.keySet();
- 11
- }
- 12 13 public static SocketWrapper get(String key) {
- 14
- return listSocketWrap.get(key);
- 15
- }
- 16 17 public static void put(String key, SocketWrapper value) {
- 18 listSocketWrap.put(key, value);
- 19 flushClientStatus(key, true);
- 20
- }
- 21 22 public static SocketWrapper remove(String key) {
- 23 flushClientStatus(key, false);
- 24
- return listSocketWrap.remove(key);
- 25
- }
- 26 27 public static void clear() {
- 28 listSocketWrap.clear();
- 29
- }
- 30 31
- /**
- 32 * <pre>content:{username:"",flag:false}</pre>
- 33 * @param flag true:put,false:remove;
- 34 */
- 35 private static void flushClientStatus(String key, boolean flag) {
- 36 ClientNotifyDTO dto = new ClientNotifyDTO(flag, key);
- 37 ReturnMessage rm = new ReturnMessage().setKey(Key.NOTIFY).setSuccess(true).setContent(dto);
- 38 rm.setFrom(ConstantValue.SERVER_NAME);
- 39
- for (String toKey: listSocketWrap.keySet()) {
- 40
- if (!toKey.equals(key)) { // not send to self
- 41 rm.setTo(toKey);
- 42 SocketWrapper wrap = listSocketWrap.get(toKey);
- 43
- if (wrap != null) {
- 44 SendHelper.send(wrap.getSocket(), rm);
- 45
- }
- 46
- }
- 47
- }
- 48
- }
- 49
- }
[SocketHandler.java、HandlerFactory.java、OtherHandlerImpl.java]
SocketDispatcher 让不同的 SocketHandler 去处理对应的消息请求,SocketHandler 的设计其实就是一套简单的工厂组件吧(其中 ReturnHandler 暂时由 SendHelper 实现信息传送,暂时没有用到,已经 @Deprecated ,这里还是给出),完整类图如下:
下面给出这一块的代码,为了缩小篇幅,将所有 Handler 实现的代码收起来。
- 1
- /**
- 2 * SocketHandler
- 3 * @author yaolin
- 4 */
- 5 public interface SocketHandler {
- 6
- /**
- 7 * Handle Client Socket
- 8 */
- 9 public Object handle(Socket client, Object data);
- 10
- }
- 1
- /**
- 2 * SocketHandlerFactory
- 3 * @author yaolin
- 4 */
- 5 public class HandlerFactory {
- 6 7 // can not create instance
- 8 private HandlerFactory() {}
- 9 10 public static SocketHandler getHandler(int type) {
- 11
- switch (type) {
- 12
- case MessageType.ALIVE:
- // usually use
- 13
- return new AliveHandler();
- 14
- case MessageType.CHAT:
- 15
- return new ChatHandler();
- 16
- case MessageType.LOGIN:
- 17
- return new LoginHandler();
- 18 // case MessageType.RETURN:
- 19 // return new ReturnHandler();
- 20
- case MessageType.LOGOUT:
- 21
- return new LogoutHandler();
- 22
- case MessageType.REGISTER:
- 23
- return new RegisterHandler();
- 24
- case MessageType.FILE:
- 25
- return new FileHandler();
- 26
- }
- 27
- return null; // NullPointException
- 28
- }
- 29
- }
View Code
- 1
- /**
- 2 * AliveSocketHandler
- 3 * @author yaolin
- 4 */
- 5 public class AliveHandler implements SocketHandler {
- 6 7
- /**
- 8 * @return null
- 9 */
- 10@Override 11 public Object handle(Socket client, Object data) {
- 12
- if (data != null) {
- 13 BaseMessage message = JSON.parseObject(data.toString(), BaseMessage.class);
- 14
- if (StringUtil.isNotEmpty(message.getFrom())) {
- 15 SocketWrapper wrapper = SocketHolder.get(message.getFrom());
- 16
- if (wrapper != null) {
- 17 wrapper.setLastAliveTime(new Date()); // KEEP SOCKET ...
- 18 SocketHolder.put(message.getFrom(), wrapper);
- 19
- }
- 20
- }
- 21
- }
- 22
- return null;
- 23
- }
- 24 25
- }
View Code
- 1
- /**
- 2 * ChatHandler
- 3 *
- 4 * @author yaolin
- 5 */
- 6 public class ChatHandler implements SocketHandler {
- 7 8@Override 9 public Object handle(Socket client, Object data) {
- 10
- if (data != null) {
- 11 ChatMessage message = JSON.parseObject(data.toString(), ChatMessage.class);
- 12 13
- if (StringUtil.isNotEmpty(message.getFrom()) && StringUtil.isNotEmpty(message.getTo())) {
- 14 // exist & send
- 15
- if (SocketHolder.keySet().contains(message.getFrom())) {
- 16 String owner = message.getFrom();
- 17 message.setOwner(owner); // owner will be display
- 18
- if (ConstantValue.TO_ALL.equals(message.getTo())) { // one-to-all
- 19 // TO_ALL TAB will be select;
- 20 message.setFrom(ConstantValue.TO_ALL);
- 21
- for (String key: SocketHolder.keySet()) {
- 22 // also send to self
- 23 SocketWrapper wrapper = SocketHolder.get(key);
- 24
- if (wrapper != null) {
- 25 SendHelper.send(wrapper.getSocket(), message);
- 26
- }
- 27
- }
- 28
- } else { // one-to-one
- 29 SocketWrapper wrapper = SocketHolder.get(message.getTo());
- 30
- if (wrapper != null) {
- 31 // owner = from
- 32 SendHelper.send(wrapper.getSocket(), message);
- 33 // also send to self
- 34 // TO TAB will be select;
- 35 message.setFrom(message.getTo()).setTo(owner);
- 36 SendHelper.send(client, message);
- 37
- }
- 38
- }
- 39
- }
- 40
- }
- 41
- }
- 42
- return null;
- 43
- }
- 44
- }
View Code
- 1 public class FileHandler implements SocketHandler {
- 2 3@Override 4 public Object handle(Socket client, Object data) {
- 5
- if (client != null) {
- 6 FileMessage message = JSON.parseObject(data.toString(), FileMessage.class);
- 7
- if (StringUtil.isNotEmpty(message.getFrom()) && StringUtil.isNotEmpty(message.getTo())) {
- 8 // exist & send
- 9
- if (SocketHolder.keySet().contains(message.getFrom())) {
- 10
- if (!ConstantValue.TO_ALL.equals(message.getTo())) { // one-to-all
- 11 SocketWrapper wrapper = SocketHolder.get(message.getTo());
- 12
- if (wrapper != null) {
- 13 SendHelper.send(wrapper.getSocket(), message);
- 14
- try {
- 15
- if (client != null && wrapper.getSocket() != null && message.getSize() > 0) {
- 16 InputStream is = client.getInputStream();
- 17 OutputStream os = wrapper.getSocket().getOutputStream();
- 18 int total = 0;
- 19
- while (!client.isClosed() && !wrapper.getSocket().isClosed()) {
- 20
- if (is.available() > 0) {
- 21 byte[] buff = new byte[ConstantValue.BUFF_SIZE];
- 22 int len = -1;
- 23
- while (is.available() > 0 && (len = is.read(buff)) != -1) {
- 24 os.write(buff, 0, len);
- 25 total += len;
- 26 LoggerUtil.debug("SEND BUFF [" + len + "]");
- 27
- }
- 28 os.flush();
- 29
- if (total >= message.getSize()) {
- 30 LoggerUtil.info("SEND BUFF [OK]");
- 31
- break;
- 32
- }
- 33
- }
- 34
- }
- 35 // AFTER SEND FILE
- 36 // SEND SUCCESSFULLY
- 37 ReturnMessage result = new ReturnMessage().setKey(Key.TIP) 38.setSuccess(true) 39.setContent(I18N.INFO_FILE_SEND_SUCCESSFULLY);
- 40 result.setFrom(message.getTo()).setTo(message.getFrom()) 41.setOwner(ConstantValue.SERVER_NAME);
- 42 SendHelper.send(client, result);
- 43 // RECEIVE SUCCESSFULLY
- 44 result.setContent(I18N.INFO_FILE_RECEIVE_SUCCESSFULLY) 45.setFrom(message.getFrom()) 46.setTo(message.getTo());
- 47 SendHelper.send(wrapper.getSocket(), result);
- 48
- }
- 49
- } catch(Exception e) {
- 50 LoggerUtil.error("Handle file failed !" + e.getMessage(), e);
- 51
- }
- 52
- }
- 53
- }
- 54
- }
- 55
- }
- 56
- }
- 57
- return null;
- 58
- }
- 59
- }
View Code
- 1
- /**
- 2 * LoginHandler
- 3 *
- 4 * @author yaolin
- 5 *
- 6 */
- 7 public class LoginHandler implements SocketHandler {
- 8 9 private UsrService usrService = new UsrService();
- 10 11@Override 12 public Object handle(Socket client, Object data) {
- 13 ReturnMessage result = new ReturnMessage();
- 14 result.setSuccess(false);
- 15
- if (data != null) {
- 16 LoginMessage message = JSON.parseObject(data.toString(), LoginMessage.class);
- 17
- if (StringUtil.isNotEmpty(message.getUsername()) && StringUtil.isNotEmpty(message.getPassword())) {
- 18
- if (usrService.login(message.getUsername(), message.getPassword()) != null) {
- 19 result.setSuccess(true);
- 20
- } else {
- 21 result.setMessage(I18N.INFO_LOGIN_ERROR_DATA);
- 22
- }
- 23 result.setFrom(ConstantValue.SERVER_NAME).setTo(message.getUsername());
- 24
- } else {
- 25 result.setMessage(I18N.INFO_LOGIN_EMPTY_DATA);
- 26
- }
- 27 // AFTER LOGIN
- 28 result.setKey(Key.LOGIN);
- 29
- if (result.isSuccess()) { // HOLD SOCKET
- 30 SocketHolder.put(result.getTo(), new SocketWrapper(client, new Date()));
- 31
- }
- 32 SendHelper.send(client, result);
- 33
- if (result.isSuccess()) { // SEND LIST USER
- 34 ClientListUserDTO dto = new ClientListUserDTO();
- 35 dto.setListUser(SocketHolder.keySet());
- 36 result.setContent(dto).setKey(Key.LISTUSER);
- 37 SendHelper.send(client, result);
- 38
- }
- 39
- }
- 40
- return null;
- 41
- }
- 42 43
- }
View Code
- 1 public class LogoutHandler implements SocketHandler {
- 2 3@Override 4 public Object handle(Socket client, Object data) {
- 5
- if (data != null) {
- 6 LogoutMessage message = JSON.parseObject(data.toString(), LogoutMessage.class);
- 7
- if (message != null && StringUtil.isNotEmpty(message.getFrom())) {
- 8 SocketWrapper wrapper = SocketHolder.get(message.getFrom());
- 9 Socket socket = wrapper.getSocket();
- 10
- if (socket != null) {
- 11
- try {
- 12 socket.close();
- 13 socket = null;
- 14
- } catch(Exception ignore) {
- 15
- }
- 16
- }
- 17 SocketHolder.remove(message.getFrom());
- 18
- }
- 19
- }
- 20
- return null;
- 21
- }
- 22 23
- }
View Code
- 1 public class RegisterHandler implements SocketHandler {
- 2 3 private UsrService usrService = new UsrService();
- 4 5@Override 6 public Object handle(Socket client, Object data) {
- 7 ReturnMessage result = new ReturnMessage();
- 8 result.setSuccess(false).setFrom(ConstantValue.SERVER_NAME);
- 9
- if (data != null) {
- 10 RegisterMessage message = JSON.parseObject(data.toString(), RegisterMessage.class);
- 11
- if (StringUtil.isNotEmpty(message.getUsername()) && StringUtil.isNotEmpty(message.getPassword())) {
- 12
- if (usrService.register(message.getUsername(), message.getPassword()) != null) {
- 13 result.setSuccess(true).setContent(I18N.INFO_REGISTER_OK);
- 14
- } else {
- 15 result.setMessage(I18N.INFO_REGISTER_CLIENT_EXIST);
- 16
- }
- 17
- } else {
- 18 result.setMessage(I18N.INFO_REGISTER_EMPTY_DATA);
- 19
- }
- 20 21
- if (StringUtil.isNotEmpty(message.getUsername())) {
- 22 result.setTo(message.getUsername());
- 23
- }
- 24 // AFTER REGISTER
- 25 result.setKey(Key.REGISTER);
- 26 SendHelper.send(client, result);
- 27
- }
- 28
- return null;
- 29
- }
- 30 31
- }
View Code
- 1
- /**
- 2 * Use SendHelper to send ReturnMessage,
- 3 * @see yaolin.chat.server.SocketDispatcher#run()
- 4 * @author yaolin
- 5 */
- 6@Deprecated 7 public class ReturnHandler implements SocketHandler {
- 8 9
- /**
- 10 * @param data ReturnMessage
- 11 */
- 12@Override 13 public Object handle(Socket client, Object data) {
- 14
- if (data != null) {
- 15 ReturnMessage message = (ReturnMessage) data;
- 16
- if (StringUtil.isNotEmpty(message.getFrom()) && StringUtil.isNotEmpty(message.getTo())) {
- 17 SocketWrapper wrap = SocketHolder.get(message.getTo());
- 18
- if (wrap != null) {
- 19 SendHelper.send(wrap.getSocket(), message);
- 20
- }
- 21
- }
- 22
- }
- 23
- return null;
- 24
- }
- 25 26
- }
用户业务:
服务端除了 socket 之外,还有一点点具体的业务,那就是用户的注册、登陆等,这里简单的列出 Usr 和 UsrService 这两个类,这些业务暂时没有怎么实现,我并不打算在这个程序中引入 ORM 框架,所以自己写一套 DBUtil(待改善),在这里也一并贴出来。
这里只进行了简单的校验,没有持久化存储到 DB 中,下面是 Usr 和 UsrService:
- 1 public class Usr {
- 2 3 private long id;
- 4 private String username;
- 5 private String password;
- 6 public long getId() {
- 7
- return id;
- 8
- }
- 9 public void setId(long id) {
- 10 this.id = id;
- 11
- }
- 12 public String getUsername() {
- 13
- return username;
- 14
- }
- 15 public void setUsername(String username) {
- 16 this.username = username;
- 17
- }
- 18 public String getPassword() {
- 19
- return password;
- 20
- }
- 21 public void setPassword(String password) {
- 22 this.password = password;
- 23
- }
- 24
- }
- 1
- /**
- 2 * // TODO
- 3 * @see yaolin.chat.server.usr.repository.UsrRepository
- 4 * @author yaolin
- 5 *
- 6 */
- 7 public class UsrService {
- 8 // TODO db
- 9 private static Map db = new HashMap();
- 10 11 public Usr register(String username, String password) {
- 12
- if (StringUtil.isEmpty(username) || StringUtil.isEmpty(password)) {
- 13
- return null;
- 14
- }
- 15
- if (db.containsKey(username)) {
- 16
- return null; // exist;
- 17
- }
- 18 Usr usr = new Usr();
- 19 usr.setUsername(username);
- 20 usr.setPassword(MD5Util.getMD5Code(password));
- 21 db.put(username, usr);
- 22
- return usr;
- 23
- }
- 24 25 public Usr login(String username, String password) {
- 26
- if (StringUtil.isEmpty(username) || StringUtil.isEmpty(password)) {
- 27
- return null;
- 28
- }
- 29
- if (db.containsKey(username)) {
- 30 Usr usr = db.get(username);
- 31
- if (MD5Util.getMD5Code(password).equals(usr.getPassword())) {
- 32
- return usr;
- 33
- }
- 34
- }
- 35
- return null;
- 36
- }
- 37
- }
下面是 DBUtil 工具:
- 1
- /**
- 2 * DBUtils // TODO 有待调整&优化!!
- 3 * @author yaolin
- 4 */
- 5 public class DBUtil {
- 6 // make connection used repeatedly
- 7 private static final List cache = new LinkedList();
- 8 private static String url;
- 9 private static String driver;
- 10 private static String user;
- 11 private static String password;
- 12 private static Boolean debug;
- 13 14 static {
- 15 InputStream is = DBUtil.class.getResourceAsStream("/db.properties");
- 16
- try {
- 17 Properties p = new Properties();
- 18 p.load(is);
- 19 url = p.getProperty("url");
- 20 driver = p.getProperty("driver");
- 21 user = p.getProperty("user");
- 22 password = p.getProperty("password");
- 23 // just for debug
- 24
- try {
- 25 debug = Boolean.valueOf(p.getProperty("debug"));
- 26
- } catch(Exception ignore) {
- 27 debug = false;
- 28
- }
- 29
- } catch(Exception e) {
- 30
- throw new RuntimeException(e);
- 31
- } finally {
- 32
- if (is != null) {
- 33
- try {
- 34 is.close();
- 35 is = null;
- 36
- } catch(Exception ignore) {
- 37
- }
- 38
- }
- 39
- }
- 40
- }
- 41 42 public synchronized static Connection getConnection() {
- 43
- if (cache.isEmpty()) {
- 44 cache.add(makeConnection());
- 45
- }
- 46 Connection conn = null;
- 47 int i = 0;
- 48
- try {
- 49 do {
- 50 conn = cache.remove(i);
- 51
- } while ( conn != null && conn . isClosed () && i < cache.size());
- 52
- } catch(Exception ignore) {
- 53
- }
- 54 55
- try {
- 56
- if (conn == null || conn.isClosed()) {
- 57 cache.add(makeConnection());
- 58 conn = cache.remove(0);
- 59
- }
- 60
- return conn;
- 61
- } catch(Exception e) {
- 62
- throw new RuntimeException(e);
- 63
- }
- 64
- }
- 65 66 public synchronized static void close(Connection connection) {
- 67
- try {
- 68
- if (connection != null && !connection.isClosed()) {
- 69
- if (debug) 70 debug("release connection!");
- 71 cache.add(connection);
- 72
- }
- 73
- } catch(SQLException ignore) {
- 74
- }
- 75
- }
- 76 77 public static Object query(String sql, ResultSetMapper mapper, Object...args) {
- 78
- if (debug) 79 debug(sql);
- 80 Connection conn = getConnection();
- 81 PreparedStatement ps = null;
- 82 ResultSet rs = null;
- 83 Object result = null;
- 84
- try {
- 85 ps = conn.prepareStatement(sql);
- 86 int i = 1;
- 87
- for (Object object: args) {
- 88 ps.setObject(i++, object);
- 89
- }
- 90 rs = ps.executeQuery();
- 91 result = mapper.mapper(rs);
- 92
- } catch(Exception e) {
- 93
- throw new RuntimeException(e);
- 94
- } finally {
- 95
- try {
- 96
- if (rs != null) {
- 97 rs.close();
- 98 rs = null;
- 99
- }
- 100
- if (ps != null) {
- 101 ps.close();
- 102 ps = null;
- 103
- }
- 104
- } catch(Exception ignore) {
- 105
- }
- 106
- }
- 107 close(conn);
- 108
- return result;
- 109
- }
- 110 111 public static int modify(String sql, Object...args) {
- 112
- if (debug) 113 debug(sql);
- 114 Connection conn = getConnection();
- 115 PreparedStatement ps = null;
- 116 int row = 0;
- 117
- try {
- 118 ps = conn.prepareStatement(sql);
- 119 int i = 1;
- 120
- for (Object object: args) {
- 121 ps.setObject(i++, object);
- 122
- }
- 123 row = ps.executeUpdate();
- 124
- } catch(Exception e) {
- 125
- throw new RuntimeException(e);
- 126
- } finally {
- 127
- try {
- 128
- if (ps != null) {
- 129 ps.close();
- 130 ps = null;
- 131
- }
- 132
- } catch(Exception ignore) {
- 133
- }
- 134
- }
- 135 close(conn);
- 136
- return row;
- 137
- }
- 138 139 public static int[] batch(List sqls) {
- 140
- if (debug) 141 debug(sqls.toString());
- 142 Connection conn = getConnection();
- 143 Statement stmt = null;
- 144 int[] row;
- 145
- try {
- 146 stmt = conn.createStatement();
- 147
- for (String sql: sqls) {
- 148 stmt.addBatch(sql);
- 149
- }
- 150 row = stmt.executeBatch();
- 151
- } catch(Exception e) {
- 152
- throw new RuntimeException(e);
- 153
- } finally {
- 154
- try {
- 155
- if (stmt != null) {
- 156 stmt.close();
- 157 stmt = null;
- 158
- }
- 159
- } catch(Exception ignore) {
- 160
- }
- 161
- }
- 162 close(conn);
- 163
- return row;
- 164
- }
- 165 166 public static int[] batch(String sql, PreparedStatementSetter setter) {
- 167
- if (debug) 168 debug(sql);
- 169 Connection conn = getConnection();
- 170 PreparedStatement ps = null;
- 171 int[] row;
- 172
- try {
- 173 ps = conn.prepareStatement(sql);
- 174 setter.setter(ps);
- 175 row = ps.executeBatch();
- 176
- } catch(Exception e) {
- 177
- throw new RuntimeException(e);
- 178
- } finally {
- 179
- try {
- 180
- if (ps != null) {
- 181 ps.close();
- 182 ps = null;
- 183
- }
- 184
- } catch(Exception ignore) {
- 185
- }
- 186
- }
- 187 close(conn);
- 188
- return row;
- 189
- }
- 190 191 private static Connection makeConnection() {
- 192
- try {
- 193 Class.forName(driver).newInstance();
- 194 Connection conn = DriverManager.getConnection(url, user, password);
- 195
- if (debug) 196 debug("create connection!");
- 197
- return conn;
- 198
- }
来源: http://www.cnblogs.com/niloay/p/socket-chatserver.html