JSP 模拟简单的聊天室

eclipse 创建一个Dynamic Web Project, SmallChat

提示:注意勾选 web.xml

JSP 模拟简单的聊天室_第1张图片
2010jing.png

构建两个简单的页面登录页面 和 聊天页面

登录页面 index.html




    
    模拟简单的聊天室
    
    
    


    

User Signin

username:

password:



Connecting...

登录页面 index.css

#divLogin{
    margin: 100px auto 100px auto;
    width: 500px;
}

.content{
    margin: 10px auto 10px auto;
    width:300px;
}

.btnCenter{
    margin: 10px auto 10px auto;
    width:150px;
}

index.html 页面效果

JSP 模拟简单的聊天室_第2张图片
2010jing.png

聊天页面 chat.html




    Chat Room
    
    
    
    


    

Chat Room

Online Member

Content Cannot Be Empty
Sending Data...

聊天页面 chat.css

body
{
    font-size:11px
}
h3
{
    margin:0px
}
.divShow
{
    border:solid 1px #ccc;
    height:300px;
    padding:5px;
    font-size:12px;
    overflow-y:scroll
}
#divMain
{
    border:solid 5px #ccc
} 
#divMain .divtop
{
    padding:10px
}
#divMain .divtop .divL
{
    float:left;
    width:78%
}
#divMain .divtop .divR
{
    float:right;
    width:20%
} 
#divMain .divBot
{
    clear:both;
    padding:10px
}
#divMain .divBot .pb
{
    padding-bottom:3px
}
#divMain .divBot .pl
{
    padding-left:12px
}
#divMain .divBot .pt
{
    padding-top:3px;
    color:#555
}
.clsTip
{
    position:absolute;
    width:160px;
    text-align:center;
    font-size:13px;
    border:solid 1px #cc3300;
    margin-top:5px;
    padding:2px;
    margin-bottom:5px;
    background-color:#ffe0a3;
    display:none;
 }
 .upload
 {
 display:none;
 }
.txt
{
    border:#666 1px solid;
    padding:2px;
    margin-right:3px
 }
.btn 
{
    border:#666 1px solid;
    padding:2px;
    width:135px;
    height:54px; 
    font-size:16px;
    filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#ffffff, EndColorStr=#ECE9D8)
}

chat.html 页面效果

JSP 模拟简单的聊天室_第3张图片
2010jing.png

模拟登陆

这里并没有真正的注册用户,只是模拟登陆。如果成功则跳转到聊天室页面,否则弹出警告信息。

首先,我们需要用ajax来实现提交表单的行为。
我们在 WebContent下的 js 文件夹内创建一个index.js文件

function UserLogin(name,pass){
    $.ajax({
        type:"POST",
        url:"chat",  //项目web.xml 内配置了 servlet
        data:"action=Login&d="+new Date()+"&name="+name+"&pass="+pass,
        success:function(msg){
            if(msg == "ok"){
                window.location.href="chat.html";
            }else{
                alert("Information Wrong!");
                return false;
            }
        }
    });
}


$(function(){
    $("#divMsg").ajaxStart(function(){
        $(this).show();
    })
    $("#divMsg").ajaxStop(function(){
        $(this).html("Success!").hide();
    })
    $("#Login").click(function(){
        var $name=$("#txtName");
        var $pass=$("#txtPass");
        if($name.val()!="" && $pass.val()!=""){

            UserLogin($name.val(),$pass.val());

        }else{
            if($name.val()==""){
                alert("username is empty");
                $name.focus();
                return false;
            }else{
                alert("password is empty");
                $pass.focus();
                return false;
            }
        }
    })
});

定义方法 UserLogin(name,pass);
提交类型 POST
请求url 是 ‘chat’, 后面会解释这个,
data 数据通过 "action=Login&d="+new Date()+"&name="+name+"&pass="+pass 传递
当成功返回时候,返回一个msg 值,这里我设置返回一个字符串, no / ok 根据情况来跳转页面 或者 弹出错误信息。

接下来的代码是 监测ajax 开始以及结束后, 对 "#divMsg" 部门进行一个状态的更改。 用到 ajaxStart 和 ajaxStop。

对登录页面 登录按钮设置了一个id "#Login", 并绑定了一个 click的事件,当点击时候,获取账号和密码的值,进行简单的校验,当不为空的时候,就调用 UserLogin(name,pass) 该方法进行登录操作。

ChatRoomServlet

在src 内建立一个package cn.crabshell.servlet 专门放置servlet的文件
然后,建立一个servlet 命名为 ChatRoomServlet

Servlet接口SUN公司定义了两个默认实现类,分别为:GenericServlet、HttpServlet。

HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。
  HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。

本例子纯粹是学习,于是打算还是尝试下覆写service方法,不直接调用doGet 和 doPost。如果感兴趣,读者完全可以自己在 doGet 或者 doPost 内完成以下的操作,只是稍作修改。

于是有如下代码

package cn.crabshell.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;

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 javax.servlet.http.HttpSession;

/**
 * Servlet implementation class ChatRoomServlet
 */
@WebServlet("/ChatRoomServlet")
public class ChatRoomServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    
    public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{
        //get user action , name, pass, content
        String strAction = request.getParameter("action");
        String strName = request.getParameter("name");
        String strPass = request.getParameter("pass");
        HttpSession session = request.getSession(); 
        response.setContentType("text/html; charset=utf-8"); 
        PrintWriter out = response.getWriter();  
        //base on action to do things
        if("Login".equals(strAction)){
            String res=UserLogin(strName,strPass,session);
            out.print(res);
            out.close();
        }
    }

    //check user login
    public String UserLogin(String strName,String strPass,HttpSession session){
            String flag = "no";
            if (strPass.equals("111"))//check pass
            {
               
                session.setAttribute("LOGINUSER",strName);//session to set user info
                flag = "ok";
            }
            return flag;
    }

}

service(HttpServletRequest request, HttpServletResponse response)
在servlet中默认情况下,无论你是get还是post 提交过来 都会经过service()方法来处理,然后转向到doGet 或是doPost方法。

strAction 获取到 action 的值
strName 获取到 name 的值
strPass 获取到 pass 的值
session 获取 session

if语句判断如果action的值是Login, 那么久会调用
UserLogin(String strName,String strPass,HttpSession session) 方法。
该方法返回一个字符串,主要作用只是判断用户输入的密码是不是111。
如果是匹配,则session 设置值,session.setAttribute("LOGINUSER",strName);

就是那么简单的的一个servlet。

回到上面的问题,在index.js 内,ajax 内有个url :"chat" 到底是什么意思?
这个 chat 是对 ChatRoomServlet 的一个mapping.

打开 web.xml
内添加


    ChatRoomServlet
    cn.crabshell.servlet.ChatRoomServlet
  
  
    ChatRoomServlet
    /chat
  

注意servlet-name 必须一致

到此... 测试一下 ,用户名随意输入,密码输入111,是可以成功跳转到chat.html页面, 密码错误的话则会弹出警告信息。

密码错误时候:


JSP 模拟简单的聊天室_第4张图片
2010jing.png

成功之后跳转到chart.html页面

JSP 模拟简单的聊天室_第5张图片
2010jing.png

接下来就是来实现,用户聊天部分。
准备了几个表情图片,放置于 Face文件夹内

JSP 模拟简单的聊天室_第6张图片
2010jing.png

在chat.js 内添加方法,用于加载表情文件。

//face icons 
function InitFace() {
    var strHTML = "";
    for (var i = 1; i <= 10; i++) {
        strHTML += "![](Face/" + i + ".gif )";
    }
    $("#divFace").html(strHTML);
}

该方法通过一个for循环,将十个表情图片,构造成一个html字符串,再通过$("#divFace").html(strHTML); 插入在 id 为 "divFace"的地方。

给表情绑定click方法。

$(function() {
    
    InitFace();

    $("table tr td img").click(function() { //表情图标单击事件
        var strContent = $("#txtContent").val() + "<:" + this.id + ":>";
        $("#txtContent").val(strContent);

    })
});



//face icons 
function InitFace() {
    var strHTML = "";
    for (var i = 1; i <= 10; i++) {
        strHTML += "![](Face/" + i + ".gif )";
    }
    $("#divFace").html(strHTML);
}

当用户点击表情的时候, 就会将表情追加到输入框内容后面。

JSP 模拟简单的聊天室_第7张图片
2010jing.png

定义获取信息的函数 GetMessageList()

//get message content
//data is the message content data
function GetMessageList() {
    $.ajax({
        type: "POST",
        url: "chat",
        data: "action=ChatList&d=" + new Date(),
        success: function(data) {
            $("#divContent").html(data);
        }
    });
    AutoUpdContent(); 
}

定义获取在线人员的函数 GetOnLineList()

//get online user list
//data is the online user data
function  GetOnLineList() {
    $.ajax({
        type: "POST",
        url: "chat",
        data: "action=OnLineList",
        success: function(data) {
            $("#divOnLine").html(data);
        }
    });
    AutoUpdContent(); 
}

AutoUpdContent() 定时触发 以上两个方法获取信息和在线人员

//auto update member and message
function AutoUpdContent() {
    setTimeout(GetMessageList, 5000);
    setTimeout(GetOnLineList, 5000);
}

写到这里,回到 ChatRoomServlet 继续完善代码。

List OnLineUserList = new ArrayList(); 
static List strSendConentList=new ArrayList();

这两个ArrayList 分别存在线用户 和 聊天信息。

AllChatList() 获取聊天记录

    //get chat content
    public String AllChatList(){
        String result="";
        if (strSendConentList.size() == 0)
        {
           result = "日前还没有找到聊天记录";
        }
        else
        {
            Iterator it=strSendConentList.iterator();
            while(it.hasNext()){
                result += it.next() + "
"; } } result= result.replace("<:", ""); return result; }

GetOnlineUserList(HttpSession session) 获取在线用户

//user list
    public String GetOnlineUserList(HttpSession session){
        String result="";
            if (OnLineUserList.size()==0)
            {
                    result="暂时没有人在线";
            }else{
            Iterator it=OnLineUserList.iterator();
            while(it.hasNext()){
                result += it.next() + "
"; } } return result; }

AddSendContent(String strContent,HttpSession session) 发送信息

//发送信息
    public Boolean AddSendContent(String strContent,HttpSession session){
            String user=(String) session.getAttribute("LOGINUSER");
            
            //String name = session.getAttribute("LOGINUSER").toString();
            if(null==user){
                return false;
            }
            String strSendConent = user + " 于 " + new java.util.Date(System.currentTimeMillis()) + " 说: " + strContent;
            if (strSendConentList.size() == 0)
            {
                strSendConentList = new ArrayList();
            }
            strSendConentList.add(strSendConent);
            return true;
    }

Logout(HttpSession session) 退出

    public boolean Logout(HttpSession session){
        if(null==session.getAttribute("LOGINUSER")){
            return false;
        }
        String name = session.getAttribute("LOGINUSER").toString();
        session.removeAttribute("LOGINUSER");
        if(OnLineUserList.size()!=0){
        for(int i=0;i

service() 内 和 之前的Login 一样的套路。

//base on action to do things
        if("Login".equals(strAction)){
            String res=UserLogin(strName,strPass,session);
            out.print(res);
            out.close();

        }else if("ChatList".equals(strAction)){
            String result1=AllChatList();
            out.println(result1);  
            out.close();

        }else if("OnLineList".equals(strAction)){
            String result2=GetOnlineUserList(session);  
            out.println(result2);  
            out.close();

        }else if("SendContent".equals(strAction)){
            Boolean res2=AddSendContent(strContent,session);
            String result="";
            if(res2){
              result="1";
            }else{
              result="2";
            }
            out.println(result);  
            out.close();

        }else if("Logout".equals(strAction)){
            boolean res3=Logout(session);
            out.println(res3);  
            out.close();
        }

最后完整的 ChatRoomServlet

package cn.crabshell.servlet;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

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 javax.servlet.http.HttpSession;

/**
 * Servlet implementation class ChatRoomServlet
 */
@WebServlet("/ChatRoomServlet")
public class ChatRoomServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;
    List OnLineUserList = new ArrayList(); 
    static List strSendConentList=new ArrayList();
    
    public void service(HttpServletRequest request, HttpServletResponse response)throws ServletException,IOException{
        //get user action , name, pass, content
        String strAction = request.getParameter("action");
        String strName = request.getParameter("name");
        String strPass = request.getParameter("pass");
          String strContent = request.getParameter("content");
        HttpSession session = request.getSession(); 
        response.setContentType("text/html; charset=utf-8"); 
        PrintWriter out = response.getWriter();  
        //base on action to do things
        if("Login".equals(strAction)){
            String res=UserLogin(strName,strPass,session);
            out.print(res);
            out.close();

        }else if("ChatList".equals(strAction)){
            String result1=AllChatList();
            out.println(result1);  
            out.close();

        }else if("OnLineList".equals(strAction)){
            String result2=GetOnlineUserList(session);  
            out.println(result2);  
            out.close();

        }else if("SendContent".equals(strAction)){
            Boolean res2=AddSendContent(strContent,session);
            String result="";
            if(res2){
              result="1";
            }else{
              result="2";
            }
            out.println(result);  
            out.close();

        }else if("Logout".equals(strAction)){
            boolean res3=Logout(session);
            out.println(res3);  
            out.close();
        }
    }

    //check user login
    public String UserLogin(String strName,String strPass,HttpSession session){
            String flag = "no";
            if (strPass.equals("111"))//check pass
            {
                if (OnLineUserList.size() == 0)//check use if empty
                {
                    OnLineUserList = new ArrayList();// in use is empty , init the arraylist
                }
                OnLineUserList.add(strName);//add use to the arrayList
                session.setAttribute("LOGINUSER",strName);//session to set user info
                flag = "ok";
            }
            return flag;
    }

    //get chat content
    public String AllChatList(){
        String result="";
        if (strSendConentList.size() == 0)
        {
           result = "日前还没有找到聊天记录";
        }
        else
        {
            Iterator it=strSendConentList.iterator();
            while(it.hasNext()){
                result += it.next() + "
"; } } result= result.replace("<:", ""); return result; } //user list public String GetOnlineUserList(HttpSession session){ String result=""; if (OnLineUserList.size()==0) { result="暂时没有人在线"; }else{ Iterator it=OnLineUserList.iterator(); while(it.hasNext()){ result += it.next() + "
"; } } return result; } //send message public Boolean AddSendContent(String strContent,HttpSession session){ String user=(String) session.getAttribute("LOGINUSER"); //String name = session.getAttribute("LOGINUSER").toString(); if(null==user){ return false; } String strSendConent = user + " 于 " + new java.util.Date(System.currentTimeMillis()) + " 说: " + strContent; if (strSendConentList.size() == 0) { strSendConentList = new ArrayList(); } strSendConentList.add(strSendConent); return true; } //logout public boolean Logout(HttpSession session){ if(null==session.getAttribute("LOGINUSER")){ return false; } String name = session.getAttribute("LOGINUSER").toString(); session.removeAttribute("LOGINUSER"); if(OnLineUserList.size()!=0){ for(int i=0;i

回到 chat.js 文件
定义 发送内容方法 SendContent(content)

//send message 
function SendContent(content) {
    $.ajax({
        type: "POST",
        url: "chat",
        data: "action=SendContent&d=" + new Date() + "&content=" + content,
        success: function(data) {
            //alert(data);
            if (data==1) {
                GetMessageList();
                $("#txtContent").val("");
            }else {
                GetMessageList();
                alert("请先登录!");
                window.location.href="Login.html";
            }
        }
    });
}

对"#Button1" 绑定click事件

$(function() {

    InitFace();
    GetMessageList();
    GetOnLineList();

    $("#Button1").click(function() { //按钮点击事件
        var $content = $("#txtContent"); //发送内容
        if ($content.val() != "") {
            SendContent($content.val());
        }
        else {
            alert("发送不能为空!");
            $content.focus();
            return false;
        }
    })

    $("table tr td img").click(function() { //表情图标单击事件
        var strContent = $("#txtContent").val() + "<:" + this.id + ":>";
        $("#txtContent").val(strContent);

    })
});

验证一下,重新发布一次项目。
打开 http://localhost:8080/SmallChat/index.html
输入2010jing 和密码 111

JSP 模拟简单的聊天室_第8张图片
2010jing.png

右边可以看到 在线用户。
2010jing 发布一条消息

JSP 模拟简单的聊天室_第9张图片
2010jing.png

再登录一个用户试试看。
新开一个tab
打开 http://localhost:8080/SmallChat/index.html
输入2010abby 和密码 111

JSP 模拟简单的聊天室_第10张图片
2010jing.png

用 2010abby 账号发布一条消息。

JSP 模拟简单的聊天室_第11张图片
2010jing.png

退出功能

$("#Button2").click(function() { //logout
        $.ajax({
            type: "POST",
            url: "chat",
            data: "action=Logout&d=" + new Date(),
            success: function(data) {
                if (data) {
                    window.location.href="index.html";
                }
                else {
                    alert("退出失败!");
                    return false;
                }
            }
        });
    })

似乎已经完成大部分功能了。
剩下就是添加附件的功能。

附件功能需要用到 jquery.uploadify.min.js 插件
使用方法
首先chat.html 引入 相关的js 和 css


    Chat Room
    
    
      
    
      
    
    


简要说明

属性
名称 默认值 说明
auto true 设置为true当选择文件后就直接上传了,为false需要点击上传按钮才上传 。
swf ‘uploadify.swf’ uploadify.swf 文件的相对路径。
uploader uploadify 后台处理程序的相对路径。
queueID false 设置上传队列容器DOM元素的ID,如果为false则自动生成一个队列容器。
queueSizeLimit 1 队列最多显示的任务数量,如果选择的文件数量超出此限制,将会出发onSelectError事件。
注意此项并非最大文件上传数量,如果要限制最大上传文件数量,应设置uploadLimit。
fileTypeDesc ‘All Files’ 这个属性值必须设置fileTypeExts属性后才有效,用来设置选择文件对话框中的提示文本,如设置fileTypeDesc为“请选择rar doc pdf文件”
fileTypeExts ‘.’ 设置可以选择的文件的类型,格式如:’.doc;.pdf;*.rar’ 。
multi true 设置为true时可以上传多个文件。
buttonText ‘SELECT FILES’ 浏览按钮的文本。

方法
upload(fileID) 立即上传指定的文件,如果fileID为’*'表示上传所有文件,要指定上传多个文件,则将每个文件的fileID作为一个参数 开始上传所有文件


取消队列中的任务,不管此任务是否已经开始上传

fileID – 要取消的文件ID,如果为空则取消队列中第一个任务,如果为’*'则取消所有任务
suppressEvent – 是否阻止触发onUploadCancel事件,当清空队列时非常实用。

取消第一个

清空队列

开始上传所有任务

更多请前往官网查看资料 Uploadify

UploadServlet

package cn.crabshell.servlet;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.fileupload.util.Streams;


public class UploadServlet extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * 获取get请求
     */
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        HttpSession session = request.getSession();
        //将接受内容以utf-8接收
        request.setCharacterEncoding("UTF-8");
        Date date = new Date();// 日期
        SimpleDateFormat sdfFileName = new SimpleDateFormat("yyyyMMddHHmmss");
        //SimpleDateFormat sdfFolder = new SimpleDateFormat("yyMM");
        String newfileName = sdfFileName.format(date);//格式化日期
        String fileRealPath = "";//真实文件路径

        String fileRealResistPath = "";//保存文件路径
        if(null==session.getAttribute("LOGINUSER")){
            return;
        }
        // 获取用户
        //String name = session.getAttribute("LOGINUSER").toString();
        String firstFileName = "";
        //保存路径
        //String savePath = this.getServletConfig().getServletContext().getRealPath("/")+ "uploads\\" + newfileName + "\\";
        
        String savePath = "C:\\home\\workshop\\jsp"+"\\uploads\\" + newfileName + "\\";
        System.out.println(savePath);
        File file = new File(savePath);
        if (!file.isDirectory()) {
            file.mkdirs();
        }

        try {
            DiskFileItemFactory fac = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(fac);
            upload.setHeaderEncoding("UTF-8");

            //获取文件列表
            List fileList = upload.parseRequest(request);
            //遍历文件arrayList
            Iterator it = fileList.iterator();
            while (it.hasNext()) {
                Object obit = it.next();
                if (obit instanceof DiskFileItem) {
                    DiskFileItem item = (DiskFileItem) obit;

                    //获取文件名
                    String fileName = item.getName();
                    if (fileName != null) {
                        firstFileName = item.getName().substring(
                                item.getName().lastIndexOf("\\") + 1);
                        String formatName = firstFileName
                                .substring(firstFileName.lastIndexOf("."));//获取文件名
                        fileRealPath = savePath + newfileName + formatName;//保存的真实文件路径

                        BufferedInputStream in = new BufferedInputStream(
                                item.getInputStream());//读取文件流
                        BufferedOutputStream outStream = new BufferedOutputStream(
                                new FileOutputStream(new File(fileRealPath)));//
                        Streams.copy(in, outStream, true);// 文件流复制
                        //写入文件
                        if (new File(fileRealPath).exists()) {
                            //
                            fileRealResistPath = fileRealPath
                                    .substring(fileRealPath.lastIndexOf("\\") + 1);
                            //获取当前用户
                            String user = session.getAttribute("LOGINUSER")
                                    .toString();

                            //截取文件的后缀
                            String FileExt = fileName
                                    .substring(fileName.lastIndexOf("."));
                            //用户发送文件的信息
                            String str=user+"发送了文件:"+""+fileName+"";
                            ChatRoomServlet.strSendConentList.add(str);
                        }

                    }
                }
            }
        } catch (FileUploadException ex) {
            ex.printStackTrace();
            response.getWriter().write("Upload error!");
            return;
        }
        response.getWriter().write("Upload success!");

    }

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        doGet(req, resp);
    }
}

上传文件需要两个Jar包
commons-fileupload-1.2.1.jar
commons-io-1.3.2.jar
别忘了在 web.xml 配置 UploadServlet

最后测试传输文件

JSP 模拟简单的聊天室_第12张图片
2010jing.png
JSP 模拟简单的聊天室_第13张图片
2010jing.png
JSP 模拟简单的聊天室_第14张图片
2010jing.png

最后完工!

你可能感兴趣的:(JSP 模拟简单的聊天室)