这篇文章用一个简单的示例回味一下Web程序的运行原理。css
在开始Web框架设计以前,有必要先讲一讲Web软件的运行原理。html
Web软件和通常软件最大不一样,就是Web软件是运行在一台服务器上,你们经过浏览器访问服务器来工做学习。而通常软件则是运行在用户手中的计算机设备里。java
从部署角度来说,Web软件是一处部署服务各方。而通常软件则须要一台一台的去安装部署。json
照此分析,现在流行的App其实说白了也算是通常软件喽?其实从运行本质上来看是这样的。不一样的是App运行在端上,这个端通常指的是Pad、手机或者智能设备。浏览器
现现在一些智能路由器也支持App了,搞很差那天咱们的插座上也能跑两个App。可是不管如何一对一的部署方式和Web一次部署服务各方这种方式。是有着本质的区别的,好了言归正传回到咱们的话题上来。服务器
所谓Web开发其实就是编写一个程序运行在一台机器上,而后经过浏览器访问这个应用。下面就展现一个很是简单的Web应用程序:网络
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.io.Reader; import java.net.ServerSocket; import java.net.Socket; public class SocketServer { public static void main(String[] args) throws IOException { int messageID = 0; ServerSocket server = new ServerSocket(80); // 服务器端Socket对象 while (true) { try { dialog(server.accept(), messageID++); } catch (Throwable e) { System.out.println("Error. " + e); } } } public static void dialog(Socket socket, int messageID) throws Throwable { // //Request Reader reader = new InputStreamReader(socket.getInputStream()); BufferedReader ioRead = new BufferedReader(reader); String readLine = null; while ((readLine = ioRead.readLine()) != null) { System.out.println("[Message " + messageID + "] - " + readLine); if ("".equals(readLine.trim())) { break; } } // //Response String body = "Hello ..."; // PrintWriter outWriter = new PrintWriter(socket.getOutputStream()); outWriter.println("HTTP/1.1 200 OK"); outWriter.println("Content-Type:text/html; charset=utf-8"); outWriter.println("\n"); outWriter.println(body); // outWriter.close(); ioRead.close(); socket.close(); } }
运行上面这个程序,它会在你的计算机上打开“80”端口,而后等待浏览器发来的请求。框架
接着你只须要打开浏览器输入你本身的机器IP地址或者输入“localhost”敲一下回车就能够看到页面上打印出“Hello...”socket
如今咱们来仔细分析一下这个Web程序都作了哪些事?学习
首先,咱们使用JDK自带的类“ServerSocket”建立了一个网络套接字并打开了80端口进行侦听。这件事若是不是很理解,能够这样比喻一下。
假设你在一个大房子里,而这个房子有好多好多的窗户,外面的人和你沟通的惟一方式就是向窗户里仍纸条。那么你要作的就是和某我的约定好,用具体的窗户。
为何要约定窗户呢?
由于考虑到这个房子里不必定就只有你一我的,假定另一我的也想和外界沟通,那他也要经过窗户传小纸条。为了不你和他争抢同一个窗户,因此你们要分开各自用各自的窗户。而且要高速和你传纸条的人,你用的是左边第一个窗户,另一我的用的是右边第一个窗户。
结合到咱们的例子里,80端口就是你在使用的“左边第一个窗户”而和你传纸条的那个同窗就是浏览器。
如今咱们经过“ServerSocket server = new ServerSocket(80);”这段代码打开了一扇窗准备和浏览器传小纸条。下面就是等着对方来跟你对暗号了。
当浏览器发来第一个“小纸条”时“server.accept()”方法就获得了返回值。接着咱们就能够经过“server.accept()”获得的“Socket”对象在“dialog”方法中作咱们想作的事情。记得在最后别忘了在写一个小纸条回递给浏览器。
浏览器就比如是一我的,当和浏览器进行交流的时候Web程序必需要“说”浏览器能听懂的语言。因此这就须要用到“HTML”了。
可是你们不要忘记,浏览器是能听懂不少语言的。像“xml、json、png、gif、css”这些东西浏览器都能听懂。因此当咱们跟浏览器交流的时候,必定要先告诉浏览器你说的是哪国语言,否则浏览器也会像人同样傻傻的呆在那里不知所云。
这就比如潜水员在水下经过手势,交流一些基本信息。
好了如今咱们知道了,要想让浏览器听懂咱们程序说的话,就要必须告诉浏览器Web程序说的是哪国语言,而这个信息就要经过HTTP协议来告诉浏览器。
当Web程序说“HTML”国的语言时,浏览器好作好准备,用“HTML”国家的思惟方式来理解咱们告诉它的内容。
这个沟通和协定说什么语言的过程有个专有名词叫作“HTTP”协议,这个就是Web程序的社交礼仪。
下面在讲一讲浏览器和Web程序之间是怎样交流的?
首先第一个地方是,交谈的方式是“一问一答”式。率先发问题的是浏览器,回答问题的是服务器。一般一次交谈,只有一个问题和一个答案。
如今回头看看咱们刚刚写的Web程序,虽然很粗糙可是已经能够和浏览器进行对话了。
咱们的例子程序很简单,以至于无论浏览器问什么问题回答的答案都同样,所以在下面这个代码中咱们只是将浏览器的问题打印到控制台不作处理。
在代码中,浏览器当问完问题等待服务器回答时,会额外送出一个“空行 + 回车”表示发问结束等待回答。而这在咱们的程序看来就是多了一个换行而已。
以下:服务器一旦读到这里就表示浏览器的问题问完了,接着程序跳出循环不在处理浏览器的问题,转而进行回答。
Reader reader = new InputStreamReader(socket.getInputStream()); BufferedReader ioRead = new BufferedReader(reader); String readLine = null; while ((readLine = ioRead.readLine()) != null) { System.out.println("[Message " + messageID + "] - " + readLine); if ("".equals(readLine.trim())) { break; } }
那么咱们的Web程序怎么回答的呢?
下面这段代码就是咱们的服务器Web程序回答问题的代码。
首先它会先告诉浏览器你的问题我都听到了“HTTP/1.1 200 OK”。而后紧接着又告诉浏览器,接下来我说的是HTML语言,而且用了一个方言是“UTF-8”。随后浏览器沉了口气“输出一个空行”。开始说咱们事先准备好的事“一段HTML代码”。
outWriter.println("HTTP/1.1 200 OK"); outWriter.println("Content-Type:text/html; charset=utf-8"); outWriter.println("\n"); outWriter.println(body);
当服务器都说完了以后,就随手把小纸条丢出去。最后还不忘记把用过的“笔墨纸砚”都收拾了一遍。
outWriter.flush(); outWriter.close(); ioRead.close(); socket.close();
最后小纸条回到了浏览器的手中,剩下的事就是浏览器本身去理解并展现给咱们了。
如今咱们已经掌握了Web程序和浏览器之间的外交礼仪,那么下面咱们在深刻一下,让两个世界的人民作一些贸易往来吧。
咱们来假设一个场景,中国打算进口一批商品。因而打开了国门准备进行贸易。
闻讯赶来的美国正好手里有一批多余的石油,想出售给中国。因而美国的商人们向中国运送了这批石油,中国收下了。并给了一个交易回执。后来欧洲的国家也过来,不过卖的倒是羊毛。
假定Web程序就是上面例子中咱们的国家中国,不一样的浏览器分别表明美国和欧洲。下面改造一下咱们的程序,让外国朋友们能够吧货物送过来。
咱们吧上面代码中,下面这个代码换成另一个
//Response String body = "Hello ...";
换成
//Response String body = "<form action='/send.do' method='get'>货物:<input type='text' name='name'/><input type='submit' value='发货'/></form>";
从新启动咱们的Web程序,你会发现浏览器上出现了一个让咱们填写的表单。咱们只要在浏览器上输入货物的名称,点击“发货”就能够了。
浏览器会以这种方式把咱们的货物 -> “表单内容”,发送给服务器。剩下的就是服务器如何接受这批货物了。
接收货物,下面咱们吧Request的部分改为下面这个代码用来收货:
Reader reader = new InputStreamReader(socket.getInputStream()); BufferedReader ioRead = new BufferedReader(reader); String readLine = ioRead.readLine(); // String requestURI = readLine.split(" ")[1]; String params = null; if (requestURI.split("\\?").length == 2) { params = requestURI.split("\\?")[1]; System.out.println("param:" + params); } // while ((readLine = ioRead.readLine()) != null) { System.out.println("[Message " + messageID + "] - " + readLine); if ("".equals(readLine.trim())) { break; } }
首先上面这段代码中,咱们加入了“ioRead.readLine();”这么一行,这行代码的意义是读取浏览器送来的第一行内容。当浏览器递交表单的时候,会把表单内容放在这里。而表单的内容就是咱们浏览器送来的货物。
由于这个内容是有特殊格式的,咱们须要将它进行拆解,这样才能真正拿到浏览器送来的货物。这就像远洋运输必定是把货物放在集装箱里经过船舶运输过来同样。
后面的if判断和字符串拆解目的就是把集装箱里的货物拿出来。最后经过“System.out.println("param:" + params);”的方式把这个货物打印出来。完成咱们例子中提到的商贸合做。
下面这个是最终的Web程序代码。
public class WebServer { public static void main(String[] args) throws IOException { int messageID = 0; ServerSocket server = new ServerSocket(80); while (true) { try { dialog(server.accept(), messageID++); } catch (Throwable e) { System.out.println("Error. " + e); } } } public static void dialog(Socket socket, int messageID) throws Throwable { // //Request Reader reader = new InputStreamReader(socket.getInputStream()); BufferedReader ioRead = new BufferedReader(reader); String readLine = ioRead.readLine(); // String requestURI = readLine.split(" ")[1]; String params = null; if (requestURI.split("\\?").length == 2) { params = requestURI.split("\\?")[1]; System.out.println("param:" + params); } // while ((readLine = ioRead.readLine()) != null) { System.out.println("[Message " + messageID + "] - " + readLine); if ("".equals(readLine.trim())) { break; } } // //Response String body = "<form action='/addUser.do'><input type='text' name='userName'/><input type='submit' value='递交'/></form>"; // PrintWriter outWriter = new PrintWriter(socket.getOutputStream()); outWriter.println("HTTP/1.1 200 OK"); outWriter.println("Content-Type:text/html; charset=utf-8"); outWriter.println("\n"); outWriter.println(body); // outWriter.flush(); outWriter.close(); ioRead.close(); socket.close(); } }