引言
本文利用java自帶的socket編程實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的靜態(tài)資源服務(wù)器,可以響應(yīng)靜態(tài)資源。本文一共有兩個(gè)版本的源碼。第一個(gè)版本名為Server_v1,該版本實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的socket的服務(wù)器,幫助讀者回憶socket編程。第二個(gè)版本名為Server_v2,該版本是對(duì)第一版的改良,給出了改良思路,做出了必要的封裝,讓其能夠響應(yīng)css、html、jpg等靜態(tài)資源。
版本一
該版本實(shí)現(xiàn)一個(gè)簡(jiǎn)單的socket服務(wù)器,針對(duì)瀏覽器的請(qǐng)求,能夠返回相應(yīng)的頁(yè)面。
其源碼如下:
package mytomcat_v1;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
/**
*
* @author zhengrongjun
* @version v1.0
*/
public class Server_V1 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket client = null;
try {
serverSocket = new ServerSocket(9999);
// 不斷接收客戶連接
while (true) {
// 服務(wù)器阻塞等待客戶端socket連接過(guò)來(lái)
client = serverSocket.accept();
// 對(duì)客戶端里面端請(qǐng)求信息進(jìn)行處理
InputStream in = client.getInputStream();
// 定義一個(gè)讀取緩沖池 主要是在inputstram流中讀取字節(jié)
byte[] buff = new byte[1024];
int len = in.read(buff);
if (len > 0) {
String msg = new String(buff, 0, len);
System.out.println("===="+msg+"======");
OutputStream out = client.getOutputStream();
//構(gòu)建響應(yīng)信息
StringBuffer sb = new StringBuffer();
sb.append("HTTP/1.1 200 OK\n");
sb.append("Content-Type: text/html; charset=UTF-8\n");
sb.append("\n");
String html="<html><head><title>賣燒餅咯</title></head></html><body>小曲經(jīng)常在"
+"<font size='14' color='red'>"
+new Date()
+"</font>"
+"<br/>賣燒餅</body></html>";
sb.append(html);
out.write(sb.toString().getBytes());
out.flush();
out.close();
}
}
} catch (Exception e) {
}
}
}
執(zhí)行效果如下圖所示,打開chrome瀏覽器,在導(dǎo)航欄輸入
http://localhost:9999/docs/index.html
顯示如下圖所示

控制臺(tái)輸出如下圖所示
====GET /docs/index.html HTTP/1.1
Host: localhost:9999
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
======
版本二
該版本在版本一的基礎(chǔ)上進(jìn)行優(yōu)化,使其能夠有效的響應(yīng)靜態(tài)資源
步驟一
先看第一部分代碼優(yōu)化,如下圖所示

紅框的部分,我們可以理解為對(duì)請(qǐng)求信息對(duì)處理,因此我們模仿Tomcat構(gòu)造一個(gè)HttpRequst來(lái)處理這一段邏輯。
另外,我們需要對(duì)靜態(tài)資源進(jìn)行響應(yīng),因此我們需要獲取輸入內(nèi)容的靜態(tài)資源地址,即以下部分的內(nèi)容。

獲取以上紅框請(qǐng)求地址內(nèi)容的代碼如下
uri = msg.substring(msg.indexOf("/"),msg.indexOf("HTTP/1.1") - 1);
綜上所述,我們有HttpRequest類如下所示
package mytomcat_v2;
import java.io.IOException;
import java.io.InputStream;
/**
* 對(duì)客戶端進(jìn)行處理對(duì)業(yè)務(wù)類
*
* @author zhengrongjun
*
*/
public class HttpRequest {
private String uri;
public String getUri() {
return uri;
}
public HttpRequest(InputStream in) throws IOException {
// 對(duì)我們對(duì)請(qǐng)求字節(jié)流進(jìn)行解析
resolverRequest(in);
}
private void resolverRequest(InputStream in) throws IOException {
// TODO Auto-generated method stub
byte[] buff = new byte[1024];
int len = in.read(buff);
if (len > 0) {
String msg = new String(buff, 0, len);
System.out.println("====" + msg + "======");
// 解析出來(lái) uri
uri = msg.substring(msg.indexOf("/"), msg.indexOf("HTTP/1.1") - 1);
} else {
System.out.println("bad Request!");
}
}
}
步驟二
接下來(lái)是第二部分的代碼優(yōu)化,如下圖所示

以上紅框部分主要是對(duì)輸出信息進(jìn)行響應(yīng),我們模仿tomcat構(gòu)造一個(gè)HttpResponse對(duì)象封裝該部分邏輯。
另外,我們獲取用戶請(qǐng)求的資源文件路徑,根據(jù)該路徑找到相應(yīng)靜態(tài)文件。將該文件寫入文件流,輸出。
因此,我們有HttpResponse對(duì)象如下所示
package mytomcat_v2;
/**
* 封裝http響應(yīng)信息
* @author zhengrongjun
*
*/
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
public class HttpResponse {
private OutputStream os = null;
public HttpResponse(OutputStream os) {
this.os = os;
}
public void writerFile(String path) throws IOException {
FileInputStream fileInputStream = new FileInputStream(path);
byte[] buff = new byte[1024];
int len = 0;
// 構(gòu)建響應(yīng)信息
StringBuffer sb = new StringBuffer();
sb.append("HTTP/1.1 200 OK\n");
sb.append("Content-Type: text/html; charset=UTF-8\n");
sb.append("\n");
os.write(sb.toString().getBytes());
while ((len = fileInputStream.read(buff)) != -1) {
os.write(buff, 0, len);
}
fileInputStream.close();
os.flush();
os.close();
}
}
步驟三
接下來(lái)我們構(gòu)建測(cè)試類,構(gòu)建之前我們先去找一些靜態(tài)資源文件。作者直接去apache的官網(wǎng)下把tomcat給下了下來(lái),然后去如下目錄拷貝靜態(tài)資源文件
apache-tomcat-8.5.28/webapps/docs
將整個(gè)docs 文件夾拷貝至你的項(xiàng)目的根目錄下
apache-tomcat-8.5.28/webapps/ROOT/favicon.ico
將favicon.ico圖片拷貝至你的根目錄下
靜態(tài)資源在你的項(xiàng)目中的結(jié)構(gòu)如下圖所示

現(xiàn)在上我們的Server_V2的代碼
package mytomcat_v2;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
/**
*
* @author zhengrongjun
* @version v2.0
*/
public class Server_V2 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket client = null;
try {
serverSocket = new ServerSocket(9999);
// 不斷接收客戶連接
while (true) {
// 服務(wù)器阻塞等待客戶端socket連接過(guò)來(lái)
client = serverSocket.accept();
// =========請(qǐng)求類處理=======
InputStream in = client.getInputStream();
HttpRequest request = new HttpRequest(in);
String requestUri = request.getUri();
// =========響應(yīng)類處理=======
OutputStream os = client.getOutputStream();
HttpResponse response = new HttpResponse(os);
//請(qǐng)求格式是/html/login.html這種,需要把開頭的/刪除
response.writerFile(requestUri.substring(1));
client.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
測(cè)試結(jié)果如下:
在瀏覽器輸入
http://localhost:9999/docs/ssl-howto.html
效果如下

你會(huì)驚奇的發(fā)現(xiàn)樣式并不能識(shí)別,因此我們對(duì)響應(yīng)頭的部分邏輯進(jìn)行修改
將
sb.append("HTTP/1.1 200 OK\n");
sb.append("Content-Type: text/html; charset=UTF-8\n");
sb.append("\n");
部分修改為
if(path.endsWith("css")) {
sb.append("HTTP/1.1 200 OK\n");
sb.append("Content-Type: text/css; charset=UTF-8\n");
sb.append("\n");
}else {
sb.append("HTTP/1.1 200 OK\n");
sb.append("Content-Type: text/html; charset=UTF-8\n");
sb.append("\n");
}
繼續(xù)啟動(dòng)測(cè)試,效果如下

已經(jīng)能夠正常顯示
總結(jié)
本文給出了兩個(gè)版本的靜態(tài)資源的服務(wù)器源碼,希望讀者能夠有所收獲。
|