自己动手编写web server(二)

最近笔者有点忙啊,每天上班,晚上回来写毕业论文,没有太多空闲时间来学东西了。上一篇文章写完,自己读了几遍,感觉笔者的表达能力和文字功底确实垃圾,高考语文也就值103分,没有遗憾!!!

笔者有个习惯,就是遇到不会的东西总爱网上搜一搜,然后看各种博客,最近也浏览了一些技术大牛的博客,他们的浏览量和回复超越笔者的博客几条街。。。。笔者决心要写出他们那样的博客!!


闲话扯多了,进入正题!!!

上一篇,简单描述了HTTP协议,以及我们的思路,接下来,我们详细地来实现这个server,首先我们认识一下Socket!!!

说实话,笔者对网络编程一直是望而却步,究其原因,就是这个Socket,各种书或者教材翻译成“套接字”,笔者当时第一次接触这个东西时,年幼无知、不了解翻译人员的意思、发散思维较差等等原因,一直无法理解“套接字”的翻译表达了什么意思!!!现在依然无法理解!!!

Socket

现在给大家讲一下笔者对Socket的理解:Socket就是两台电脑之间的连接。。。那么两台电脑如何连接?现实生活中,我们用网线、设置IP地址等等,那么Socket也一样,充当网线与IP的功能。使用Socket对象,我们要为他设置IP以及端口,Socket对象为我们提供了输入输出功能,就像网线,可以传输数据。

public Socket (java.lang.String host, int port)

上面是Socket的其中一个构造函数,第一个参数:要请求的服务器名称或者IP地址;第二个参数:服务器端的端口号。

一旦你成功创建了一个Socket对象,你就可以通过这个对象获得与服务器之间的输入输出流。如果你要发送字节给服务器,使用getOutputStream方法来获取一个java.io.OutputStream对象,这就是输出流;如果你要读取服务器发送给你的字节,使用getInputStream方法用来返回一个java.io.InputStream对象,这是输入流。java的读取流,可以参考我以前的blog:http://blog.youkuaiyun.com/zsmj_2011/article/details/7975661http://blog.youkuaiyun.com/zsmj_2011/article/details/7977754

ServerSocket

上面介绍了Socket类,这个类主要代表客户端与服务器端的连接,那大家肯定会想到,服务器端如何接受这个连接?如何设置监听端口?ServerSocket类主要是做这些事情:指定服务器监听端口和地址;等待客户端连接;新建一个Socket来处理客户端连接。
public ServerSocket(int port, int backLog, InetAddress bindingAddress);

上面是ServerSocket的一个构造函数,第一参数:指定服务器端监听端口号;第二参数:指定服务器端处理请求队列的大小,第三个参数:指定监听的IP地址,一般是本机。
当你成功创建了ServerSocket对象时,可以使用accept方法,监听客户端的请求,当有客户端连接进来,accept方法返回一个Socket对象,通过Socket对象,与客户端进行交互。

web server 设计

上面介绍了客户端与服务器端所使用的类,我们主要的目的是编写一个web server,因此,主要是服务器端编程。
首先,通过上一篇介绍的HTTP协议知道,服务器端需要处理客户端的请求--Request,发送响应---Response给客户端。因此,我们设计两个类:Request和Response,分别对应HTTP协议中的Request和Response;还需要一个类WebServer,就是服务器主线程类,用于接收客户端请求,调用Request与Response两个类。
不废话了,贴代码,很简单,网上也有很多类似的
package com.la.webserver;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class WebServer {
	//设置静态资源的目录
	public static final String WEB_ROOT=System.getProperty("user.dir")+File.separator+"webroot";
	//设置关闭命令
	private static final String SHUTDOWN_COMMAND="/SHUTDOWN";
	private boolean shutdown=false;
	public static void main(String[] args) {
		WebServer server=new WebServer();
		server.await();
	}

	public void await() {
		ServerSocket serverSocket=null;
		//监听8080端口
		int port=8080;
		try {
			serverSocket=new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
		} catch (IOException e) {
			e.printStackTrace();
			System.exit(1);
		}
		//循环等待客户端连接
		while (!shutdown) {
			Socket socket=null;
			InputStream inputStream=null;
			OutputStream outputStream=null;
			try {
				socket=serverSocket.accept();//当有客户端连接时
				inputStream=socket.getInputStream();//获取请求字符流
				outputStream=socket.getOutputStream();//输出流
				//处理客户端请求---Request
				Request request=new Request(inputStream);
				request.parse();
				//向客户端发送Response
				Response response=new Response(outputStream);
				response.setRequest(request);
				response.sendStaticResource();
				//关闭socket
				socket.close();
				//检查如果是shutdown命令,关闭服务器
				shutdown=request.getUri().equals(SHUTDOWN_COMMAND);
			} catch (Exception e) {
				e.printStackTrace();
				continue;
			}
		}
	}
}

package com.la.webserver;

import java.io.IOException;
import java.io.InputStream;

public class Request {
	private InputStream inputStream;
	private String uri;
	public String getUri() {
		return uri;
	}
	public void setUri(String uri) {
		this.uri = uri;
	}
	public  Request(InputStream inputStream) {
		this.inputStream=inputStream;
	}
	public void parse() {
		//从socket读取字符
		StringBuffer request=new StringBuffer(2048);
		int i;
		byte[] buffer=new byte[2048];
		try {
			i=inputStream.read(buffer);
		} catch (IOException e) {
			e.printStackTrace();
			i=-1;
		}
		for (int j = 0; j < i; j++) {
			request.append((char)buffer[j]);
			
		}
		System.out.println(request.toString());
		uri=parseUri(request.toString());
	}
	/**
	 * 从客户端请求字符串中截取请求资源uri
	 * @param requeString 请求字符串,包括请求头、请求方法、请求资源的uri
	 * @return 请求资源的uri
	 */
	private String parseUri(String requeString){
		int index1,index2;
		//比如GET /index.html HTTP/1.1
		//请求方法、请求资源、版本之间以空格隔开
		index1=requeString.indexOf(' ');
		if (index1!=-1) {
			index2=requeString.indexOf(' ', index1+1);
			if (index2>index1) {
				return requeString.substring(index1+1, index2);
			}
		}
		return null;
	}
}

package com.la.webserver;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;

public class Response {

	private static final int BUFFER_SIZE=1024;
	Request request;
	OutputStream outputStream;
	public  Response(OutputStream outputStream) {
		this.outputStream=outputStream;
	}
	public void setRequest(Request request) {
		this.request=request;
	}
	//发送静态资源
	public void sendStaticResource() throws IOException {
		byte[] bytes=new byte[BUFFER_SIZE];
		FileInputStream fis=null;
		try {
			File file=new File(WebServer.WEB_ROOT, request.getUri());
			if (file.exists()) {//如果请求的静态资源文件存在
				fis=new FileInputStream(file);
				int ch=fis.read(bytes, 0, BUFFER_SIZE);
				while (ch!=-1) {
					outputStream.write(bytes, 0, ch);
					ch=fis.read(bytes, 0, BUFFER_SIZE);
				}
			}else {//如果静态资源不存在
				String errorMessage="HTTP/1.1 404 File Not Found\r\n"+
						"Content-Type:test/html\r\n"+
						"Content-Length:23\r\n"+
						"\r\n"+
						"<h1>File Not Found</h1>";
				outputStream.write(errorMessage.getBytes());
			}
		} catch (Exception e) {
			System.out.println(e.toString());
		}
		finally{
			if (fis!=null) {
				fis.close();
			}
		}
	}
}

首先你的程序目录里要有个文件夹“webroot”,这个文件夹里要有你请求的静态资源,运行效果如下:

在浏览器中输入地址

运行正常,说明我们的webserver还是不错的,可以工作!!!

需要改进的地方

这只是一个简单的webserver,只能返回静态资源,像Jsp这样的页面,需要更多的处理;另外,这个服务器一次只能响应一个请求,也就是说其他想要连接服务器的请求只能等候;阻塞通信,可以使用NIO来进行改进。
接下来,我们的目标是:
  1. 可以处理servlet、jsp;
  2. 使用多线程技术,优化服务器处理
  3. 使用NIO技术
敬请期待下一篇blog!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值