首先抛出问题:

程序1---错误版本

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket; public class HttpSimpleServer
{
public void startServer() throws IOException
{
ServerSocket ss = new ServerSocket(10021); Socket so = ss.accept(); InputStream in = so.getInputStream(); PrintWriter pw = new PrintWriter(so.getOutputStream(),true); byte[] bytes = new byte[1024]; int num = 0; while((num = in.read(bytes))!=-1)
{
String str = new String(bytes,0,num);
if(str.trim().length() <= 0)
{
break;
}
System.out.print(str);
}
pw.println("<font color='red' size='7'> 今天天气真好</font>");
so.close();
ss.close();
} public static void main(String[] args) throws IOException
{
new HttpSimpleServer().startServer();
}
}

 

上面的代码是一个基于Java的简单的HTTP服务器,输入访问地址:http://localhost:10021/ 进行访问

服务端输出:

GET /favicon.ico HTTP/1.1
Host: localhost:10021
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

而浏览器一直卡着,看不到输出的信息。

当浏览器访问连接:http://localhost:10021/的时候,服务端将一直阻塞,浏览器收不到响应。
出现无响应的原因在于:
浏览器和你的http服务器建立连接后,先发送请求头信息,然后并不会断开连接,所以read方法就会一直阻塞,等待服务器关闭连接(只有关闭后才会返回-1)。我么知道TCP断开连接使用的是四次分手原则,之所以使用四次分手原则,是因为TCP是个全双工的管道,每一端既可以发送也可以接受,当一端调用socket.close()之后,会发送fin关闭连接的报文(表明自己不再发送数据了,但是可以接收数据),对方收到之后,发送ACK报文,确认自己已经收到所有信息,这样一端关闭了发送,一端关闭了接收,当被动关闭方发送完毕之后,发送fin给主动关闭方,从而全部关闭.

于是Http的TCP连接的主动关闭方一般都是服务端,是在服务端的Response通过TCP发送出去之后再调用socket.close()方法的。read方法会阻塞(流没结束并且没有断开信号),这是主要原因。

public abstract int read()
throws IOException
从输入流中读取数据的下一个字节。返回 0255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。

子类必须提供此方法的一个实现。

返回:
下一个数据字节;如果到达流的末尾,则返回 -1
抛出:
IOException - 如果发生 I/O 错误。

其实浏览器此时正在等待你的响应,所以我们需要自己界定请求头的范围。请求头的结束标志是两个连续的换行(这个换行是有标准规定的,必须为\r\n而不是只使用\n),即\r\n\r\n。于是我们在收到这个字符串后就可以不再读取数据,而开始写入数据了。

其他就是该服务程序是一次性的,访问之后就不能再访问了,起码应该写成可以多次访问,进一步可以修改成可以同时多次访问,即多线程访问的。

还有问题就是返回信息没有HTTP响应头部,可能会出现乱码或者浏览器无法识别等问题

程序2--修正版本1:

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner; /**
* @author 作者 E-mail:
* @version 创建时间:2015-8-27 下午09:02:16 类说明
*/
public class HttpSimpleServer2
{
public void startServer() throws IOException
{
// 建立ServerSocket
ServerSocket serso = new ServerSocket(10021);
// 获取客户端对象
Socket so = serso.accept();
// 获取相关流对象
InputStream in = so.getInputStream();
PrintWriter pw = new PrintWriter(so.getOutputStream(), true);
Scanner sc = new Scanner(in);
sc.useDelimiter("\r\n\r\n");
if (sc.hasNext())
{
String header = sc.next();
System.out.println(header);
}
// HHTP响应头部信息
pw.print("HTTP/1.0 200 OK\r\n");
pw.print("Content-type:text/html; charset=utf-8\r\n");
pw.print("\r\n");
// HTTP响应内容
pw.println("<font color='red' size='7'>good</font>");
sc.close();
so.close();
serso.close();
} public static void main(String[] args) throws IOException
{
new HttpSimpleServer2().startServer();
}
}

服务端输出:

GET / HTTP/1.1
Host: localhost:10021
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.152 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

缺点:①不能多线程访问 ②只能访问一次,程序就运行结束了

程序3---修正版本2:

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.TimeUnit; /**
* 现在这个请求仍然是阻塞的,单线程的,同一时刻只能有一个程序进行访问
* @author 作者 E-mail:
* @version 创建时间:2015-8-27 下午09:08:26 类说明
*/
public class HttpSimpleServer3
{ public void startServer() throws IOException, InterruptedException
{
// 建立ServerSocket 这里默认的backlog 是50 可以有50个请求在排队等待
ServerSocket serso = new ServerSocket(10021);
while (true)
{
// 获取客户端对象
Socket so = serso.accept();
// 获取相关流对象
InputStream in = so.getInputStream();
PrintWriter pw = new PrintWriter(so.getOutputStream(), true);
Scanner sc = new Scanner(in);
sc.useDelimiter("\r\n\r\n");
if (sc.hasNext())
{
String header = sc.next();
System.out.println(header);
}
// HHTP响应头部信息
pw.print("HTTP/1.0 200 OK\r\n");
pw.print("Content-type:text/html; charset=utf-8\r\n");
pw.print("\r\n");
// HTTP响应内容
pw.println("<font color='red' size='7'>good</font>");
pw.flush();
Thread.sleep(100000); //单线程程序在多个同时访问的时候就会受限
sc.close();
//在sc关闭之前是写不出去的,因为没有flush so.close();
}
} public static void main(String[] args) throws IOException, InterruptedException
{
new HttpSimpleServer3().startServer();
}
}

使用while循环持续监听client连接

缺点:仍旧不能多线程访问

程序4:----修正版本3

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner; /**
* @author 作者 E-mail:
* @version 创建时间:2015-8-27 下午09:16:50 类说明
*/ class Runner implements Runnable
{
private final Socket socket; public Runner(Socket socket)
{
this.socket = socket;
} @Override
public void run()
{
// 获取相关流对象 try
{
InputStream in = socket.getInputStream();
PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
Scanner sc = new Scanner(in);
sc.useDelimiter("\r\n\r\n");
if (sc.hasNext())
{
String header = sc.next();
System.out.println(header);
}
// HTTP响应头部信息
pw.print("HTTP/1.0 200 OK\r\n");
pw.print("Content-type:text/html; charset=utf-8\r\n");
pw.print("\r\n");
// HTTP响应内容
pw.println("<font color='red' size='7'>good</font>");
pw.flush();
try
{
Thread.sleep(100000);
}
catch(InterruptedException e)
{
e.printStackTrace();
}
sc.close(); //服务器端的关闭请求。
socket.close();
}
catch(IOException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
} } }
//实现了多线程的访问,但是效率明显有点低哈哈哈哈
public class HttpSimpleServer4
{
public void startServer() throws IOException
{
// 建立ServerSocket 这里默认的backlog 是50 可以有50个请求在排队等待
ServerSocket serso = new ServerSocket(10021);
while (true)
{
// 获取客户端对象
Socket so = serso.accept();
Runnable runner = new Runner(so);
Thread thread = new Thread(runner);
// run就相当于在本线程当中调用,start才是启动的新线程
thread.start();
}
} public static void main(String[] args) throws IOException,
InterruptedException
{
new HttpSimpleServer4().startServer();
}
}

实现了多线程访问,

缺点:是阻塞式的,每个访问都要启用一个线程,没有数据输入线程就会阻塞,线程重复创建和销毁

程序5---修正版本4

草稿

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Set; /**
* @author 作者 E-mail:
* @version 创建时间:2015-8-27 下午09:37:25 类说明
*/
interface Handler
{
void doHandle(SelectionKey key);
} class AcceptHandler implements Handler
{ @Override
public void doHandle(SelectionKey key)
{
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = null;
// 在非阻塞模式下,serverSocketChannel.accept()有可能返回null
// 判断socketChannel是否为null,可以使程序更加健壮,避免NullPointException
try
{
socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
if (socketChannel == null)
return;
System.out.println("接收到客户链接,来自:" + socketChannel.socket().getInetAddress() + ":" + socketChannel.socket().getPort());
RequestHandler requestHandler = new RequestHandler(socketChannel);
socketChannel.register(key.selector(), SelectionKey.OP_READ, requestHandler);
}
catch(IOException ex)
{
ex.printStackTrace(); } } } class RequestHandler implements Handler
{
private SocketChannel socketChannel = null; public RequestHandler(SocketChannel socketChannel)
{
this.socketChannel = socketChannel;
} @Override
public void doHandle(SelectionKey key)
{
try
{
ByteBuffer buffer = ByteBuffer.allocate(1024);
socketChannel.read(buffer);
if(buffer.position()!=0)
{
System.out.println(new String(buffer.array()));
}
else
{
// 这里可能出现问题
System.out.println(buffer.toString());
}
buffer.flip();
// System.out.println(buffer)
// buffer.wrap(array)
// // HTTP响应头部信息
// S
// pw.print("HTTP/1.0 200 OK\r\n");
// pw.print("Content-type:text/html; charset=utf-8\r\n");
// pw.print("\r\n");
// // HTTP响应内容
// pw.println("<font color='red' size='7'>good</font>");
// pw.flush();
}
catch(IOException ex)
{
ex.printStackTrace();
} }
} public class HttpSimpleServer5
{
private Selector selector = null; private ServerSocketChannel serverSocketChannel = null; private int port = 10021; public HttpSimpleServer5() throws IOException
{
// 创建一个Selector对象
selector = Selector.open();
// 创建一个ServerSocketChannel对象
serverSocketChannel = ServerSocketChannel.open();
// 使ServerSocketChannel工作处于非阻塞模式
serverSocketChannel.configureBlocking(false);
// 使得在同一个主机上关闭了服务器程序,紧接着再启动该服务器程序时
// 可以顺利的绑定到相同的端口
serverSocketChannel.socket().setReuseAddress(true);
// 把服务器进程与一个本地端口绑定
serverSocketChannel.socket().bind(new InetSocketAddress(10021));
} void startServer() throws IOException
{
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT, new AcceptHandler());
while (selector.select() > 0)
{
// 获得Selector的selector-keys集合
Set<SelectionKey> readKeys = selector.selectedKeys();
Iterator<SelectionKey> it = readKeys.iterator();
while (it.hasNext())
{
SelectionKey key = it.next();
it.remove(); // 由Handler处理连接就绪事件
final Handler handler = (Handler) key.attachment();
handler.doHandle(key); }
}
} public static void main(String[] args) throws IOException
{
new HttpSimpleServer5().startServer();
}
}

  

基于Java的Http服务器几种模式演进的更多相关文章

  1. CentOS 6 搭建SVN支持httpd和svnserve独立服务器两种模式 以及邮件配置

    Linux下SVN服务器同时支持Apache的http和svnserve独立服务器两种模式且使用相同的访问权限账号 服务器操作系统:CentOS 6.x 1.在服务器上安装配置SVN服务: 2.配置S ...

  2. ftp服务器三种模式

    一.匿名开放模式(最不安全) 1.[root@localhost ~]# vim  /etc/vsftpd/vsftpd.conf  (主配置) anonymous_enable=YES    //允 ...

  3. 基于Java的二叉树的三种遍历方式的递归与非递归实现

    二叉树的遍历方式包括前序遍历.中序遍历和后序遍历,其实现方式包括递归实现和非递归实现. 前序遍历:根节点 | 左子树 | 右子树 中序遍历:左子树 | 根节点 | 右子树 后序遍历:左子树 | 右子树 ...

  4. 第三节:Windows平台部署Asp.Net Core应用(基于IIS和Windows服务两种模式)

    一. 简介 二. 文件系统发布至IIS 三. Web部署发布至IIS 四. FTP发布至IIS 五. Windows服务的形式发布 ! 作       者 : Yaopengfei(姚鹏飞) 博客地址 ...

  5. Java实现http服务器(一)

    基于Java实现Http服务器有多种多样的方法 一种轻量级的方式是使用JDK内置的com.sun.net.httpserver包下和sun.net.httpserver包下类提供的方法构建,该方法轻便 ...

  6. 基于JAVA语言的多线程技术

    1.简介 多线程技术属于操作系统范围内的知识: 进程与线程 可以这么理解,一个应用程序就是一个进程,在一个进程中包含至少一个线程:进程就是线程的容器,真正工作.处理任务的是线程. 进程是操作系统分配资 ...

  7. Spring核心技术(十二)——基于Java的容器配置(二)

    使用@Configuration注解 @Configuration注解是一个类级别的注解,表明该对象是用来指定Bean的定义的.@Configuration注解的类通过@Bean注解的方法来声明Bea ...

  8. 基于Java Mina框架的部标808服务器设计和开发

    在开发部标GPS平台中,部标808GPS服务器是系统的核心关键,决定了部标平台的稳定性和行那个.Linux服务器是首选,为了跨平台,开发语言选择Java自不待言. 我们为客户开发的部标服务器基于Min ...

  9. 基于Java Mina框架的部标jt808服务器设计和开发

    在开发部标GPS平台中,部标jt808GPS服务器是系统的核心关键,决定了部标平台的稳定性和行那个.Linux服务器是首选,为了跨平台,开发语言选择Java自不待言.需要购买jt808GPS服务器源码 ...

随机推荐

  1. Java或web中解决所有路径问题

    Java开发中使用的路径,分为两种:绝对路径和相对路径.归根结底,Java本质上只能使用绝对路径来寻找资源.所有的相对路径寻找资源的方法,都不过是一些便利方法.不过是API在底层帮助我们构建了绝对路径 ...

  2. SQL SERVER 查询死锁

    USE mastergo CREATE PROCEDURE [dbo].[sp_who_lock]AS     BEGIN        DECLARE @spid INT ,             ...

  3. 底部菜单栏(三)Fragment+FragmentTabHost实现仿新浪微博底部菜单栏

    一.实现效果图 二.项目工程结构 三.详细代码编写 1.主tab布局界面,main_tab_layout: 双击代码全选 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ...

  4. show()与showDialog()的区别

    A.WinForm中窗体显示  显示窗体可以有以下2种方法:  Form.ShowDialog方法 (窗体显示为模式窗体) Form.Show方法 (窗体显示为无模式窗体) 2者具体区别如下: 1.在 ...

  5. windows 8 解决端口(COM和LPT)问题:Prolific USB-to-Serial Comm Port(COM4)驱动异常的问题

    本来都正常的,一段时间后就不能用了,网上搜索了一圈,现记录下来,供大家参考 操作系统是win8 64位的 参考网址(http://blog.sina.com.cn/s/blog_92942dba010 ...

  6. Sublime Text使用心得(一)

    以前写web前端样式都是用eclipse.myeclispe这些IDE开发工具,现在想纯粹的写点HTML的东西,一心想找一个轻量的编辑器,这样能够随手打开编写,方便平时业余学习.网上搜罗了一堆编辑器, ...

  7. c# 获取MAC IP TCP列表

    转载自baidu:http://hi.baidu.com/jackeyrain/item/ff94efcfd5cf3a3099b498e9 namespace Public { public clas ...

  8. C# url 路径转换 相对路径 转换为 绝对路径

    用C#写爬虫时候,比较实用的一项技巧. /// <summary> /// 格式化URL函数 urlX 传入相对URL objurl 传入绝对基URL 基URL 一定要带HTTP:// / ...

  9. mvc性能优化

    mvc性能优化 (1)移动设备卡顿问题 -1请求方式 在mvc中GET请求有问题,出现错误 在MVC中在进行GET请求获取JSON数据时,需要进行如下设置: return Json("&qu ...

  10. .NET多线程同步方法详解

    .NET多线程同步方法详解(一):自由锁(InterLocked) .NET多线程同步方法详解(二):互斥锁(lock) NET多线程同步方法详解(三):读写锁(ReadWriteLock) .NET ...