java写的简单的HTTP服务器,firefox下访问时会提示连接被重置

wuliuxu 2013-06-17 02:31:01
使用java实现的HTTP服务器响应功能。但是在通过firefox反复刷新访问localhost:8416时,有时可以得到正常的响应,但更多的时候无法得到正常响应,浏览器显示连接被重置

代码如下:

首先是main函数:
public static void main(String[] args) {
int port = 8416;
int backlog = 500;
int acceptCount = 0;
Socket serverSocket = null;

try {
ServerSocket httpServer = new ServerSocket(port, backlog);
System.out.println("server:httpy");

while(true){
// 从等待队列中取出一个连接请求,建立socket连接
serverSocket = httpServer.accept();
acceptCount ++;

// 对每次客户端的请求都使用一个独立的线程来响应
HttpResponse response = new HttpResponse(serverSocket);
Thread thread = new Thread(response);
thread.start();

// 打出服务端已经接受并处理请求数
System.out.println(acceptCount);
}
} catch (IOException e) {
e.printStackTrace();
}
}

然后是服务端进行响应的线程类(HttpResponse):
class HttpResponse implements Runnable{
// 与客户端进行连接的socket
Socket serverSocket = null;
// 回车换行
String crlf = "\r\n";
// 服务端进行响应的输出流
BufferedWriter responseWriter = null;
BufferedReader br = null;

HttpResponse(Socket _socket){
serverSocket = _socket;
}

public void run(){
response();
}

/**
* 对客户端请求进行响应的处理方法
*
* @param none
* @return none
*/
public void response(){
try{
responseWriter =
new BufferedWriter(
new OutputStreamWriter(
serverSocket.getOutputStream()));
// 服务端进行响应的响应内容
StringBuffer content = new StringBuffer();
content.append("<html>");
content.append("<head>");
content.append("<title>");
content.append("httpy web server");
content.append("</title>");
content.append("</head>");
content.append("<body>");
content.append("<div>");
content.append("<a>server:httpy</a>");
content.append("</div>");
content.append("</body>");
content.append("</html>");

// 服务端进行响应的响应头
StringBuffer header = new StringBuffer();
header.append("HTTP/1.1 200 OK");
header.append(crlf);
header.append("Content-Length:");
header.append(content.toString().getBytes("utf-8").length);
header.append(crlf);
header.append("Content-Type:text/html;UTF-8");
header.append(crlf);
header.append(crlf);

// 输出响应头
responseWriter.write(header.toString());
// 输出响应内容
responseWriter.write(content.toString());

} catch (Exception e) {
e.printStackTrace();
}finally{
// 关闭资源连接
try {
if(responseWriter != null){
responseWriter.close();
}

if(serverSocket != null){
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}


客户端访问
使用浏览器:firefox
访问地址 :localhost:8416

如果反复快速的进行访问(不管是反复回车请求,F5刷新请求,还是Ctrl + F5刷新请求),就会出现,有时浏览器得到正常页面显示,而更多的时候却显示连接被重置,不知道是什么原因导致的。

以下是页面正常显示的效果:


以下是请求异常的时候页面显示的效果:


另外,本人下载了一个java开源的HTTP服务器项目,在本机上测试时一切正常,排除浏览器,系统环境的影响。
...全文
793 19 打赏 收藏 转发到动态 举报
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
wuliuxu 2013-07-21
  • 打赏
  • 举报
回复
今天抽了个时间进行了测试,验证了如下描述:
socket在自身接收缓冲区仍存在数据的情况下关闭时会导致发出RST数据包。
上例中,由于服务端没有对接收缓冲区的数据进行读取,这样,服务端只要关闭socket,就会向客户端发送一个RST包。


至于浏览器为什么有时显示正常,有时又显示未连接被重置。由于本人不清楚浏览器接收到RST包后内部会做何种处理,只能对该现象做出一个可能的推测:
TCP协议规定,RST包可以优先于发送缓冲区的数据包发出,这样,如果服务端在发送缓冲区中的数据还未发出前(发出部分算不算这个我还不能肯定)就关闭了socket,那产生的RST就会立即发送给浏览器端,浏览器端接收到RST肯定也会断开本端的连接,停止接收数据,由于浏览器接收不到服务器响应的数据包,这样就无法显示任何消息,只能给出连接被重置的信息。 但如果服务端RST包在发送给浏览器端之前,服务端发送缓冲区的内容(也就是响应内容)已经全部发到浏览器端的接收缓冲区,这样浏览器已经接收到响应消息就能正常显示网页内容。

wuliuxu 2013-06-24
  • 打赏
  • 举报
回复
这几天做别的事没好好处理这个问题,不过最近看到这样一篇介绍好像能很好的解释我所遇到的问题,链接是:http://my.oschina.net/costaxu/blog/127394,其中介绍的第3中发送RST的情况大概就是我这种情况,大概意思也跟fei1710先前的说法类似。最近事较多没来得及验证,验证主要是解释下面两个问题: 1、服务端发送RST包的情况; 2、浏览器在接到RST包后显示正常与异常的时机问题。 初步解释参考上面链接的第3点,测试待续,测试完毕就结贴..
wuliuxu 2013-06-21
  • 打赏
  • 举报
回复
感谢shnulaa,辛苦做了这么多测试。其实,发帖之前我也考虑过你说的这种原因——服务端在客户端未获取到全部数据前就关闭socket引发异常。但是之后发现这个分析还是有疑点: 1、按照TCP/IP的4次握手原则,主动关闭的一方在发送fin包之后会停止向接收方发送数据,而被动关闭的一方在接到fin包之后也不再从主动关闭的一方那里读取数据,那出现了上面的情况到底是哪个环节出了问题呢? 2、我之前也试过在关闭前让线程sleep一会给客户端留出时间,但是后来一想,服务器在设计的时候真的会在close前sleep一段时间吗,服务器要的就是快速响应,阻塞一段时间的做法就是在降低响应速度,而且,从我下的一个开源的HTTP服务器来看,它的服务端完全没有采取sleep之类的做法。 3、我在7楼提供了一种解决方法,就是在服务端添加一个BufferReader来读取客户端发送的消息,问题就不会出现了,不用sleep,也不用在客户端接受服务端关闭信息。如果是因为服务端提前关闭的原因,上面这种做法是无法掩盖问题的。 当然,shnulaa所作的分析确实揭示了问题的一个现象,后续的分析我觉得需要结合这两种解决方法分析,当然还是感谢各位的解答。 待续..
晓风吹雾 2013-06-21
  • 打赏
  • 举报
回复

String line = null;
while ((line = reader.readLine()) != null) {
    System.out.println(line);
}
改成

String line = null;
while (!(line = reader.readLine()).isEmpty()) {
    System.out.println(line);
}
当接收到空字符串的时候就退出等待
晓风吹雾 2013-06-21
  • 打赏
  • 举报
回复
加几个细节的问题,调试了一下代码,发现reader.readLine()的block分成2个部分,客户端读取第一部分

 POST / HTTP/1.1
 Content-Length: 0
 Host: 192.168.1.83:8416
 Connection: Keep-Alive
的时间节点是在读取服务端的response之前。 之后的第二部分又会block,这次block的release是在客户端的connection关闭后release.也就是在以下代码之后

 if (httpPost != null) {
    httpPost.releaseConnection();
 }
 if (httpClient != null) {
    httpClient.getConnectionManager().shutdown();
 }
这也就保证了服务端的socket in out的关闭是在客户端主动关闭以后服务端才释放资源的。
晓风吹雾 2013-06-21
  • 打赏
  • 举报
回复
这个问题有点意思,所以大致看了一下。 需要分析原因 1 用浏览器不大好分析原因,所以使用httpclient模拟浏览器来进行模拟多用户并发提交 以下是具体的代码,主要是模拟n个线程来提交m个http request.

 import java.io.IOException;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.http.HttpEntity;
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.HttpPost;
 import org.apache.http.client.params.HttpClientParams;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
 import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.HttpConnectionParams;
 import org.apache.http.params.HttpParams;
 import org.apache.http.util.EntityUtils;
 
 public class HttpClientTest {
 
     /**
      * @param args
      * @throws InterruptedException
      */
     public static void main(String[] args) throws InterruptedException {
         ExecutorService service = null;
         try {
             service = Executors.newFixedThreadPool(100);
             final Runnable runnableImpl = new Runnable() {
                 public void run() {
                     System.out.println(Thread.currentThread().getName());
                     postCommunication("http://192.168.1.83:8416/");
                 }
             };
             for (int i = 0; i < 100; i++) {
                 service.submit(runnableImpl);
             }
 
             service.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
         } finally {
             if (service != null) {
                 service.shutdown();
             }
         }
     }
 
     public static final void postCommunication(final String url) {
         DefaultHttpClient httpClient = null;
         HttpPost httpPost = null;
         String bodyMessage = null;
         int statusCode = -1;
         try {
             // create instance httpClient with hTTP parameter
             // set the retry handler
             httpClient = new DefaultHttpClient(setParam());
             httpClient
                     .setHttpRequestRetryHandler(new DefaultHttpRequestRetryHandler(
                             0, false));
             // create post instance
             httpPost = new HttpPost(url);
 
 //            httpPost.addHeader("Connection", "close");
 
             // get the status code and body message.
             HttpResponse response = httpClient.execute(httpPost);
             statusCode = response.getStatusLine().getStatusCode();
             HttpEntity reEntity = response.getEntity();
             if (reEntity != null) {
                 bodyMessage = EntityUtils.toString(reEntity);
             }
         } catch (IOException e) {
             e.printStackTrace();
         } catch (Exception e) {
             e.printStackTrace();
         } finally {
             if (httpPost != null) {
                 httpPost.releaseConnection();
             }
             if (httpClient != null) {
                 httpClient.getConnectionManager().shutdown();
             }
         }
         System.out.println("statusCode:" + statusCode + ",bodyMessage:"
                 + bodyMessage);
     }
 
     private static final HttpParams setParam() {
         final HttpParams httpParams = new BasicHttpParams();
         HttpConnectionParams.setConnectionTimeout(httpParams, 2000);
         HttpConnectionParams.setSoTimeout(httpParams, 2000);
         HttpConnectionParams.setSocketBufferSize(httpParams, 1024 * 2);
         HttpClientParams.setRedirecting(httpParams, true);
 
         return httpParams;
     }
 }
以上的代码模拟100个用户并发执行100个http request. 但是经常会出现问题。 错误为以下
java.net.SocketException: Software caused connection abort: recv failed
这是什么原因呢,那我们就分析一下。 首先看一下出错在什么地方。 我们的代码错误在

 HttpResponse response = httpClient.execute(httpPost);
 
到底在什么情况下出现这种问题,那就看一下httpclinet的源代码。 发现在这种情况下会出现错误。 1 server端

 responseWriter.write(header.toString());
 responseWriter.write(content.toString());
 
以后会立即关闭responseWriter 2 客户端httpclient在read response的时候根本还没有来得及read的时候,因为服务端已经主动关闭了。抛出了IOException httpclient出错的具体位置

  DefaultHttpResponseParser.java
  method parseHead
  int i = sessionBuffer.readLine(this.lineBuf); // 这里出错了,由于服务端的socketoutputstream已经关闭了 这里的sessionBuffer readline也就会抛出io exception.
  
所以说总结一下,出现这种错误的原因是因为当服务端向socket写数据的时候,由于没有等待客户端的读数据操作完成,就关闭了socket的outputstream和socket对象,导致客户端抛出了异常 下面说一下解决方法 解决方法 我想是不是可以在服务端写数据以后,有一个缓冲的时间,让客户端将数据读好呢。所以就先加了一个thread.sleep,这样测试下来,错误明显减少了不少,但是这并不能解决根本的问题 所以想当客户端将response读完以后,肯定对服务端有个回应才对,即告诉服务端我这边已经读取好了,你可以关闭了,所以在再服务端的代码中加入了对socket inputstream的读取,是加载write以后的。代码比较简单,如下

 BufferedWriter responseWriter = null;
 BufferedReader reader = null;
 
 try {
     responseWriter = new BufferedWriter(new OutputStreamWriter(
     socket.getOutputStream()));
     reader = new BufferedReader(new InputStreamReader(
     socket.getInputStream()));
 
     StringBuffer content = new StringBuffer();
     content.append("<html>");
     content.append("<head>");
     content.append("<title>");
     content.append("httpy web server");
     content.append("</title>");
     content.append("</head>");
     content.append("<body>");
     content.append("<div>");
     content.append("<a>server:httpy</a>");
     content.append("</div>");
     content.append("</body>");
     content.append("</html>");
 
     StringBuffer header = new StringBuffer();
     header.append("HTTP/1.1 200 OK");
     header.append(BR);
     header.append("Content-Length:");
     header.append(content.toString().getBytes("utf-8").length);
     header.append(BR);
     header.append("Content-Type:text/html;UTF-8");
     header.append(BR);
     header.append(BR);
 
     responseWriter.write(header.toString());
     responseWriter.write(content.toString());
     
     // 这里的flush也是必须的。
     responseWriter.flush(); 
     // Thread.sleep(10);
     
     // 这里等待客户端读取完response返回通知。
     String line = null;
     while ((line = reader.readLine()) != null) {
          System.out.println(line);
     }
 
 } catch (Exception e) {
     e.printStackTrace();
 } finally {
     try {
 if (responseWriter != null) {
     responseWriter.close();
 }
     } catch (IOException e) {
 e.printStackTrace();
     }
 
     try {
 if (socket != null) {
     socket.close();
 }
     } catch (IOException e) {
 e.printStackTrace();
     }
 }
 
综上所述,修改好以上的代码,1000线程做1000个http request也是没有什么问题的。代码可以优化的地方 thread的地方可以使用线程池,不需要一个request对应一个thread.只需要线程池就可以了。
fei1710 2013-06-19
  • 打赏
  • 举报
回复
引用 9 楼 wuliuxu 的回复:
[quote=引用 8 楼 fei1710 的回复:] [quote=引用 6 楼 wuliuxu 的回复:] [quote=引用 5 楼 fei1710 的回复:] 貌似当接收缓存区还存在数据时关闭socket会导致异常关闭,即发送RST立即关闭TCP连接。 当接收方收到RST时系统会丢弃TCP的发送和接收缓冲区,如果在此之前浏览器进程没有完全接收TCP缓冲区的数据的话,数据就不完整,浏览器就会报错。因为你没读取发送的数据,所以会出现这种情况。
我想确认一下,你说的接受缓存区是指服务端的接收缓存区,还是指客户端(浏览器)的接收缓存区。测试中,如果在服务端添加读取socket接收缓冲区的代码后,问题就消息了,从这个现象上看,有点支持你的观点。但问题是,上面的代码中,服务端根本就没有读取自身接收缓存区的数据,如果是你说的这个原因,那么,测试的时候,应该一次也不会成功,但为什么测试的结果却是有时成功,有时失败呢?[/quote] 这是一个时机问题,如果在收到RST之前已经读取了接收缓冲区的数据就不会报错,否则就会报错。 我测试了一下java下报的是这个错误 java.net.SocketException: Software caused connection abort: recv failed at java.net.SocketInputStream.socketRead0(Native Method)[/quote] 我想再次确认一下,你说的缓冲区(上面加粗加下划线标明)是指服务端的缓冲区还是客户端(浏览器)的缓冲区? [/quote] socket缓冲区
wuliuxu 2013-06-19
  • 打赏
  • 举报
回复
引用 8 楼 fei1710 的回复:
[quote=引用 6 楼 wuliuxu 的回复:] [quote=引用 5 楼 fei1710 的回复:] 貌似当接收缓存区还存在数据时关闭socket会导致异常关闭,即发送RST立即关闭TCP连接。 当接收方收到RST时系统会丢弃TCP的发送和接收缓冲区,如果在此之前浏览器进程没有完全接收TCP缓冲区的数据的话,数据就不完整,浏览器就会报错。因为你没读取发送的数据,所以会出现这种情况。
我想确认一下,你说的接受缓存区是指服务端的接收缓存区,还是指客户端(浏览器)的接收缓存区。测试中,如果在服务端添加读取socket接收缓冲区的代码后,问题就消息了,从这个现象上看,有点支持你的观点。但问题是,上面的代码中,服务端根本就没有读取自身接收缓存区的数据,如果是你说的这个原因,那么,测试的时候,应该一次也不会成功,但为什么测试的结果却是有时成功,有时失败呢?[/quote] 这是一个时机问题,如果在收到RST之前已经读取了接收缓冲区的数据就不会报错,否则就会报错。 我测试了一下java下报的是这个错误 java.net.SocketException: Software caused connection abort: recv failed at java.net.SocketInputStream.socketRead0(Native Method)[/quote] 我想再次确认一下,你说的缓冲区(上面加粗加下划线标明)是指服务端的缓冲区还是客户端(浏览器)的缓冲区?
fei1710 2013-06-19
  • 打赏
  • 举报
回复
引用 6 楼 wuliuxu 的回复:
[quote=引用 5 楼 fei1710 的回复:] 貌似当接收缓存区还存在数据时关闭socket会导致异常关闭,即发送RST立即关闭TCP连接。 当接收方收到RST时系统会丢弃TCP的发送和接收缓冲区,如果在此之前浏览器进程没有完全接收TCP缓冲区的数据的话,数据就不完整,浏览器就会报错。因为你没读取发送的数据,所以会出现这种情况。
我想确认一下,你说的接受缓存区是指服务端的接收缓存区,还是指客户端(浏览器)的接收缓存区。测试中,如果在服务端添加读取socket接收缓冲区的代码后,问题就消息了,从这个现象上看,有点支持你的观点。但问题是,上面的代码中,服务端根本就没有读取自身接收缓存区的数据,如果是你说的这个原因,那么,测试的时候,应该一次也不会成功,但为什么测试的结果却是有时成功,有时失败呢?[/quote] 这是一个时机问题,如果在收到RST之前已经读取了接收缓冲区的数据就不会报错,否则就会报错。 我测试了一下java下报的是这个错误 java.net.SocketException: Software caused connection abort: recv failed at java.net.SocketInputStream.socketRead0(Native Method)
wuliuxu 2013-06-19
  • 打赏
  • 举报
回复
非常感谢u010255083的辛苦测试和解答,提供的博文也非常的有用。结合你的测试和分析来看,好像问题集中到了“客户端为什么会发一个RST报文这点了”,接下来我得再好好考虑这点了。
火影之贺 2013-06-19
  • 打赏
  • 举报
回复
http://blog.csdn.net/sureyonder/article/details/5633647 网上又找了个文章看看,希望对你有帮助!
火影之贺 2013-06-19
  • 打赏
  • 举报
回复
兄弟你的问题太有意思了。我之前测试,确实只是间隔的刷新页面,没有持续的刷新。

这次测试,使用ie按住f5键,后台有对应的错误日志:
938
939
java.net.SocketException: Connection reset by peer: socket write error
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
at sun.nio.cs.StreamEncoder.implClose(StreamEncoder.java:297)
at sun.nio.cs.StreamEncoder.close(StreamEncoder.java:130)
at java.io.OutputStreamWriter.close(OutputStreamWriter.java:216)
at java.io.BufferedWriter.close(BufferedWriter.java:248)
at httpserver.HttpResponse.response(HttpResponse.java:76)
at httpserver.HttpResponse.run(HttpResponse.java:24)
at java.lang.Thread.run(Thread.java:619)
940
941
942

因为ie不断的刷新,所以肉眼比较难分辨出对应这个错误日志时ie展示了什么。

不过我有抓包如下:

请求有167往165发起

图中,从上往下看,是三次请求,在最左边我用三个方框标了出来。
第一次正常的,第二次异常,第三次正常(就没给出完整的截图)。

因为http基于tcp,所以每个http报文前肯定要经过tcp三次握手,http结束后要有4个tcp断开的报文(暂时不关心)。图中小圆形标出来的,就是http之前的三次握手。最后一列椭圆形就是握手过程的几个关键flag。

再看第一个小圆形下方紧跟着的http请求和响应,就是HTTP GET和HTTP 200OK。可见第一个请求是正常的。

看第二个小圆形下方紧跟着的http请求,只有HTTP GET,没见到200 OK。14405这一行,我用一个椭圆和一个长方形标了出来,其中有个RST报文。
网上摘录一段话“RST一般是在FIN之后才会出现为1的情况,表示的是连接重置。”
我想:如果连接都重置了,服务器端就会出现写失败。正好和上面的错误日志吻合。

那么现在只是抓包和日志对上了,但是为什么客户端会发一个RST的报文,我就不清楚了。
wuliuxu 2013-06-18
  • 打赏
  • 举报
回复
还有下面一条测试线索: 如果加入以下代码($$$$$$$$$$$$$$$$$$$$$$$$标示出的中间部分),问题就消息了。 [下面是加入新代码后的程序片段,其他地方都不作改变]

public void response(){
        try{
            responseWriter = 
                    new BufferedWriter(
                            new OutputStreamWriter(
                                    serverSocket.getOutputStream()));


            // 此处是新加入的代码片段$$$$$$$$$$$$$$$$$$$$$$$$
            br = 
                    new BufferedReader(
                            new OutputStreamReader(
                                    serverSocket.getInputStream()));
            br.read();
            // 以上是新加入的代码片段$$$$$$$$$$$$$$$$$$$$$$$$



            // 服务端进行响应的响应内容
            StringBuffer content = new StringBuffer();
            content.append("<html>");
wuliuxu 2013-06-18
  • 打赏
  • 举报
回复
引用 5 楼 fei1710 的回复:
貌似当接收缓存区还存在数据时关闭socket会导致异常关闭,即发送RST立即关闭TCP连接。 当接收方收到RST时系统会丢弃TCP的发送和接收缓冲区,如果在此之前浏览器进程没有完全接收TCP缓冲区的数据的话,数据就不完整,浏览器就会报错。因为你没读取发送的数据,所以会出现这种情况。
我想确认一下,你说的接受缓存区是指服务端的接收缓存区,还是指客户端(浏览器)的接收缓存区。测试中,如果在服务端添加读取socket接收缓冲区的代码后,问题就消息了,从这个现象上看,有点支持你的观点。但问题是,上面的代码中,服务端根本就没有读取自身接收缓存区的数据,如果是你说的这个原因,那么,测试的时候,应该一次也不会成功,但为什么测试的结果却是有时成功,有时失败呢?
fei1710 2013-06-18
  • 打赏
  • 举报
回复
貌似当接收缓存区还存在数据时关闭socket会导致异常关闭,即发送RST立即关闭TCP连接。 当接收方收到RST时系统会丢弃TCP的发送和接收缓冲区,如果在此之前浏览器进程没有完全接收TCP缓冲区的数据的话,数据就不完整,浏览器就会报错。因为你没读取发送的数据,所以会出现这种情况。
wuliuxu 2013-06-17
  • 打赏
  • 举报
回复
感谢u010255083的辛苦测试,但是我机器上使用IE跑,还是会出现问题,不过显示的不是连接被重置,而是“无法显示当前页面”。你是怎么测试的,有没有反复的刷新,就是按着F5不动。浏览器断断续续的请求几次可能没问题,高频率的刷新问题就出现了。期待u010255083再测试一次,是高频率刷新哦
火影之贺 2013-06-17
  • 打赏
  • 举报
回复
拷贝到本机并运行了,因为没有装firefox浏览器,我只用了IE和chrome这2个浏览器测试。 测试结果一切正常。google了“firefox 连接被重置”,好多人都遇到了类似的问题。 建议楼主也可以从firefox这里找找原因,不一定是你的代码的问题。 http://www.elecbench.com/?p=268 最后找到了真正的解决办法,应该是火狐的一些设置或插件的问题。办法如下:删除C:\Documents and Settings\Administrator\Application Data\目录下Mozilla文件夹。前提是你没有安装Mozilla的其他软件。
wuliuxu 2013-06-17
  • 打赏
  • 举报
回复
我是一只掉队的程序员,没工作,没朋友,没机器,就一个人呆在住的地方敲代码学习。其实程序所有的代码就上面那一点,各位不介意可以copy到自己的机器上跑跑看,整个过程大概耗时30秒。
晓风吹雾 2013-06-17
  • 打赏
  • 举报
回复
试试不要再一台机器上发布http server和访问这个http server. 可以换换将server发布到局域网的另外的机器,然后访问。
编辑器 KindEditor 4.0.4 KindEditor 是一套开源的在线HTML编辑器,主要用于让用户在网站上获得所见即所得编辑效果,开发人员可以用 KindEditor 把传统的多行文本输入框(textarea)替换为可视化的富文本输入框。 KindEditor 使用 JavaScript 编,可以无缝地与 Java、.NET、PHP、ASP 等程序集成,比较适合在 CMS、商城、论坛、博客、Wiki、电子邮件等互联网应用上使用 主要特点 快速:体积小,加载速度快 开源:开放源代码,高水平,高品质 底层:内置自定义 DOM 类库,精确操作 DOM 扩展:基于插件的设计,所有功能都是插件,可根据需求增减功能 风格:修改编辑器风格非常容易,只需修改一个 CSS 文件 兼容:支持大部分主流浏览器,比如 IE、Firefox、Safari、Chrome、Opera ver 4.0.4 (2011-12-11) 新增: 阿拉伯语语言包。 改善: 上传文件时显示上传中提示。 改善: JSON解析失败时,通过弹出层显示服务器返回的HTML页面。 改善: [IE] 弹出框支持阴影效果。 Bugfix: 浏览器使用有些插件时,上传文件提示不正确。 Bugfix: 单独调用图片功能时,点击重置大小图标报错。 Bugfix: 设置了参数filterMode:true,分页符就会丢失样式。 Bugfix: [FF] 撤销全屏后页面会滚动到顶部。 Bugfix: [ASP] demo.asp没有指定编码,导致提交后HTML出现乱码。 Bugfix: 单独调用上传按钮时,无法与旁边输入框对齐。 Bugfix: [WEBKIT] 在图片、视频、flash等前一个光标处右键,在不选中节点的状态下也能弹出修改属性。 Bugfix: [IE] 编辑器无内容,加粗,切换到代码模式,再回到可视化模式,加粗,JS报错。 Bugfix: [IE] 插入<input value=”abc"def”/>,会自动变为 <input value=”abc”def”/>。 Bugfix: [WEBKIT] 点击粗体后丢失光标。 Bugfix: [OPERA] 切换到代码模式后不显示部分工具栏图标。 Bugfix: del标签被定义在块级元素里,导致格式化HTML时自动换行。 Bugfix: 开启过滤模式,获取HTML时删除线被过滤。 Bugfix: [IE] 两张相邻图片添加超级链接,修改其中一个链接,另外一个链接也会被修改。 Bugfix: 内嵌脚本的小于号会被转义导致脚本错误。 Bugfix: 分页符在不同浏览器下生成的HTML代码不一致。 Bugfix: [IE6-7] 插入URL里有大字符的图片,右键点击选择图片属性,更改图片属性后图片不能显示
Asprain是一个适合于各中小学、中专、技校、职高建设校园论坛、师生交流论坛,一些教科研部门、公司企业建设内部论坛、IT技术爱好者建设技术交流论坛的免费论坛程序。它有两个版本,分别是asp+Access版和asp+sqlServer版,功能完全一致,建站者可以根据自己的需要选用不同的版本。 Asprain 1.3 Access 更新记录: 10月4日的一些修正 01.Sql版:修正chrims皮肤的显示不正常的问题。Access版:修正版块标莫名其妙出现在中间的问题,现在可以设置版块图片了。 02.修正首页分区间广告不起作用的问题。 03.修正部分空广告内容也被保存到数据库的问题 04.修改了默认皮肤的那个过于难看的“发表话题”按钮 05.添加了百度sitemap_baidu.xml功能。 4月12日以来的一些改进: 01.修正服务器清空缓存之后版块帖子统计自动清零的bug。 02.修正Sql版论坛数据库无法清理的bug。 03.论坛首页可以添加版块图片了。你可以在后台为每个版块添加一个面积不大于150*48像素的图片作为版块标志。 如果你在后台不添加版块图片地址的话,这个图片不会显示。如果你在后台添加了版块图片地址的话,而且你选择首 页单栏通栏的话,这个图片会出现在版块名称、版块说明的右边。 04.修正收不到密码找回邮件的bug。 05.修正一系列因为论坛放在子目录中导致的问题。 06.修正论坛放在子目录时,上传的文件地址出现子目录重复的问题。 3月16日以来的修正 01.[更新]版主进行帖子操作(删除回复、加精、移动帖子、取消精华等操作)完成后,页面不需要再跳转,进一步减少了带宽流量,增强了操作便捷性。 02.[更新]回复帖子,如果遇到“对不起,字数不够”或者其它不能提交的问题时,不需要再刷新后重了,只需要继续帖子,完提交就行。 03.[修正]管理员不能设置帖子版内置顶的问题 04.[修正]后台不能修改用户注册协议的问题 05.[更新]在“站内信箱”里加了一个“全部设为已读”的功能。这样有时候遇到明明没有新信,却不断地提示“您有新的消息”的问题可以轻松解决了: 06.[修正]在后台对版块添加了版主之后,版主不能立即获得管理权的问题 07.[修正]在后台修改版块信息时,如果版块下面还有子版块就会出错的问题 08.[修正]在后台添加页头广告时莫名其妙地变成两侧对联广告的bug 3月26日的重大改进:Asprain论坛可以在子目录里安装了。不过建议不要在多级子目录里安装,而要在根目录或者一级子目录里安装。如果你把它安装在子目录中的话,global.asa依然会自动解压到根目录里的。 2月26日以来的bug修正: 01.[修正]用户在阅读新信之前把它删掉,会导致反复提示“你有新的信件,请注意查找”的bug 02.[修正]后台批量添加会员头衔出错的bug 03.[修正]后台功能搜索的bug 04.[修正]后台网站信息设置中不能修改description的bug 05.[修改]把后台的一些地方,“重置”按钮改成“刷新”按钮 06.[修改]用户修改自己的密码的时候,自动更新cookies 07.[修正]上传图片最大宽度和最大高度不能设为0的bug 08.[修正]后台添加网站公告无法即时更新的bug Asprain论坛体积小巧,但是功能完整。它前端脚本基于jquery框架,跨浏览器性能出色。对IE6、IE7、IE8、firefox、Chrome、Saferi、Opera七种主流浏览器有良好的兼容性。可以轻松松换肤,制作个性皮肤也很方便,只要修改几个css就可以了。 asprain论坛使用了大量的ajax效果,不仅能够很有效地节省数据流量、应付网络拥堵,而且极酷的脚本动画和遮罩框效果也能使用户获得更好的应用体验。在asprain里,用户切换隐身/在线状态、更换自己的头像、版主删除水帖、加亮优秀帖等很多操作,都只要一键完成,不需要页面刷新或者跳转。asprain论坛还支持版主批量删帖、转移帖子、将帖子设为精华等等操作,并支持用户对帖子的评分、举报帖子等功能。 asprain论坛使用了一个非常独创的可视化UBB在线编辑器,实现了真正的所见即所得的UBB编辑,安全与便捷兼顾。在asprain论坛的在线编辑器中,可以做到一键插入本地图片、一键完成远程图片上传、轻松插入优酷网等视频网站上的的视频地址、插入回复可读、付费可读等标记,还可以插入程序代码。asprain论坛使用了改进版的chili插件,UBB标签code/code能够很好地对JavaScript、php、asp、sql、css、html、C++、C#、Delphi、java、locus这十种
Asprain是一个适合于各中小学、中专、技校、职高建设校园论坛、师生交流论坛,一些教科研部门、公司企业建设内部论坛、IT技术爱好者建设技术交流论坛的免费论坛程序。它有两个版本,分别是asp+Access版和asp+sqlServer版,功能完全一致,建站者可以根据自己的需要选用不同的版本。 Asprain 1.3 SQL 更新记录: 10月4日的一些修正 01.Sql版:修正chrims皮肤的显示不正常的问题。Access版:修正版块标莫名其妙出现在中间的问题,现在可以设置版块图片了。 02.修正首页分区间广告不起作用的问题。 03.修正部分空广告内容也被保存到数据库的问题 04.修改了默认皮肤的那个过于难看的“发表话题”按钮 05.添加了百度sitemap_baidu.xml功能。 4月12日以来的一些改进: 01.修正服务器清空缓存之后版块帖子统计自动清零的bug。 02.修正Sql版论坛数据库无法清理的bug。 03.论坛首页可以添加版块图片了。你可以在后台为每个版块添加一个面积不大于150*48像素的图片作为版块标志。 如果你在后台不添加版块图片地址的话,这个图片不会显示。如果你在后台添加了版块图片地址的话,而且你选择首 页单栏通栏的话,这个图片会出现在版块名称、版块说明的右边。 04.修正收不到密码找回邮件的bug。 05.修正一系列因为论坛放在子目录中导致的问题。 06.修正论坛放在子目录时,上传的文件地址出现子目录重复的问题。 3月16日以来的修正 01.[更新]版主进行帖子操作(删除回复、加精、移动帖子、取消精华等操作)完成后,页面不需要再跳转,进一步减少了带宽流量,增强了操作便捷性。 02.[更新]回复帖子,如果遇到“对不起,字数不够”或者其它不能提交的问题时,不需要再刷新后重了,只需要继续帖子,完提交就行。 03.[修正]管理员不能设置帖子版内置顶的问题 04.[修正]后台不能修改用户注册协议的问题 05.[更新]在“站内信箱”里加了一个“全部设为已读”的功能。这样有时候遇到明明没有新信,却不断地提示“您有新的消息”的问题可以轻松解决了: 06.[修正]在后台对版块添加了版主之后,版主不能立即获得管理权的问题 07.[修正]在后台修改版块信息时,如果版块下面还有子版块就会出错的问题 08.[修正]在后台添加页头广告时莫名其妙地变成两侧对联广告的bug 3月26日的重大改进:Asprain论坛可以在子目录里安装了。不过建议不要在多级子目录里安装,而要在根目录或者一级子目录里安装。如果你把它安装在子目录中的话,global.asa依然会自动解压到根目录里的。 2月26日以来的bug修正: 01.[修正]用户在阅读新信之前把它删掉,会导致反复提示“你有新的信件,请注意查找”的bug 02.[修正]后台批量添加会员头衔出错的bug 03.[修正]后台功能搜索的bug 04.[修正]后台网站信息设置中不能修改description的bug 05.[修改]把后台的一些地方,“重置”按钮改成“刷新”按钮 06.[修改]用户修改自己的密码的时候,自动更新cookies 07.[修正]上传图片最大宽度和最大高度不能设为0的bug 08.[修正]后台添加网站公告无法即时更新的bug Asprain论坛体积小巧,但是功能完整。它前端脚本基于jquery框架,跨浏览器性能出色。对IE6、IE7、IE8、firefox、Chrome、Saferi、Opera七种主流浏览器有良好的兼容性。可以轻松松换肤,制作个性皮肤也很方便,只要修改几个css就可以了。 asprain论坛使用了大量的ajax效果,不仅能够很有效地节省数据流量、应付网络拥堵,而且极酷的脚本动画和遮罩框效果也能使用户获得更好的应用体验。在asprain里,用户切换隐身/在线状态、更换自己的头像、版主删除水帖、加亮优秀帖等很多操作,都只要一键完成,不需要页面刷新或者跳转。asprain论坛还支持版主批量删帖、转移帖子、将帖子设为精华等等操作,并支持用户对帖子的评分、举报帖子等功能。 asprain论坛使用了一个非常独创的可视化UBB在线编辑器,实现了真正的所见即所得的UBB编辑,安全与便捷兼顾。在asprain论坛的在线编辑器中,可以做到一键插入本地图片、一键完成远程图片上传、轻松插入优酷网等视频网站上的的视频地址、插入回复可读、付费可读等标记,还可以插入程序代码。asprain论坛使用了改进版的chili插件,UBB标签code/code能够很好地对JavaScript、php、asp、sql、css、html、C++、C#、Delphi、java、locus这十种开发语

50,545

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧