详解基于java的Socket聊天程序——服务端(附demo)

写在前面:

昨天在博客记录自己抽空写的一个Socket聊天程序的初始设计,那是这个程序的整体设计,为了完整性,今天把服务端的设计细化记录一下,首页贴出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接口,下面给出服务端的代码:

package yaolin.chat.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import yaolin.chat.common.ConstantValue;
import yaolin.chat.util.LoggerUtil;

/**
 * 服务器
 * @author yaolin
 */
public class Server {

  private final ServerSocket server;
  private final ExecutorService pool;

  public Server() throws IOException {
    server = new ServerSocket(ConstantValue.SERVER_PORT);
    pool = Executors.newFixedThreadPool(ConstantValue.MAX_POOL_SIZE);
  }

  public void start() {
    try {
      ScheduledExecutorService schedule = Executors.newScheduledThreadPool(1);
      // Watch dog. Exception??
      schedule.scheduleAtFixedRate(new SocketSchedule(), 10, ConstantValue.TIME_OUT, TimeUnit.SECONDS);

      while (true) {
        pool.execute(new SocketDispatcher(server.accept()));
        LoggerUtil.info("ACCEPT A CLIENT AT " + new Date());
      }
    } catch (IOException e) {
      pool.shutdown();
    }
  }

  public static void main(String[] args) {
    try {
      new Server().start();
    } catch (IOException e) {
      LoggerUtil.error("Server start failed! -> " + e.getMessage(), e);
    }
  }
}

[SocketDispatcher.java]

Server只是服务端的入口,并指挥中心,SocketDispatcher才是服务端的指挥中心,对客户端不同的消息类型请求进行分发,让不同的SocketHandler去处理对应的消息请求,这里服务端和客户端的消息交互都是用JSON数据,所有消息类都继承BaseMessage,所以将接收到数据转换成BaseMessage类型,再判断其类型,(数据类型模块属于common模块),这里需要提一下的是当消息类型是文件类型的时候会睡眠配置执行的间隔时间,这样FileHandler才能有时间对文件流进行读取和重新发送给指定的客户端,而不会立即进入下一次循环对消息类型的判断(可能这里设计有点问题,不过暂时先这样做),下面给出SocketDispatcher的代码:

/**
 * SocketDispatcher
 *
 * @author yaolin
 */
public class SocketDispatcher implements Runnable {

  private final Socket socket;

  public SocketDispatcher(Socket socket) {
    this.socket = socket;
  }

  @Override
  public void run() {
    if (socket != null) {
      while (!socket.isClosed()) {
        try {
          InputStream is = socket.getInputStream();
          String line = null;
          StringBuffer sb = null;

          if (is.available() > 0) {

            BufferedReader bufr = new BufferedReader(new InputStreamReader(is));
            sb = new StringBuffer();
            while (is.available() > 0 && (line = bufr.readLine()) != null) {
              sb.append(line);
            }
            LoggerUtil.trach("RECEIVE [" + sb.toString() + "] AT " + new Date());

            BaseMessage message = JSON.parseObject(sb.toString(), BaseMessage.class);

            switch (message.getType()) {
            case MessageType.ALIVE:
              HandlerFactory.getHandler(MessageType.ALIVE).handle(socket, sb.toString());
              break;
            case MessageType.CHAT:
              HandlerFactory.getHandler(MessageType.CHAT).handle(socket, sb.toString());
              break;
            case MessageType.FILE:
              HandlerFactory.getHandler(MessageType.FILE).handle(socket, sb.toString());
              LoggerUtil.trach("SEVER:PAUSE TO RECEIVE FILE");
              Thread.sleep(ConstantValue.MESSAGE_PERIOD);
              break;
            case MessageType.LOGIN:
              HandlerFactory.getHandler(MessageType.LOGIN).handle(socket, sb.toString());
              break;
            case MessageType.LOGOUT:
              break;
            case MessageType.REGISTER:
              HandlerFactory.getHandler(MessageType.REGISTER).handle(socket, sb.toString());
              break;
            }
          } else {
            Thread.sleep(ConstantValue.MESSAGE_PERIOD);
          }
        } catch (Exception e) { // catch all handler exception
          LoggerUtil.error("SocketDispatcher Error!" + e.getMessage(), e);
        }
      }
    }
  }
}

[SocketSchedule.java]

跟Server有直接关系的另一个类(组件)是SocketSchedule,SocketSchedule主要负责检测客户端的最新一次跟服务端的交互时间是否超过系统配置允许最大的时间,如果超过了,则将该客户端socket从服务端移除,否则更新客户端的最新一次跟服务端的交互时间。下面是具体的实现:

/**
 * Remove socket from SocketHolder if lastAliveTime > TIME_OUT
 * @author yaolin
 *
 */
public class SocketSchedule implements Runnable {

  @Override
  public void run() {
    for (String key : SocketHolder.keySet()) {
      SocketWrapper wrapper = SocketHolder.get(key);
      if (wrapper != null && wrapper.getLastAliveTime() != null) {
        if (((new Date().getTime() - wrapper.getLastAliveTime().getTime()) / 1000) > ConstantValue.TIME_OUT) {
          // remove socket if timeout
          SocketHolder.remove(key);
        }
      }
    }
  }
}

[SocketHolder.java、SocketWrapper.java]

从上面的代码可以看出,SocketSchedule#run()只是简单的对时间进行一次判断,真正有意义的其实是SocketHolder和SocketWrapper,SocketWrapper则是对socket加了一层外壳包装,SocketHolder的存储了当前有效时间内所有跟服务端有交互的客户端,SocketHolder以客户端的唯一标识(这里使用用户名),作为KEY,客户端所在的socket作为VALUE的键值对形式存储,其中SocketHolder#flushClientStatus()的处理逻辑是用于通知其他客户端当前客户端的上线/离线状态,下面给出这两个类的具体实现:

/**
 * Wrap Socket, SocketSchedule remove socket if lastAliveTime > TIME_OUT
 * @author yaolin
 *
 */
public class SocketWrapper {

  private Socket socket;
  private Date lastAliveTime;

  // full constructor
  public SocketWrapper(Socket socket, Date lastAliveTime) {
    this.socket = socket;
    this.lastAliveTime = lastAliveTime;
  }
  public Socket getSocket() {
    return socket;
  }
  public void setSocket(Socket socket) {
    this.socket = socket;
  }
  public Date getLastAliveTime() {
    return lastAliveTime;
  }
  public void setLastAliveTime(Date lastAliveTime) {
    this.lastAliveTime = lastAliveTime;
  }
}
/**
 * SocketHolder
 * @author yaolin
 */
public class SocketHolder {

  private static ConcurrentMap<String, SocketWrapper> listSocketWrap = new ConcurrentHashMap<String, SocketWrapper>();

  public static Set<String> keySet() {
    return listSocketWrap.keySet();
  }

  public static SocketWrapper get(String key) {
    return listSocketWrap.get(key);
  }

  public static void put(String key, SocketWrapper value) {
    listSocketWrap.put(key, value);
    flushClientStatus(key, true);
  }

  public static SocketWrapper remove(String key) {
    flushClientStatus(key, false);
    return listSocketWrap.remove(key);
  }

  public static void clear() {
    listSocketWrap.clear();
  }

  /**
   * <pre>content:{username:"",flag:false}</pre>
   * @param flag true:put,false:remove;
   */
  private static void flushClientStatus(String key, boolean flag) {
    ClientNotifyDTO dto = new ClientNotifyDTO(flag, key);
    ReturnMessage rm = new ReturnMessage().setKey(Key.NOTIFY).setSuccess(true).setContent(dto);
    rm.setFrom(ConstantValue.SERVER_NAME);
    for (String toKey : listSocketWrap.keySet()) {
      if (!toKey.equals(key)) { // not send to self
        rm.setTo(toKey);
        SocketWrapper wrap = listSocketWrap.get(toKey);
        if (wrap != null) {
          SendHelper.send(wrap.getSocket(), rm);
        }
      }
    }
  }
}

[SocketHandler.java、HandlerFactory.java、OtherHandlerImpl.java]

SocketDispatcher让不同的SocketHandler去处理对应的消息请求,SocketHandler的设计其实就是一套简单的工厂组件吧(其中ReturnHandler暂时由SendHelper实现信息传送,暂时没有用到,已经@Deprecated ,这里还是给出),完整类图如下:

下面给出这一块的代码,为了缩小篇幅,将所有Handler实现的代码收起来。

/**
 * SocketHandler
 * @author yaolin
 */
public interface SocketHandler {
  /**
   * Handle Client Socket
   */
  public Object handle(Socket client,Object data);
}
/**
 * SocketHandlerFactory
 * @author yaolin
 */
public class HandlerFactory {

  // can not create instance
  private HandlerFactory(){}

  public static SocketHandler getHandler(int type) {
    switch (type) {
    case MessageType.ALIVE: // usually use
      return new AliveHandler();
    case MessageType.CHAT:
      return new ChatHandler();
    case MessageType.LOGIN:
      return new LoginHandler();
//    case MessageType.RETURN:
//      return new ReturnHandler();
    case MessageType.LOGOUT:
      return new LogoutHandler();
    case MessageType.REGISTER:
      return new RegisterHandler();
    case MessageType.FILE:
      return new FileHandler();
    }
    return null; // NullPointException
  }
}
/**
 * AliveSocketHandler
 * @author yaolin
 */
public class AliveHandler implements SocketHandler {

  /**
   * @return null
   */
  @Override
  public Object handle(Socket client, Object data) {
    if (data != null) {
      BaseMessage message = JSON.parseObject(data.toString(), BaseMessage.class);
      if (StringUtil.isNotEmpty(message.getFrom())) {
        SocketWrapper wrapper = SocketHolder.get(message.getFrom());
        if (wrapper != null) {
          wrapper.setLastAliveTime(new Date()); // KEEP SOCKET ...
          SocketHolder.put(message.getFrom(), wrapper);
        }
      }
    }
    return null;
  }

}
/**
 * ChatHandler
 *
 * @author yaolin
 */
public class ChatHandler implements SocketHandler {

  @Override
  public Object handle(Socket client, Object data) {
    if (data != null) {
      ChatMessage message = JSON.parseObject(data.toString(), ChatMessage.class);

      if (StringUtil.isNotEmpty(message.getFrom()) && StringUtil.isNotEmpty(message.getTo())) {
        // exist & send
        if (SocketHolder.keySet().contains(message.getFrom())) {
          String owner = message.getFrom();
          message.setOwner(owner); // owner will be display
          if (ConstantValue.TO_ALL.equals(message.getTo())) { // one-to-all
            // TO_ALL TAB will be select;
            message.setFrom(ConstantValue.TO_ALL);
            for (String key : SocketHolder.keySet()) {
              // also send to self
              SocketWrapper wrapper = SocketHolder.get(key);
              if (wrapper != null) {
                SendHelper.send(wrapper.getSocket(), message);
              }
            }
          } else {// one-to-one
            SocketWrapper wrapper = SocketHolder.get(message.getTo());
            if (wrapper != null) {
              // owner = from
              SendHelper.send(wrapper.getSocket(), message);
              // also send to self
              // TO TAB will be select;
              message.setFrom(message.getTo()).setTo(owner);
              SendHelper.send(client, message);
            }
          }
        }
      }
    }
    return null;
  }
}
public class FileHandler implements SocketHandler {

  @Override
  public Object handle(Socket client, Object data) {
    if (client != null) {
      FileMessage message = JSON.parseObject(data.toString(), FileMessage.class);
      if (StringUtil.isNotEmpty(message.getFrom()) && StringUtil.isNotEmpty(message.getTo())) {
        // exist & send
        if (SocketHolder.keySet().contains(message.getFrom())) {
          if (!ConstantValue.TO_ALL.equals(message.getTo())) { // one-to-all
            SocketWrapper wrapper = SocketHolder.get(message.getTo());
            if (wrapper != null) {
              SendHelper.send(wrapper.getSocket(), message);
              try {
                if (client != null && wrapper.getSocket() != null && message.getSize() > 0) {
                  InputStream is = client.getInputStream();
                  OutputStream os = wrapper.getSocket().getOutputStream();
                  int total = 0;
                  while (!client.isClosed() && !wrapper.getSocket().isClosed()) {
                    if (is.available() > 0) {
                      byte[] buff = new byte[ConstantValue.BUFF_SIZE];
                      int len = -1;
                      while (is.available() > 0 && (len = is.read(buff)) != -1) {
                        os.write(buff, 0, len);
                        total += len;
                        LoggerUtil.debug("SEND BUFF [" + len + "]");
                      }
                      os.flush();
                      if (total >= message.getSize()) {
                        LoggerUtil.info("SEND BUFF [OK]");
                        break;
                      }
                    }
                  }
                  // AFTER SEND FILE
                  // SEND SUCCESSFULLY
                  ReturnMessage result = new ReturnMessage().setKey(Key.TIP)
                      .setSuccess(true)
                      .setContent(I18N.INFO_FILE_SEND_SUCCESSFULLY);
                  result.setFrom(message.getTo()).setTo(message.getFrom())
                      .setOwner(ConstantValue.SERVER_NAME);
                  SendHelper.send(client, result);
                  // RECEIVE SUCCESSFULLY
                  result.setContent(I18N.INFO_FILE_RECEIVE_SUCCESSFULLY)
                      .setFrom(message.getFrom())
                      .setTo(message.getTo());
                  SendHelper.send(wrapper.getSocket(), result);
                }
              } catch (Exception e) {
                LoggerUtil.error("Handle file failed !" + e.getMessage(), e);
              }
            }
          }
        }
      }
    }
    return null;
  }
}
/**
 * LoginHandler
 *
 * @author yaolin
 *
 */
public class LoginHandler implements SocketHandler {

  private UsrService usrService = new UsrService();

  @Override
  public Object handle(Socket client, Object data) {
    ReturnMessage result = new ReturnMessage();
    result.setSuccess(false);
    if (data != null) {
      LoginMessage message = JSON.parseObject(data.toString(), LoginMessage.class);
      if (StringUtil.isNotEmpty(message.getUsername()) && StringUtil.isNotEmpty(message.getPassword())) {
        if (usrService.login(message.getUsername(), message.getPassword()) != null) {
          result.setSuccess(true);
        } else {
          result.setMessage(I18N.INFO_LOGIN_ERROR_DATA);
        }
        result.setFrom(ConstantValue.SERVER_NAME).setTo(message.getUsername());
      } else {
        result.setMessage(I18N.INFO_LOGIN_EMPTY_DATA);
      }
      // AFTER LOGIN
      result.setKey(Key.LOGIN);
      if (result.isSuccess()) { // HOLD SOCKET
        SocketHolder.put(result.getTo(), new SocketWrapper(client, new Date()));
      }
      SendHelper.send(client, result);
      if (result.isSuccess()) { // SEND LIST USER
        ClientListUserDTO dto = new ClientListUserDTO();
        dto.setListUser(SocketHolder.keySet());
        result.setContent(dto).setKey(Key.LISTUSER);
        SendHelper.send(client, result);
      }
    }
    return null;
  }

}
public class LogoutHandler implements SocketHandler {

  @Override
  public Object handle(Socket client, Object data) {
    if (data != null) {
      LogoutMessage message = JSON.parseObject(data.toString(), LogoutMessage.class);
      if (message != null && StringUtil.isNotEmpty(message.getFrom())) {
        SocketWrapper wrapper = SocketHolder.get(message.getFrom());
        Socket socket = wrapper.getSocket();
        if (socket != null) {
          try {
            socket.close();
            socket = null;
          } catch (Exception ignore) {
          }
        }
        SocketHolder.remove(message.getFrom());
      }
    }
    return null;
  }

}
public class RegisterHandler implements SocketHandler {

  private UsrService usrService = new UsrService();

  @Override
  public Object handle(Socket client, Object data) {
    ReturnMessage result = new ReturnMessage();
    result.setSuccess(false).setFrom(ConstantValue.SERVER_NAME);
    if (data != null) {
      RegisterMessage message = JSON.parseObject(data.toString(), RegisterMessage.class);
      if (StringUtil.isNotEmpty(message.getUsername()) && StringUtil.isNotEmpty(message.getPassword())) {
        if (usrService.register(message.getUsername(), message.getPassword()) != null) {
          result.setSuccess(true).setContent(I18N.INFO_REGISTER_OK);
        } else {
          result.setMessage(I18N.INFO_REGISTER_CLIENT_EXIST);
        }
      } else {
        result.setMessage(I18N.INFO_REGISTER_EMPTY_DATA);
      }

      if (StringUtil.isNotEmpty(message.getUsername())) {
        result.setTo(message.getUsername());
      }
      // AFTER REGISTER
      result.setKey(Key.REGISTER);
      SendHelper.send(client, result);
    }
    return null;
  }

}
/**
 * Use SendHelper to send ReturnMessage,
 * @see yaolin.chat.server.SocketDispatcher#run()
 * @author yaolin
 */
@Deprecated
public class ReturnHandler implements SocketHandler {

  /**
   * @param data ReturnMessage
   */
  @Override
  public Object handle(Socket client, Object data) {
    if (data != null) {
      ReturnMessage message = (ReturnMessage) data;
      if(StringUtil.isNotEmpty(message.getFrom()) && StringUtil.isNotEmpty(message.getTo())) {
        SocketWrapper wrap = SocketHolder.get(message.getTo());
        if (wrap != null) {
          SendHelper.send(wrap.getSocket(), message);
        }
      }
    }
    return null;
  }

}

用户业务:

服务端除了socket之外,还有一点点具体的业务,那就是用户的注册、登陆等,这里简单的列出Usr和UsrService这两个类,这些业务暂时没有怎么实现,我并不打算在这个程序中引入ORM框架,所以自己写一套DBUtil(待改善),在这里也一并贴出来。

这里只进行了简单的校验,没有持久化存储到DB中,下面是Usr和UsrService:

public class Usr {

  private long id;
  private String username;
  private String password;
  public long getId() {
    return id;
  }
  public void setId(long id) {
    this.id = id;
  }
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
}
/**
 * // TODO
 * @see yaolin.chat.server.usr.repository.UsrRepository
 * @author yaolin
 *
 */
public class UsrService {
  // TODO db
  private static Map<String,Usr> db = new HashMap<String,Usr>();

  public Usr register(String username, String password) {
    if (StringUtil.isEmpty(username) || StringUtil.isEmpty(password)) {
      return null;
    }
    if (db.containsKey(username)) {
      return null; // exist;
    }
    Usr usr = new Usr();
    usr.setUsername(username);
    usr.setPassword(MD5Util.getMD5Code(password));
    db.put(username, usr);
    return usr;
  }

  public Usr login(String username, String password) {
    if (StringUtil.isEmpty(username) || StringUtil.isEmpty(password)) {
      return null;
    }
    if (db.containsKey(username)) {
      Usr usr = db.get(username);
      if (MD5Util.getMD5Code(password).equals(usr.getPassword())) {
        return usr;
      }
    }
    return null;
  }
}

下面是DBUtil工具:

 /**
 * DBUtils // TODO 有待调整&优化!!
 * @author yaolin
 */
public class DBUtil {
  // make connection used repeatedly
  private static final List<Connection> cache = new LinkedList<Connection>();
  private static String url;
  private static String driver;
  private static String user;
  private static String password;
  private static Boolean debug;

  static {
    InputStream is = DBUtil.class.getResourceAsStream("/db.properties");
    try {
      Properties p = new Properties();
      p.load(is);
      url = p.getProperty("url");
      driver = p.getProperty("driver");
      user = p.getProperty("user");
      password = p.getProperty("password");
      // just for debug
      try {
        debug = Boolean.valueOf(p.getProperty("debug"));
      } catch (Exception ignore) {
        debug = false;
      }
    } catch (Exception e) {
      throw new RuntimeException(e);
    } finally {
      if (is != null) {
        try {
          is.close();
          is = null;
        } catch (Exception ignore) {
        }
      }
    }
  }

  public synchronized static Connection getConnection() {
    if (cache.isEmpty()) {
      cache.add(makeConnection());
    }
    Connection conn = null;
    int i = 0;
    try {
      do {
        conn = cache.remove(i);
      } while (conn != null && conn.isClosed() && i < cache.size());
    } catch (Exception ignore) {
    }

    try {
      if (conn == null || conn.isClosed()) {
        cache.add(makeConnection());
        conn = cache.remove(0);
      }
      return conn;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  public synchronized static void close(Connection connection) {
    try {
      if (connection != null && !connection.isClosed()) {
        if (debug)
          debug("release connection!");
        cache.add(connection);
      }
    } catch (SQLException ignore) {
    }
  }

  public static Object query(String sql, ResultSetMapper mapper, Object... args) {
    if (debug)
      debug(sql);
    Connection conn = getConnection();
    PreparedStatement ps = null;
    ResultSet rs = null;
    Object result = null;
    try {
      ps = conn.prepareStatement(sql);
      int i = 1;
      for (Object object : args) {
        ps.setObject(i++, object);
      }
      rs = ps.executeQuery();
      result = mapper.mapper(rs);
    } catch (Exception e) {
      throw new RuntimeException(e);
    } finally {
      try {
        if (rs != null) {
          rs.close();
          rs = null;
        }
        if (ps != null) {
          ps.close();
          ps = null;
        }
      } catch (Exception ignore) {
      }
    }
    close(conn);
    return result;
  }

  public static int modify(String sql, Object... args) {
    if (debug)
      debug(sql);
    Connection conn = getConnection();
    PreparedStatement ps = null;
    int row = 0;
    try {
      ps = conn.prepareStatement(sql);
      int i = 1;
      for (Object object : args) {
        ps.setObject(i++, object);
      }
      row = ps.executeUpdate();
    } catch (Exception e) {
      throw new RuntimeException(e);
    } finally {
      try {
        if (ps != null) {
          ps.close();
          ps = null;
        }
      } catch (Exception ignore) {
      }
    }
    close(conn);
    return row;
  }

  public static int[] batch(List<String> sqls) {
    if (debug)
      debug(sqls.toString());
    Connection conn = getConnection();
    Statement stmt = null;
    int[] row;
    try {
      stmt = conn.createStatement();
      for (String sql : sqls) {
        stmt.addBatch(sql);
      }
      row = stmt.executeBatch();
    } catch (Exception e) {
      throw new RuntimeException(e);
    } finally {
      try {
        if (stmt != null) {
          stmt.close();
          stmt = null;
        }
      } catch (Exception ignore) {
      }
    }
    close(conn);
    return row;
  }

  public static int[] batch(String sql, PreparedStatementSetter setter) {
    if (debug)
      debug(sql);
    Connection conn = getConnection();
    PreparedStatement ps = null;
    int[] row;
    try {
      ps = conn.prepareStatement(sql);
      setter.setter(ps);
      row = ps.executeBatch();
    } catch (Exception e) {
      throw new RuntimeException(e);
    } finally {
      try {
        if (ps != null) {
          ps.close();
          ps = null;
        }
      } catch (Exception ignore) {
      }
    }
    close(conn);
    return row;
  }

  private static Connection makeConnection() {
    try {
      Class.forName(driver).newInstance();
      Connection conn = DriverManager.getConnection(url, user, password);
      if (debug)
        debug("create connection!");
      return conn;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

  private static void debug(String sqls) {
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    System.out.println(sdf.format(new Date())
        + " DEBUG " + Thread.currentThread().getId()
        + " --- [" + Thread.currentThread().getName() + "] " + "excute sqls : " + sqls);
  }
}
/**
 * PreparedStatementSetter
 * @author yaolin
 */
public interface PreparedStatementSetter {
  public void setter(PreparedStatement ps);
}
/**
 * ResultSetMapper
 * @author yaolin
 */
public interface ResultSetMapper {
  public Object mapper(ResultSet rs);
}

源码下载:demo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 基于Tomcat7、Java、WebSocket的服务器推送聊天室实例

    前言 HTML5 WebSocket实现了服务器与浏览器的双向通讯,双向通讯使服务器消息推送开发更加简单,最常见的就是即时通讯和对信息实时性要求比较高的应用.以前的服务器消息推送大部分采用的都是"轮询"和"长连接"技术,这两中技术都会对服务器产生相当大的开销,而且实时性不是特别高.WebSocket技术对只会产生很小的开销,并且实时性特别高.下面就开始讲解如何利用WebSocket技术开发聊天室.在这个实例中,采用的是Tomcat7服务器,每个服务器对于WebSoc

  • Java基于socket服务实现UDP协议的方法

    本文实例讲述了Java基于socket服务实现UDP协议的方法.分享给大家供大家参考.具体如下: 示例1: 接收类: package com.socket.demo; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; public class UDPReceiveDemo { public static void main(String[] args) throw

  • Java Socket实现文件传输示例代码

    最近学Socket学上瘾了,就写了一个简单的文件传输程序. 客户端设计思路:客户端与服务端建立连接,选择客户端本地文件,先将文件名及大小等属性发送给服务端,再将文件通过流的方式传输给服务端.传输的进度打印到控制台中,直到传输完成. 服务端设计思路:服务端接收客户端的请求(阻塞式),每接收到一个客户端请求连接后,就新开一个处理文件的线程,开始写入流,将文件到服务器的指定目录下,并与传输过来的文件同名. 下面是客户端和服务端的代码实现: 客户端代码: import java.io.DataOutpu

  • Java Socket编程实例(二)- UDP基本使用

    一.服务端代码: import java.io.*; import java.net.*; public class UDPEchoServer { private static final int ECHOMAX = 255; // Maximum size of echo datagram public static void main(String[] args) throws IOException { int servPort = 5500; // Server port Datagr

  • java使用Socket类接收和发送数据

    网络应用分为客户端和服务端两部分,而Socket类是负责处理客户端通信的Java类.通过这个类可以连接到指定IP或域名的服务器上,并且可以和服务器互相发送和接受数据.在本文及后面的数篇文章中将详细讨论Socket类的使用,内容包括Socket类基础.各式各样的连接方式.get和set方法.连接过程中的超时以及关闭网络连接等. 在本文中,我们将讨论使用Socket类的基本步骤和方法.一般网络客户端程序在连接服务程序时要进行以下三步操作. 连接服务器 发送和接收数据 关闭网络连接 一.连接服务器 在

  • 详解java WebSocket的实现以及Spring WebSocket

    开始学习WebSocket,准备用它来实现一个在页面实时输出log4j的日志以及控制台的日志. 首先知道一些基础信息: 1.java7 开始支持WebSocket,并且只是做了定义,并未实现 2.tomcat7及以上,jetty 9.1及以上实现了WebSocket,其他容器没有研究 3.spring 4.0及以上增加了WebSocket的支持 4.spring 支持STOMP协议的WebSocket通信 5.WebSocket 作为java的一个扩展,它属于javax包目录下,通常需要手工引入

  • java WebSocket的实现以及Spring WebSocket示例代码

    开始学习WebSocket,准备用它来实现一个在页面实时输出log4j的日志以及控制台的日志. 首先知道一些基础信息: 1.java7 开始支持WebSocket,并且只是做了定义,并未实现 2.tomcat7及以上,jetty 9.1及以上实现了WebSocket,其他容器没有研究 3.spring 4.0及以上增加了WebSocket的支持 4.spring 支持STOMP协议的WebSocket通信 5.WebSocket 作为java的一个扩展,它属于javax包目录下,通常需要手工引入

  • 详解基于java的Socket聊天程序——客户端(附demo)

    写在前面: 上周末抽点时间把自己写的一个简单Socket聊天程序的初始设计和服务端细化设计记录了一下,周二终于等来毕业前考的软考证书,然后接下来就是在加班的日子度过了,今天正好周五,打算把客户端的详细设计和Common模块记录一下,因为这个周末开始就要去忙其他东西了. 设计: 客户端设计主要分成两个部分,分别是socket通讯模块设计和UI相关设计. 客户端socket通讯设计: 这里的设计其实跟服务端的设计差不多,不同的是服务端是接收心跳包,而客户端是发送心跳包,由于客户端只与一个服务端进行通

  • Java Socket聊天室编程(一)之利用socket实现聊天之消息推送

    相关阅读:Java Socket聊天室编程(二)之利用socket实现单聊聊天室 网上已经有很多利用socket实现聊天的例子了,但是我看过很多,多多少有一些问题存在. 这里我将实现一个比较完整的聊天例子,并解释其中的逻辑. 由于socket这一块比较大,所以我将分出几篇来写一个比较完整的socket例子. 这里我们先来实现一个最简单的,服务器与客户端通讯,实现消息推送的功能. 目的:服务器与客户端建立连接,客户端可以向服务器发送消息,服务器可以向客户端推送消息. 1,使用java建立socke

  • JAVA实现基于Tcp协议的简单Socket通信实例

    好久没写博客了,前段时间忙于做项目,耽误了些时间,今天开始继续写起~ 今天来讲下关于Socket通信的简单应用,关于什么是Socket以及一些网络编程的基础,这里就不提了,只记录最简单易懂实用的东西.  1.首先先来看下基于TCP协议Socket服务端和客户端的通信模型: Socket通信步骤:(简单分为4步) 1.建立服务端ServerSocket和客户端Socket 2.打开连接到Socket的输出输入流 3.按照协议进行读写操作 4.关闭相对应的资源 2.相关联的API: 1.首先先来看下

  • java Socket简易聊天工具

    本文实例为大家分享了一款Socket简易聊天工具,希望大家喜欢. 代码运行如图,看起来还不错 服务端 package qiu; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.print.Printable; import java.io.BufferedReader; import java.io.IOEx

  • Java Socket编程实例(五)- NIO UDP实践

    一.回传协议接口和UDP方式实现: 1.接口: import java.nio.channels.SelectionKey; import java.io.IOException; public interface EchoProtocol { void handleAccept(SelectionKey key) throws IOException; void handleRead(SelectionKey key) throws IOException; void handleWrite(

  • java Socket UDP实例详解

    UDP编程示例 服务器端: package socket; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; public class UDPServer { public static void main(String[] args) throws IOException { byte[] buf

随机推荐