在日常的开发中,我们用过非常多开源的webserver,比如tomcat、apache等等。如今我们自己实现一个简单的webserver,主要的功能就是用户点击要訪问的资源,server将资源发送到client的浏览器。为了简化操作。这里不考虑资源不存在等异常情况。web服务基于的是HTTP协议。用户在浏览器的地址栏输入要訪问的地址,server怎样得到该地址是个关键。先看下一般的HTTP请求和响应报文的一般格式:

HTTP 请求报文

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZ2hhbzEwOQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center">

HTTP 响应报文

webserver获取一个用户的连接时,会初始化一个线程和用户通信,代码例如以下:

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket; //每有一个连接建立时,server分出一个通信的线程
public class CommunicateThread extends Thread{
//与客户端通信的套接字
Socket client; public CommunicateThread(Socket s) {
client = s;
} //获取浏览器请求资源的路径
public String getResourcePath(String s){
// 一般的HTTP请求报文的第一行是“GET /index.html HTTP/1.1”
// 我们要获取的就是中间的"/indext.apsx" //获取资源的位置
String s1 = s.substring(s.indexOf(' ')+1);
s1 = s1.substring(1,s1.indexOf(' ')); //默认资源为index.html
if(s1.equals(""))
s1 = "index.html"; return s1;
} public void sendFile(PrintStream out,File file){
try{
DataInputStream in = new DataInputStream(new FileInputStream(file));
int len = (int)file.length();
byte buf[] = new byte[len];
in.readFully(buf);//读取文内容到buf数组中
out.write(buf,0,len);
out.flush();
in.close();
}
catch(Exception e){
System.out.println(e.getMessage());
System.exit(1);
}
} public void run(){
try{
//获取用户的IP地址和端口号
String clientIP = client.getInetAddress().toString();
int clientPort = client.getPort();
//创建输出流对象
PrintStream out = new PrintStream(client.getOutputStream());
//创建输入流对象
DataInputStream in = new DataInputStream(client.getInputStream());
//读取浏览器提交的请求
String msg = in.readLine(); //获取文件路径
String fileName = getResourcePath(msg);
System.out.println("The user asked for resource: "+fileName);
File file = new File(fileName); if(file.exists()){
//依据响应报文格式设置
System.out.println(fileName+" start send"); out.println("HTTP/1.0 200 OK");
out.println("MIME_version:1.0");
out.println("Content_Type:text/html");
int len = (int) file.length();
out.println("Content_Length:"+len);
out.println("");//报文头和信息之间要空一行 //发送文件
sendFile(out,file); out.flush();
}
client.close();
}
catch(Exception e){
System.out.println(e.getMessage());
}
} }

server主要负责初始化套接字和线程。代码例如以下:

import java.net.ServerSocket;
import java.net.Socket; public class WebServer { public static void main(String[] args) {
int Port = 12345;//端口号,因为这里是測试,所以不要使用经常使用端口
//创建两个套接字
ServerSocket server = null;
Socket client = null;
try{
server = new ServerSocket(Port);
//服务器開始监听
System.out.println("The WebServer is listening on port "+server.getLocalPort());
while(true){
client = server.accept();
//多线程执行
new CommunicateThread(client).start();
}
}catch(Exception e){
System.out.println(e.getMessage());
}
} }

执行測试:

编写一个index.html文件

<html>
<head></head>
<body>
<h1>This is the index of my WebServer</h1><hr></body>
</html>

放到项目文件的根文件夹,然后在浏览器地址栏输入:“localhost:12345/index.html”,就能够看到位于server端的html文件了。注意因为server是死循环,重新启动server会发现指定的port已被绑定,仅仅须要进入任务管理器,关闭"Java(TM) Platfrom SE binary"进程就可以。最后结果例如以下所看到的:

这个server程序非常简陋,还有非常大的改进余地。

大家能够自己尝试改进。这里能够尝试一下訪问其它的文件,发现时成功的。说明这server非常不安全呀。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZ2hhbzEwOQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center">

java实现简单webserver(分析+源码)的更多相关文章

  1. java分析源码-ReentrantLock

    一.前言 在分析了 AbstractQueuedSynchronier 源码后,接着分析ReentrantLock源码,其实在 AbstractQueuedSynchronizer 的分析中,已经提到 ...

  2. Java集合系列[4]----LinkedHashMap源码分析

    这篇文章我们开始分析LinkedHashMap的源码,LinkedHashMap继承了HashMap,也就是说LinkedHashMap是在HashMap的基础上扩展而来的,因此在看LinkedHas ...

  3. Java并发系列[2]----AbstractQueuedSynchronizer源码分析之独占模式

    在上一篇<Java并发系列[1]----AbstractQueuedSynchronizer源码分析之概要分析>中我们介绍了AbstractQueuedSynchronizer基本的一些概 ...

  4. 【JVM】深度分析Java的ClassLoader机制(源码级别)

    原文:深度分析Java的ClassLoader机制(源码级别) 为了更好的理解类的加载机制,我们来深入研究一下ClassLoader和他的loadClass()方法. 源码分析 public abst ...

  5. java集合系列之LinkedList源码分析

    java集合系列之LinkedList源码分析 LinkedList数据结构简介 LinkedList底层是通过双端双向链表实现的,其基本数据结构如下,每一个节点类为Node对象,每个Node节点包含 ...

  6. java集合系列之ArrayList源码分析

    java集合系列之ArrayList源码分析(基于jdk1.8) ArrayList简介 ArrayList时List接口的一个非常重要的实现子类,它的底层是通过动态数组实现的,因此它具备查询速度快, ...

  7. java多线程系列(九)---ArrayBlockingQueue源码分析

    java多线程系列(九)---ArrayBlockingQueue源码分析 目录 认识cpu.核心与线程 java多线程系列(一)之java多线程技能 java多线程系列(二)之对象变量的并发访问 j ...

  8. Java并发系列[3]----AbstractQueuedSynchronizer源码分析之共享模式

    通过上一篇的分析,我们知道了独占模式获取锁有三种方式,分别是不响应线程中断获取,响应线程中断获取,设置超时时间获取.在共享模式下获取锁的方式也是这三种,而且基本上都是大同小异,我们搞清楚了一种就能很快 ...

  9. Java并发系列[5]----ReentrantLock源码分析

    在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可 ...

随机推荐

  1. 8个对程序员来说有用的jQuery小贴士和技巧

    1) 禁用鼠标右键单击 jQuery程序员可以使用此代码在网页上禁用鼠标右键点击. 1 2 3 4 5 6 7 8 9 10 $(document).ready(function() {     // ...

  2. CoCreateInstance(转)

      CoCreateInstance  创建组件的最简单的方法是使用CoCreateInstance函数. 在COM库中包含一个用于创建组件的名为CoCreateInstance的函数.此函数需要一个 ...

  3. 如何在程序中使用CString

    在新建项目的时候,如果选择了MFC并且使用ATL,那么在程序中使用CString是没有问题的. 但是如果当初没有选,后面再改,虽然选上了,但是CString在编译的时候还是不被编译器识别.怎么办那? ...

  4. 自己的一个js库

    https://github.com/sqqihao/nono_framework.git 持续更新,慢慢写着,这样才能成长

  5. Android studio 2.0--android增量更新的那些事

    用了这么久的AS 2.0预览版本号.4.7日谷歌最终公布了android studio 2.0正式版,小编当日便下载了.玩了一下.感觉第二次build编译明显快了,并且好像并没有又一次部署apk.经过 ...

  6. 打开mdb文件0x80040213

    在操作数据库时,有时遇到 0x80040213的错误,这往往是路径的问题,即所操作的文件.图层.字段等不存在. 在<ArcGIS Engine地理信息系统开发从入门到精通>一书中也谈到了这 ...

  7. Objective-C中NSString与int和float的相互转换

    NSString *tempA = @"123"; NSString *tempB = @"456"; 1,字符串拼接 NSString *newString ...

  8. C# WebRequest处理Https请求

    http://www.cnblogs.com/youlechang123/archive/2013/03/23/2976630.html 正常情况下,处理https和http没有什么区别,如以下代码, ...

  9. 如何固定OpenERP顶的主菜单,方便滚动至第二屏以及多屏时,快速切换主菜单

    如何固定OpenERP顶的主菜单,方便滚动至第二屏以及多屏时,快速切换主菜单 作者:广州-步科,来自OpenERP应用群() 将“addons\web\static\src\css”目录下的“base ...

  10. python之函数用法isinstance()

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #python之函数用法isinstance() #isinstance() #说明:返回一个布尔值,判断数据 ...