最近笔者有点忙啊,每天上班,晚上回来写毕业论文,没有太多空闲时间来学东西了。上一篇文章写完,自己读了几遍,感觉笔者的表达能力和文字功底确实垃圾,高考语文也就值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/7975661和http://blog.youkuaiyun.com/zsmj_2011/article/details/7977754
ServerSocket
public ServerSocket(int port, int backLog, InetAddress bindingAddress);
上面是ServerSocket的一个构造函数,第一参数:指定服务器端监听端口号;第二参数:指定服务器端处理请求队列的大小,第三个参数:指定监听的IP地址,一般是本机。
web server 设计
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”,这个文件夹里要有你请求的静态资源,运行效果如下:
需要改进的地方
- 可以处理servlet、jsp;
- 使用多线程技术,优化服务器处理
- 使用NIO技术