每日一例 | 搞個(gè)大事……手寫簡(jiǎn)易web服務(wù)器

前言
已經(jīng)做java開發(fā)好幾年了,但是一直覺得web服務(wù)器是個(gè)很神秘的東西,之前有參考過一個(gè)大佬用netty寫的簡(jiǎn)易版的springboot,但是最后都不了了之了。今天,突然又有了想寫個(gè)web服務(wù)器了,所以就有了今天個(gè)小demo。
但是demo還是有點(diǎn)問題,就是瀏覽器請(qǐng)求的時(shí)候,服務(wù)端無法響應(yīng),但是把瀏覽器請(qǐng)求終止掉,服務(wù)端開始響應(yīng)了,但是這時(shí)候響應(yīng)結(jié)果已經(jīng)無法傳給瀏覽器了。這個(gè)問題,后期得研究下。
開整
今天才真正知道并理解了Tomcat是基于socket實(shí)現(xiàn)的,以前可能看到過相關(guān)博客和文檔,但是一直無法正在理解,想不明白它到底是咋樣通過Socket實(shí)現(xiàn)的,直到今天寫完這個(gè)demo,我發(fā)現(xiàn)突然就想明白了,頓悟的感覺,所以我已經(jīng)決定自己要?jiǎng)邮謱懸粋€(gè)web框架,名字就叫syske-boot,flag先立起來。
服務(wù)端
服務(wù)端其實(shí)就是一個(gè)socket服務(wù)端,用于處理客戶端請(qǐng)求。
public class SyskeCatServer {
private static final int port = 8080;
public static void main(String[] args) throws Exception{
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
System.out.println("SyskeCatServer is starting……");
while (true){
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
OutputStream outputStream = accept.getOutputStream();
SimpleHttpServer simpleHttpServer = new SimpleHttpServer();
SyskeRequest syskeRequest = new SyskeRequest(inputStream);
SyskeResponse syskeResponse = new SyskeResponse(outputStream);
simpleHttpServer.doService(syskeRequest, syskeResponse);
accept.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
serverSocket.close();
}
}
}
首先,從serverSocket中分別獲取輸入流和輸出流,這里的SyskeRequest就是對(duì)InputStream的封裝,SyskeResponse就是對(duì)OutputStream的封裝。我們根據(jù)這個(gè)也可以大膽推測(cè),tomcat的Request和Response也是類似的實(shí)現(xiàn)。
doService方法
class SimpleHttpServer {
public void doService(SyskeRequest request, SyskeResponse response) throws Exception {
try {
byte[] bytes = new byte[1024];
int read;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
while ((read = request.getInputStream().read(bytes)) != -1) {
byteArrayOutputStream.write(bytes);
}
byte[] toByteArray = byteArrayOutputStream.toByteArray();
String requestStr = new String(toByteArray);
System.out.println(String.format("請(qǐng)求參數(shù):%s", requestStr));
String[] split = requestStr.split("\r\n");
System.out.println("end");
response.write("hello syskeCat");
} catch (Exception e) {
e.printStackTrace();
}
}
}
SyskeRequest
這里就是包裝了InputStream
public class SyskeRequest {
private InputStream inputStream;
public InputStream getInputStream() {
return inputStream;
}
public SyskeRequest(InputStream inputStream) {
this.inputStream = inputStream;
}
}
SyskeResponse
這里就是對(duì)OutputStream簡(jiǎn)單包裝
public class SyskeResponse {
private OutputStream outputStream;
public SyskeResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
//將文本轉(zhuǎn)換為字節(jié)流
public void write(String content) throws IOException {
StringBuffer httpResponse = new StringBuffer();
httpResponse.append("HTTP/1.1 200 OK\n") //按照HTTP響應(yīng)報(bào)文的格式寫入
.append("Content-Type:text/html\n")
.append("\r\n")
.append("<html><head><link rel=\"icon\" href=\"data:;base64,=\"></head><body>")
.append(content) //將頁(yè)面內(nèi)容寫入
.append("</body></html>");
outputStream.write(httpResponse.toString().getBytes()); //將文本轉(zhuǎn)為字節(jié)流
outputStream.close();
}
}
測(cè)試
好了,上面這些就是核心代碼了,是不是很簡(jiǎn)單。我們來運(yùn)行下看看,直接運(yùn)行main方法就行了。服務(wù)端啟動(dòng)了:

瀏覽器訪問下看看:

這里瀏覽器一直沒響應(yīng),但是后臺(tái)也沒有收到請(qǐng)求,但是當(dāng)我終止請(qǐng)求后(點(diǎn)擊瀏覽器的x),服務(wù)端收到了請(qǐng)求:

看下后端收到的socket請(qǐng)求參數(shù),是不是很熟悉:
請(qǐng)求參數(shù):GET /tfgdfgdf HTTP/1.1
Host: localhost:8080
Connection: keep-alive
Cache-Control: max-age=0
sec-ch-ua: " Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: NMTID=00O3GJZgJXpkp8vI0cpmNRliUnGvYkAAAF4sPsi2w
這不就是瀏覽器的請(qǐng)求頭嗎?里面包括了請(qǐng)求地址、以及客戶端的其他信息。
如果你想實(shí)現(xiàn)更加請(qǐng)求地址響應(yīng)的需求,只需要解析下請(qǐng)求數(shù)據(jù)就行了,我覺得這些都簡(jiǎn)單了,最難的是從0到1,就是瀏覽器可以請(qǐng)求后可以收到響應(yīng),我覺得我現(xiàn)在做到了0.5,后面的0.5還需要再研究下。
總結(jié)
整體來說,這個(gè)實(shí)例還是比較簡(jiǎn)單的,雖然最后沒有完整地達(dá)到了最終的目的,但是還是有收獲的,至少服務(wù)請(qǐng)求收到了吧,所以從這個(gè)點(diǎn)上講,收獲還是大于預(yù)期的,而且對(duì)我而言還發(fā)現(xiàn)新大陸,只要這個(gè)缺憾,后面再好好摸索下,反正這個(gè)硬砍我啃定了。
- END -