项目简介
本项目是基于WebSocket和Servlet实现的网页聊天室。使用到的技术包括:MVC编程思想、WebSocket、Servlet、MySQL、DruidDataSource、Json、Junit、Lombok、Maven
功能实现
1、通过浏览器访问Tomcat服务器,显示注册、登录、聊天界面,实现聊天室的可视化功能
2、使用MySQL数据库存储用户信息,通过与数据库交互来实现用户登录、注册等功能
3、使用WebSocket协议来实现客户端与服务端之间数据的双向交互,以此完成聊天功能
4、通过遍历在线用户列表,广播发送消息,实现群聊、提醒某用户上线下线的功能
思路
1.注册实现
2.登录实现
3.私聊实现
4.群聊实现
后端设计
- 工具类
package com.bittech.java7.chatroom.utils;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
* @Description:封装基础的工具方法,如加载配置文件、json序列化等
*/
public class CommUtils {
private static final Gson gson = new GsonBuilder().create();
private CommUtils(){}
/**
* 根据指定的文件名加载配置文件
* @param fileName 配置文件名
* @return
*/
public static Properties loadProperties(String fileName) {
Properties properties = new Properties();
// 获取当前配置文件夹下的文件输入流
InputStream in = CommUtils.class.getClassLoader()
.getResourceAsStream(fileName);
// 加载配置文件中的所有内容
try {
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
}
return properties;
}
public static String object2Json(Object obj) {
return gson.toJson(obj);
}
public static Object json2Object(String jsonStr,Class objClass) {
return gson.fromJson(jsonStr,objClass);
}
public static boolean strIsNull(String str) {
return str == null || str.equals("");
}
}
- DAO层
AcountDao类:
package com.bittech.java7.chatroom.dao;
import com.bittech.java7.chatroom.entity.User;
import org.apache.commons.codec.digest.DigestUtils;
import java.sql.*;
/**
* @Description:关于用户模块的dao层
*/
public class AccountDao extends BaseDao{
// 用户登录 select
public User userLogin(String userName,String password) {
Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
User user = null;
try {
connection = getConnection();
String sql = "SELECT * FROM user WHERE username = ? AND " +
" password = ?";
statement = connection.prepareStatement(sql);
statement.setString(1,userName);
statement.setString(2, DigestUtils.md5Hex(password));
resultSet = statement.executeQuery();
if (resultSet.next()) {
user = getUserInfo(resultSet);
}
}catch (Exception e) {
System.err.println("查询用户信息出错");
e.printStackTrace();
}finally {
closeResources(connection,statement,resultSet);
}
return user;
}
// 用户注册 insert
public boolean userRegister(User user) {
String userName = user.getUserName();
String password = user.getPassword();
Connection connection = null;
PreparedStatement statement = null;
boolean isSuccess = false;
try {
connection = getConnection();
String sql = "INSERT INTO user(username, password)" +
" VALUES(?,?) ";
statement = connection.prepareStatement(sql,Statement.RETURN_GENERATED_KEYS);
statement.setString(1,userName);
statement.setString(2,DigestUtils.md5Hex(password));
isSuccess = (statement.executeUpdate() == 1);
}catch (Exception e) {
System.err.println("用户注册失败");
e.printStackTrace();
}finally {
closeResources(connection,statement);
}
return isSuccess;
}
// 将数据表信息封装到User类中
public User getUserInfo(ResultSet resultSet) throws SQLException {
User user = new User();
user.setId(resultSet.getInt("id"));
user.setUserName(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
}
BaseDao类:
package com.bittech.java7.chatroom.dao;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import com.bittech.java7.chatroom.utils.CommUtils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
/**
* @Description:封装基础操作,数据源、获取连接、关闭资源
*/
public class BaseDao {
private static DataSource dataSource;
static {
Properties properties = CommUtils.
loadProperties("datasource.properties");
try {
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
System.err.println("数据源加载失败");
}
}
// 获取数据库连接
protected Connection getConnection() {
try {
return dataSource.getConnection();
} catch (SQLException e) {
System.err.println("获取连接失败");
}
return null;
}
// 关闭资源Statement ResultSet Connection
protected void closeResources(Connection connection,
Statement statement) {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
protected void closeResources(Connection connection,
Statement statement,
ResultSet resultSet) {
closeResources(connection,statement);
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- entity层
package com.bittech.java7.chatroom.entity;
import lombok.Data;
import java.util.Map;
/**
* @Description:后端发送给前端的信息实体
*/
@Data
public class Message2Client {
// 聊天内容
private String content;
// 服务端登录的所有用户列表
private Map<String, String> names;
public void setContent(String msg) {
this.content = msg;
}
public void setContent(String userName,String msg) {
this.content = userName + "说:" + msg;
}
}
package com.bittech.java7.chatroom.entity;
import lombok.Data;
/**
* @Description:前端发送给后端的信息实体类
* 群聊:{"msg":"777","type":1}
* 私聊:{"to":"0-","msg":"33333","type":2}
*/
@Data
public class MessageFromClient {
// 聊天信息
private String msg;
// 聊天类别: 1表示群聊 2表示私聊
private String type;
// 私聊的对象SessionID
private String to;
}
package com.bittech.java7.chatroom.entity;
import lombok.Data;
@Data
public class User {
private Integer id;
private String userName;
private String password;
}
- controller层
package com.bittech.java7.chatroom.controller;
import com.bittech.java7.chatroom.service.AccountService;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @Description:
*/
@WebServlet(urlPatterns = "/doRegister")
public class AccountController extends HttpServlet {
private AccountService accountService = new AccountService();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userName = req.getParameter("username");
String password = req.getParameter("password");
resp.setContentType("text/html;charset=utf8");
PrintWriter writer = resp.getWriter();
if (accountService.userRegister(userName,password)) {
// 用户注册成功
// 弹框提示,返回登陆界面
writer.println("<script>\n" +
" alert(\"注册成功\");\n" +
" window.location.href = \"/index.html\";\n" +
"</script>");
}else {
// 弹框提示失败,保留原页面
writer.println("<script>\n" +
" alert(\"注册失败\");\n" +
" window.location.href = \"/registration.html\";\n" +
"</script>");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
package com.bittech.java7.chatroom.controller;
import java.util.HashMap;
import com.bittech.java7.chatroom.config.FreeMarkerListener;
import com.bittech.java7.chatroom.service.AccountService;
import com.bittech.java7.chatroom.utils.CommUtils;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
/**
* @Description:
*/
@WebServlet(urlPatterns = "/login")
public class LoginController extends HttpServlet {
private AccountService accountService = new AccountService();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userName = req.getParameter("username");
String password = req.getParameter("password");
resp.setContentType("text/html;charset=utf8");
PrintWriter out = resp.getWriter();
if (CommUtils.strIsNull(userName) || CommUtils.strIsNull(password)) {
// 登录失败,停留登录页面
out.println(" <script>\n" +
" alert(\"用户名或密码为空!\");\n" +
" window.location.href = \"/index.html\";\n" +
" </script>");
}
if (accountService.userLogin(userName,password)) {
// 登录成功,跳转到聊天页面
// 加载chat.ftl
Template template = getTemplate(req,"/chat.ftl");
Map<String, String> map = new HashMap<>();
map.put("username",userName);
try {
template.process(map, out);
} catch (TemplateException e) {
e.printStackTrace();
}
}else {
// 登录失败,停留在登录页面
out.println(" <script>\n" +
" alert(\"用户名或密码不正确!\");\n" +
" window.location.href = \"/index.html\";\n" +
" </script>");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
private Template getTemplate(HttpServletRequest req,String fileName) {
Configuration cfg = (Configuration)
req.getServletContext().getAttribute(FreeMarkerListener.TEMPLATE_KEY);
try {
return cfg.getTemplate(fileName);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
- service层:
package com.bittech.java7.chatroom.service;
import com.bittech.java7.chatroom.dao.AccountDao;
import com.bittech.java7.chatroom.entity.User;
/**
* @Description:
*/
public class AccountService {
private AccountDao accountDao = new AccountDao();
// 用户登陆
public boolean userLogin(String userName,String password) {
User user = accountDao.userLogin(userName,password);
if (user == null) {
return false;
}
return true;
}
// 用户注册
public boolean userRegister(String userName,String password) {
User user = new User();
user.setUserName(userName);
user.setPassword(password);
return accountDao.userRegister(user);
}
}
package com.bittech.java7.chatroom.service;
import com.bittech.java7.chatroom.entity.Message2Client;
import com.bittech.java7.chatroom.entity.MessageFromClient;
import com.bittech.java7.chatroom.utils.CommUtils;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @Description:
*/
@ServerEndpoint("/websocket")
public class WebSocket {
// 存储所有连接到后端的websocket
private static CopyOnWriteArraySet<WebSocket> clients =
new CopyOnWriteArraySet<>();
// 缓存所有的用户列表
private static Map<String,String> names = new ConcurrentHashMap<>();
// 绑定当前websocket会话
private Session session;
// 当前客户端的用户名
private String userName;
@OnOpen
public void onOpen(Session session) {
this.session = session;
// username=' + '${username}
String userName = session.getQueryString().split("=")[1];
this.userName = userName;
// 将客户端聊天实体保存到clients
clients.add(this);
// 将当前用户以及SessionID保存到用户列表
names.put(session.getId(),userName);
System.out.println("有新的连接,SessionID为"+session.getId() +
",用户名为"+userName);
// 发送给所有在线用户一个上线通知
Message2Client message2Client = new Message2Client();
message2Client.setContent(userName+"上线了!");
message2Client.setNames(names);
// 发送信息
String jsonStr = CommUtils.object2Json(message2Client);
for (WebSocket webSocket : clients) {
webSocket.sendMsg(jsonStr);
}
}
@OnError
public void onError(Throwable e) {
System.err.println("WebSocket连接失败!");
}
@OnMessage
//群聊:{"msg":"777","type":1}
//私聊:{"to":"0-","msg":"33333","type":2}
public void onMessage(String msg) {
// 将msg -> MessageFromClient
MessageFromClient messageFromClient = (MessageFromClient) CommUtils
.json2Object(msg,MessageFromClient.class);
if (messageFromClient.getType().equals("1")) {
// 群聊信息
String content = messageFromClient.getMsg();
Message2Client message2Client = new Message2Client();
message2Client.setContent(content);
message2Client.setNames(names);
// 广播发送
for (WebSocket webSocket : clients) {
webSocket.sendMsg(CommUtils.object2Json(message2Client));
}
}else if (messageFromClient.getType().equals("2")) {
// 私聊信息
// {"to":"0-1-2-","msg":"33333","type":2}
// 私聊内容
String content = messageFromClient.getMsg();
int toL = messageFromClient.getTo().length();
String tos[] = messageFromClient.getTo()
.substring(0,toL-1).split("-");
List<String> lists = Arrays.asList(tos);
// 给指定的SessionID发送信息
for (WebSocket webSocket : clients) {
if (lists.contains(webSocket.session.getId()) &&
this.session.getId() != webSocket.session.getId()) {
// 发送私聊信息
Message2Client message2Client = new Message2Client();
message2Client.setContent(userName,content);
message2Client.setNames(names);
webSocket.sendMsg(CommUtils.object2Json(message2Client));
}
}
}
}
@OnClose
public void onClose() {
// 将客户端聊天实体移除
clients.remove(this);
// 将当前用户以及SessionID保存到用户列表
names.remove(session.getId());
System.out.println("有连接下线了"+
",用户名为"+userName);
// 发送给所有在线用户一个下线通知
Message2Client message2Client = new Message2Client();
message2Client.setContent(userName+"下线了!");
message2Client.setNames(names);
// 发送信息
String jsonStr = CommUtils.object2Json(message2Client);
for (WebSocket webSocket : clients) {
webSocket.sendMsg(jsonStr);
}
}
public void sendMsg(String msg) {
try {
this.session.getBasicRemote().sendText(msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
- config层:
package com.bittech.java7.chatroom.config;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import freemarker.template.Configuration;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* @Description:
*/
@WebListener
public class FreeMarkerListener implements ServletContextListener {
public static final String TEMPLATE_KEY = "_template_";
@Override
public void contextInitialized(ServletContextEvent sce) {
// 配置版本
Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
// 配置加载ftl的路径
try {
cfg.setDirectoryForTemplateLoading(
new File("/Users/liuyiming/Documents/IDEA_Project/java7_chatroom_websocket/src/main/webapp"));
} catch (IOException e) {
e.printStackTrace();
}
// 配置页面编码
cfg.setDefaultEncoding(StandardCharsets.UTF_8.displayName());
sce.getServletContext().setAttribute(TEMPLATE_KEY,cfg);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
用户界面设计
- chat.ftl
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="UTF-8">
<meta http-equiv="Access-Control-Allow-Origin" content="*">
<title>java7_chatroom</title>
<link rel="stylesheet" href="assets/css/bootstrap.css"/>
<link rel="stylesheet" href="assets/css/font-awesome.min.css"/>
<link rel="stylesheet" href="assets/css/build.css"/>
<link rel="stylesheet" type="text/css" href="assets/css/qq.css"/>
</head>
<body>
<div class="qqBox">
<div class="context">
<div class="conLeft">
<ul id="userList">
</ul>
</div>
<div class="conRight">
<div class="Righthead">
<div class="headName">${username}</div>
</div>
<div class="RightCont">
<ul class="newsList" id="message">
</ul>
</div>
<div class="RightMiddle">
<div class="file">
<form id="form_photo" method="post" enctype="multipart/form-data"
style="width:auto;">
<input type="file" name="filename" id="filename" onchange="fileSelected();"
style="display:none;">
</form>
</div>
</div>
<div class="RightFoot">
<textarea id="dope"
style="width: 100%;height: 100%; border: none;outline: none;padding:20px 0 0 3%;" name=""
rows="" cols=""></textarea>
<button id="fasong" class="sendBtn" onclick="send()" style="border-radius: 5px">发送</button>
</div>
</div>
</div>
</div>
<script src="assets/js/jquery_min.js"></script>
<script src="https://cdn.bootcss.com/jquery.form/4.2.2/jquery.form.min.js"></script>
<script type="text/javascript">
var webSocket = null;
//判断当前浏览器是否支持WebSocket
if ('WebSocket' in window) {
webSocket = new WebSocket('ws://127.0.0.1:8080/websocket?username=' + '${username}');
} else {
alert("当前浏览器不支持WebSocket");
}
//连接发生错误的回调方法
webSocket.onerror = function () {
setMessageInnerHTML("WebSocket连接发生错误!");
};
webSocket.onopen = function () {
setMessageInnerHTML("WebSocket连接成功!")
};
webSocket.onmessage = function (event) {
$("#userList").html("");
eval("var msg=" + event.data + ";");
if (undefined != msg.content)
setMessageInnerHTML(msg.content);
if (undefined != msg.names) {
$.each(msg.names, function (key, value) {
var htmlstr = '<li>'
+ '<div class="checkbox checkbox-success checkbox-inline">'
+ '<input type="checkbox" class="styled" id="' + key + '" value="' + key + '" checked>'
+ '<label for="' + key + '"></label>'
+ '</div>'
+ '<div class="liLeft"><img src="assets/img/robot2.jpg"/></div>'
+ '<div class="liRight">'
+ '<span class="intername">' + value + '</span>'
+ '</div>'
+ '</li>'
$("#userList").append(htmlstr);
})
}
};
webSocket.onclose = function () {
setMessageInnerHTML("WebSocket连接关闭");
};
window.onbeforeunload = function () {
closeWebSocket();
};
function closeWebSocket() {
webSocket.close();
}
function send() {
var time = new Date().toLocaleString();
var message = $("#dope").val();
$("#dope").val("");
//发送消息
var htmlstr = '<li><div class="answerHead"><img src="assets/img/2.png"></div><div class="answers">'
+ '[本人]' + ' ' + time + '<br/>' + message + '</div></li>';
webSocketSend(htmlstr,message,"");
};
function webSocketSend(htmlstr,message,re){
$("#message").append(htmlstr);
var ss = $("#userList :checked");
var to = "";
$.each(ss, function (key, value) {
to += value.getAttribute("value") + "-";
});
console.info(to);
if (ss.size() == 0) {
var obj = {
msg: message,
type: 1
}
} else {
var obj = {
to: to,
msg: message,
type: 2
}
}
var msg = JSON.stringify(obj);
webSocket.send(msg);
if(re){
$("#jsonImg").attr("src", string.data);
// loadDiv(re);
}
}
//回车键发送消息
$(document).keypress(function (e) {
if ((e.keyCode || e.which) == 13) {
$("#fasong").click();
}
});
//局部刷新div
function loadDiv(sJ) {
$("#delayImgPer").html('<img src="'+sJ+'" class="delayImg" >');
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML) {
var msg = '<li>'
+ '<div class="nesHead">'
+ '<img src="assets/img/robot.jpg">'
+ ' </div>'
+ '<div class="news">'
+ innerHTML
+ '</div>'
+ '</li>';
$("#message").append(msg);
}
</script>
</body>
</html>
- index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>log-in</title>
<link rel="stylesheet" href="assets/css/common_form.css"/>
<script src="assets/js/common_form_test.js"></script>
</head>
<body>
<header>
<div class="header-line"></div>
</header>
<div class="content">
<img class="content-logo" src="/assets/img/robot.jpg" alt="logo">
<h1 class="content-title">登录</h1>
<div class="content-form">
<form method="post" action="/login" onsubmit="return submitTest()">
<div id="change_margin_1">
<input class="username" type="text" name="username" placeholder="请输入用户名" onblur="oBlur_1()" onfocus="oFocus_1()">
</div>
<!-- input的value为空时弹出提醒 -->
<p id="remind_1"></p>
<div id="change_margin_2">
<input class="password" type="password" name="password" placeholder="请输入密码" onblur="oBlur_2()" onfocus="oFocus_2()">
</div>
<!-- input的value为空时弹出提醒 -->
<p id="remind_2"></p>
<div id="change_margin_3">
<input class="content-form-signup" type="submit" onclick="#" value="登录">
</div>
</form>
</div>
<div class="content-login-description">没有账户?</div>
<div><a class="content-login-link" href="/registration.html">注册</a></div>
<div style="height: 60px; width: 200px;margin: auto;margin-top: 40px;" >
<li><a href="#" target="_blank"><span style="color: dodgerblue; font-size: 18px;">MyBlog,欢迎骚扰!</span></a></li>
<li><a href="#" target="_blank"><span style="color: dodgerblue; font-size: 18px;">Github,欢迎骚扰!</span></a></li>
</div>
</div>
</body>
</html>
- registration.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>sign-up</title>
<link rel="stylesheet" href="assets/css/common_form.css">
</head>
<body>
<header>
<div class="header-line"></div>
</header>
<div class="content">
<img class="content-logo" src="assets/img/robot.jpg" alt="logo">
<h1 class="content-title">创建账户</h1>
<div class="content-form">
<form method="post" action="/doRegister" onsubmit="return submitTest()">
<div id="change_margin_1">
<input class="username" type="text" name="username" placeholder="请输入用户名" onblur="oBlur_1()" onfocus="oFocus_1()">
</div>
<!-- input的value为空时弹出提醒 -->
<p id="remind_1"></p>
<div id="change_margin_2">
<input class="password" type="password" name="password" placeholder="请输入密码" onblur="oBlur_2()" onfocus="oFocus_2()">
</div>
<!-- input的value为空时弹出提醒 -->
<p id="remind_2"></p>
<div id="change_margin_3">
<input class="content-form-signup" type="submit" value="创建账户">
</div>
</form>
</div>
<div class="content-login-description">已经拥有账户?</div>
<div><a class="content-login-link" href="/index.html">登录</a></div>
</div>
<div style="height: 60px; width: 200px;margin: auto;margin-top: 40px;" >
<li><a href="#" target="_blank"><span style="color: dodgerblue; font-size: 18px;">MyBlog,欢迎骚扰!</span></a></li>
<li><a href="#" target="_blank"><span style="color: dodgerblue; font-size: 18px;">Github,欢迎骚扰!</span></a></li>
</div>
<script src="assets/js/common_form_test.js"></script>
</body>
</html>
- websocketTest.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>websocket demo</title>
</head>
<body>
请输入要发送的信息:
<input type="text" id="text">
<button onclick="sendMsg2Server()">发送信息</button>
<hr>
收到服务端信息为:
<div id="read_from_server"></div>
<hr>
<button onclick="closeWebSocket()">关闭websocket</button>
<script>
var websocket = null;
if ('WebSocket' in window) {
console.log("支持webcoket!");
// 后端websocket地址
websocket = new WebSocket("ws://localhost:8080/websocket");
}else {
alert("浏览器不支持websocket!");
}
// 浏览器与服务端建立链接后回调方法
websocket.onopen = function() {
console.log("websocket连接成功");
};
// 建立websocket失败
websocket.onerror = function() {
console.log("websocket连接失败");
};
// 浏览器收到服务器信息
websocket.onmessage = function (event) {
var msg = event.data;
flushDiv(msg);
};
// websocket关闭
websocket.onclose = function () {
closeWebSocket();
};
// 窗口关闭,主动将当前窗口websocket关闭
window.onbeforeunload = function () {
closeWebSocket();
};
// 将浏览器信息发送到服务端
function sendMsg2Server() {
var msg = document.getElementById("text").value;
websocket.send(msg);
}
// 刷新当前div
function flushDiv(msg) {
document.getElementById("read_from_server").innerText = msg;
}
function closeWebSocket() {
websocket.close();
}
</script>
</body>
</html>