一、B/S网络架构概述
- 全称Browser/Server结构,是浏览器/服务器结构,就是我们经常利用浏览器进行访问,常见的有chrome,Firefox,IE等等,都基于统一的应用层协议HTTP来交互数据。
- HTTP采用无状态的短连接的通信方式,通常情况下,一次请求完成了一次数据交互,然后这次通信连接就断开了。这种方式的好处是能够让服务器处理海量用户的访问请求。
二、如何发起一个HTTP请求
- 当你在浏览器中输入一个URL时,按回车键后这个HTTP请求就发起了
- 如何发起一个HTTP请求和如何建立一个Socket连接区别不大,只不过outputStream.write写的二进制数据格式要符合HTTP。(建议先去了解Java的I/O流后再来看这篇文章)浏览器在建立Socket连接之前,必须根据地址栏内输入的URL的域名DNS解析出IP地址,在根据这个IP地址和默认的端口号80与远程服务器建立Socket连接,然后浏览器根据这个URL组装成一个get类型的HTTP请求头,通过outputStream.write发送到目标服务器,服务器等待inputStream.read返回数据,最后断开这个连接。
一句话,发起一个HTTP请求的过程就是建立一个Socket通信的过程。
你可以使用Linux中的curl指令来发送一个简单的HTTP请求:
可以看到返回的是html格式的代码
接下来我们要用Java代码来模拟B/S服务器,来了解服务器在接受到请求后做了哪些操作
三、通过java来模拟B/S服务器
首先来看看我们从浏览器请求一个网址我们的后台会收到什么:
- 创建一个服务器代码:
public static void main(String[] args) {
try {
//创建一个服务器ServerSocket,并指定端口号8088
ServerSocket server = new ServerSocket(8088);
//使用accept方法获取到请求的客户端对象(浏览器)
Socket socket = server.accept();
//使用socket对象中的方法getInputStream获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//把网络字节输入流转换为字符缓冲输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//把客户端(浏览器)的请求信息打印出来
String line = br.readLine();
while(line != null) {
System.out.println(line);
line = br.readLine();
}
socket.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
- 先运行代码,然后再请求一个网址,假如我请求的是这个网址:
发现打印了一些HTTP请求头(HTTP Header),我这里只介绍一些常用的请求头
- 从第一行我们可以看出我们发送的是一个GET请求,请求的路径为 /src/index.html ,使用的协议为 HTTP/1.1;
- Host:表示被请求资源的Internet主机和端口号,如 Host:www.taobao.com,也就是运行着这个程序的这台电脑,,这里的ip为localhost,端口号为8088;
- Connection:表示当前连接是否保持,keep-alive 表示保持连接;
- Cache-Control:表示是否缓存,这个有好几种缓存方式,max-age=xxx表示缓存的内容在xxx秒后失效,其他的几种我就不一细说了,感兴趣的可以自己去了解一下;
- User-Agent:表示客户端(浏览器)将它的操作系统、浏览器和其他属性告诉服务器;
- Accept-Encoding:表示指定可接受的内容编码;
- Accept-Language:由于指定一种自然语言。
那么怎么模拟服务器在接收到这些请求后返回指定路径的资源呢,假定src目录下创建了一个index.html文件,那么要怎样才能返回这个资源从而让客户端(浏览器)访问到这个资源呢
显然,我们需要使用到Java中的 I/O操作, I/O 可以说是机器获取和交换信息的主要渠道,如何优化 I/O 也是目前Web应用中面临的主要问题,那么我们先来了解一些基本的 I/O 操作类吧,方便更好的理解我们之后要做的一系列操作。
- 基于字符操作的 I/O 接口:InputStream 和OutputStream。
- 基于字符操作的 I/O 接口:Writer 和 Reader。
- 基于磁盘操作的 I/O 接口:File。
- 基于网络操作的 I/O 接口:Socket。
- 代码实现:
public static void main(String[] args) {
try {
//创建一个服务器ServerSocket,并指定端口号8088
ServerSocket server = new ServerSocket(8088);
//使用accept方法获取到请求的客户端对象(浏览器)
Socket socket = server.accept();
//使用socket对象中的方法getInputStream获取到网络字节输入流InputStream对象
InputStream is = socket.getInputStream();
//把网络字节输入流转换为字符缓冲输入流
BufferedReader br = new BufferedReader(new InputStreamReader(is));
//这里我们只需要第一行就行了 GET /src/index.html HTTP/1.1
String line = br.readLine();
//截取到中间的路径 /src/index.html,赋给htmlpath
String[] arr = line.split(" ");
String htmlpath = arr[1].substring(1);
//创建一个文件输出流,读取服务器上该路径的文件信息
FileInputStream fis = new FileInputStream(htmlpath);
//使用socket对象中的方法getOutputStream获取到网络字节输出流OutputStream对象
OutputStream os = socket.getOutputStream();
//写入HTTP响应头,固定写法
os.write("HTTP/1.1 200 OK\r\n".getBytes());
os.write("Content-type:text/html\r\n".getBytes());
os.write("\r\n".getBytes());
//一读一写复制文件,把服务器读取的html文件回写到客户端(浏览器)
int len = 0;
byte[] bytes = new byte[1024];
while((len = fis.read(bytes)) != -1) {
os.write(bytes,0,len);
}
//释放资源
fis.close();
socket.close();
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
运行我们的代码,再次到浏览器上访问这个网址:
可以看到我们的index.html页面显示出来了:
从浏览器的调试工具中也能看到各种信息,Request Headers是我们浏览器向服务器请求数据的请求头,这个前面已经介绍过了;Response Headers是服务器返回给浏览器的响应头,里面的 Content-Type 可以用来指明发送给浏览器的实体正文的媒体类型,还有很多其他的响应头,这里我也不一介绍了。
网友评论