1.B/S和C/S架构
1.1B/S架构
B/S架构是Browser/Server的简称,即浏览器/服务器结构,它的特点是客户端只需要安装浏览器即可访问资源,应用程序逻辑和数据均放在服务端共享访问。
- 优点:易于维护,服务端升级版本后,客户端(浏览器)不用升级,网页刷新后就可以获取就可以获取最新的版本
- 缺点:会占用服务端的带宽资源
1.2C/S架构
C/S架构是Client/Serverd的简称,即客户机/服务器结构,C/S通常采用两层结构,服务端负责数据管理,客户端负责界面交互,例如微信、QQ等等
- 优点:静态资源下载到本地安装,不用大量请求资源,占用服务端带宽资源相对B/S架构的小
- 缺点:版本升级需要重新安装才可以使用
2.http协议
超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上
2.1http协议的特点
- 底层基于TCP协议实现,面向连接方式安全
- 基于请求(request)和响应(response)模型
- http协议是无状态协议,对于事务处理没有任何记忆功能
- http多次请求数据无法共享(通常使用cookie和session解决)
- http协议传输数据过程属于同步的过程,如果客户端发送请求到达服务端,服务端一直没有响应,客户端会一直阻塞等待,对于用户体验不是那么友好
2.2http请求格式
http请求格式有三部分组成:请求行,请求头以及请求体
- 请求行:由三部分组成:请求方法(请求方法包括GET、HEAD、PUT、POST、TRACE、OPTIONS、DELETE以及扩展方法) URL以及协议的版本,之间由空格分隔,例如:POST /v1/api/check/userstatus HTTP/1.1
- 请求头:键值对的形式,传递服务器可能会用到的参数,比如cookie,应用程序名等等
- 请求体:客户端传送给服务端的数据,如:username=zhxd&&age=18
- get请求参数放在请求行中,没有请求体
- post请求参数在请求体中
- get请求参数有大小限制,post请求没有
2.3http响应格式
http响应格式也分为三个部分:响应行,响应头,响应体
- 响应行:响应数据第一行,包括http协议版本号 状态码 状态信息
HTTP/1.1 200 OK
- 响应头:键值对的形式,返回客户端解析资源时可能要用到的参数
Location: http://www.baidu.com(服务端需要客户端访问的页面路径)
Server:apache tomcat(服务端的Web服务端名)
Content-Encoding: gzip(服务端能够发送压缩编码类型)
Content-Length: 80(服务端发送的压缩数据的长度)
Content-Language: zh-cn(服务端发送的语言类型)
Content-Type: text/html; charset=GB2312(服务端发送的类型及采用的编码方式)
Last-Modified: Tue, 11 Jul 2000 18:23:51 GMT(服务端对该资源最后修改的时间)
Refresh: 1;url=http://www.mayikt.com(服务端要求客户端1秒钟后,刷新,然后访问指定的页面路径)
Content-Disposition: attachment; filename=aaa.zip(服务端要求客户端以下载文件的方式打开该文件)
Transfer-Encoding: chunked(分块传递数据到客户端)
Set-Cookie:SS=Q0=5Lb_nQ; path=/search(服务端发送到客户端的暂存数据)
Expires: date(Cache-Control过期时间)
Cache-Control: no-cache(服务端禁止客户端缓存页面数据) max-age=xx(通知浏览器:xx秒之内别来烦我,自己从缓冲区中刷新)
Pragma: no-***(服务端禁止客户端缓存页面数据)
Connection: close(1.0)/(1.1)Keep-Alive(维护客户端和服务端的连接关系)
Date: Tue, 11 Jul 2000 18:23:51 GMT(服务端响应客户端的时间)
- 响应体:响应给客户端的数据(内容)
{
"username": "m0_63836635",
"biz": "blog",
"subBiz": "article"
}
- 1xx:临时响应
- 2xx:成功响应
- 200:成功,服务器成功处理请求
- 201:已创建,请求成功并且服务器创建新的资源
- 202:已接收,服务器已接受请求,但尚未处理
- 203:非授权信息,服务器成功处理请求,但消息可能来自其他来源
- 204:无内容,服务器成功处理请求,但没有返回任何内容
- 205:重置内容,服务器成功处理请求,但没有返回任何内容
- 206: 服务器成功处理了部分 GET 请求
- 3xx:重定向
- 301:永久移动, 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置
- 302:临时移动,服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求
- 4xx(客户端错误)
- 5xx:服务端错误
3.UDP协议
UDP协议 为应用程序提供了一种无需建立连接就可以发送封装的 IP 数据报的方法,俗称面向无连接。通俗易懂讲解 UDP协议会把数据打包发送给目标地址, 这个数据包能不能发送给目标地址就不管了,所以我们的udp协议 它是不可靠协议、安全性低,容易丢包 但是速度非常快 无需类似于 tcp协议三次握手。
UDP的特点:
- 面向无连接,不可靠协议
- 安全系数低,容易丢包
- 传输速度非常快
4.TCP协议
TCP面向的连接是有效可靠的,通过三次握手建立连接和四次挥手拆除连接,TCP建立和拆除连接中有一些专业表示名词,SYN建立连接,ACK确认标志,FIN终止标志。
- 第一次握手:客户端向服务端发送建立连接标志syn=1,并生成随机的数字seq_number=x发送到服务端(SYN=syn+x)
- 第二次握手:服务端接收到客户端发送的数据之后,确认ack=x+1(ACK),向客户端发送连接标志并生成随机数字seq_number=y(SYN)
- 第三次握手:客户端接收到SYN+ACK,向服务端发送确认ack=y+1,TCP连接建立
- 第一次挥手:客户端向服务器发送fin=1,并生成随机数seq_number=u
- 第二次挥手:服务端接受到释放报文后,发送ack=u+1,并生成随机数seq_number=v给客户端,当前状态为关闭等待状态
客户端收到了服务器确认通知之后,此时客户端就会进入到终止状态,等待服务器端发送释放报文
- 第三次挥手:服务端确认数据发送完毕之后,就向客户端发送fin=1,ack=u+1,并生成随机数seq_number=w给客户端
- 第四次挥手:客户端必须发出确认,ACK=1,ack=w+1,而自己的序列号是seq=u+1,此时客户端就入了TIME-WAIT(时间等待)状态。此时的TCP连接还没有释放,必须经过2**MSL(最长报文寿命),当客户端撤销相应的TCB后,客户端进入CLOSED状态(服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些)
5.DNS域名解析
域名系统(英文:Domain Name System,缩写:DNS)是互联网的一项服务。它作为将域名和IP地址相互映射的一个分布式数据库,能够使人更方便地访问互联网(通俗讲就是为了方便记忆 我们会使用域名在通过dns解析成我们的ip地址)
域名解析会先走本地配置的域名解析器,如果没有找到再往上级域名解析器去找,如果都没有,就会到公网ip电信运行商去找。windows系统域名解析配置在C:\Windows\System32\drivers\etc
中
6.socket
- 计算机网络是通过传输介质、通信设施和网络通信协议,把分散在不同地点的计算机设备互连起来,实现资源共享和数据传输的系统。网络编程就就是编写程序使联网的两个(或多个)设备(例如计算机)之间进行数据传输。Java语言对网络编程提供了良好的支持,通过其提供的接口我们可以很方便地进行网络编程。例如我们的QQ聊天
- Java是 Internet 上的语言,它从语言级上提供了对网络应用程 序的支持,程序员能够很容易开发常见的网络应用程序。
- Java提供的网络类库,可以实现无痛的网络连接,联网的底层细节被隐藏在 Java 的本机安装系统里,由 JVM 进行控制。并 且 Java 实现了一个跨平台的网络库,程序员面对的是一个统一的网络编程环境。
网络通信三要素:ip 端口号 协议
6.1InetAddress
在jdk中提供了一个与IP相关的InterAddress类,该类用于封装一个IP地址,并提供了一系列与IP相关的方法。
InterAddress常用方法
-
static InetAddress getByName(String host)
:获取给定主机名的IP地址,host参数表示指定主机
-
InetAddress getLocalhost()
:获取本机的IP地址
-
String getHostName()
:获取本地的IP地址的主机名
-
String getHostAddress()
:获取字符串格式的原始IP地址
-
static boolean isReachable(int timeout)
:判断在限定时间内指定的IP地址是否可以访问
package com.zhxd.test;
/*
* author zhxd
* ClassName interAddressTest
*/
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class inetAddressTest {
public static void main(String[] args) throws IOException {
//获取指定主机的IP
InetAddress inetAddress = InetAddress.getByName("www.zhxd.com");//主机名可在hosts中配置
//获取字符串IP地址
String ip = inetAddress.getHostAddress();
System.out.println(ip);
//获取本地IP
InetAddress inetAddress2 = InetAddress.getLocalHost();
System.out.println(inetAddress2.getHostAddress());
//获取本地IP的主机名
String hostName = inetAddress2.getHostName();
System.out.println(hostName);
//判断指定IP地址在限定时间内是否可以连通
boolean reachable = InetAddress.getByName("www.baidu.com").isReachable(1000);
System.out.println(reachable);
}
}
6.2UDP发送端
- 创建发送端Socket对象:
new DatagramSocket()
- 提供数据,把数据封装到数据包中:
new DatagramPacket(byte[] msg, int msglength,InetAddress inetAddress,int port)
- msg:字节形式的消息
- msglength:消息的长度
- inetAddress:接收端IP
- port:接收端端口号
- 通过Socket服务的发送功能,把数据包发送出去:
datagramSocket.send(DatagramPacket datagramPacket )
- 释放资源:
datagramSocket.close()
package com.zhxd.test;
import java.io.IOException;
import java.net.*;
public class UDPClient{
public static void main(String[] args) throws IOException {
//1.创建UDP发送端
DatagramSocket datagramSocket = new DatagramSocket();
//2.封装数据包
byte[] msg = "你好".getBytes();
InetAddress inetAddress = InetAddress.getByName("www.zhxd.com");
int port = 8081;
DatagramPacket datagramPacket = new DatagramPacket(msg, msg.length, inetAddress, port);
//发送消息
datagramSocket.send(datagramPacket);
//释放资源
datagramSocket.close();
}
}
6.3UDP接收端
- 创建接收端socket对象:
new DatagramSocket(int port)
- 接收数据:
new DatagramPacket(byte[] msg,int msgLength)
- msg:接收发送端的消息
- msgLength:消息的长度
- 解析数据:
datagramSocket.receive(DatagramPacket datagramPacket)
- datagramPacket:数据包,把接受到的数据往datagramPacket存放
- 输出数据:
datagramPacket.getData()
- 释放资源:
datagramSocket.close()
package com.zhxd.test;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class UDPServer{
public static void main(String[] args) throws IOException {
//1. 创建接收端的socket对象
DatagramSocket datagramSocket = new DatagramSocket(8081);
//2. 接收数据
byte[] msg = new byte[1024];
DatagramPacket datagramPacket = new DatagramPacket(msg, msg.length);
//3.解析数据
datagramSocket.receive(datagramPacket);
//4.输出数据
System.out.println(new String(datagramPacket.getData()));
//5.释放资源
datagramSocket.close();
}
}
6.4TCP发送端
- 创建发送端Socket对象(创建连接):
new Socket(InetAddress inetAddress,int port)
- inetAddress:接收端IP地址
- port:接收端端口号
- 获取输出流对象:
socket.getOutputStream()
- 发送数据:
outputStream.write(byte[] msg)
- 释放资源:
socket.close()
package com.zhxd.test;
/*
* author zhxd
* ClassName TCPClient
*/
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient {
public static void main(String[] args) throws IOException {
//1. 创建发送端Socket对象
InetAddress localHost = InetAddress.getLocalHost();
Socket socket = new Socket(localHost, 8081);
//2. 获取输出流
OutputStream outputStream = socket.getOutputStream();
//3. 写入数据
outputStream.write("tcp Client msg".getBytes());
//4. 释放资源
outputStream.close();
socket.close();
}
}
6.5TCP接收端
- 创建接收端Socket对象:
new ServerSocket(int port)
- 监听(阻塞:如果建立连接失败,程序会一直阻塞,不往下执行):
Soket soket = ServerSocket.accept()
- 获取输入流对象:
socket.getInputStream()
- 获取数据:
inputStream.read(bytes)
- 输出数据:
new String(bytes, 0, len))
- 释放资源:
ServerSocket.close()
package com.zhxd.test;
/*
* author zhxd
* ClassName TCPServer
*/
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer {
public static void main(String[] args) throws IOException {
//1. 创建接收端socket对象
ServerSocket serverSocket = new ServerSocket(8081);
//2. 监听
Socket socket = serverSocket.accept();
//3. 获取输入流
InputStream inputStream = socket.getInputStream();
//4. 获取数据
byte[] bytes = new byte[1024];
int len = inputStream.read(bytes);
//5. 输出数据
System.out.println(new String(bytes,0 ,len));
//6. 释放资源
serverSocket.close();
socket.close();
inputStream.close();
}
}
7.练习题:手写web服务器
package com.zhxd.test;
/*
* author zhxd
* ClassName HttpServer
*/
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = null;
Socket socket = null;
OutputStream outputStream = null;
InputStream inputStream = null;
FileInputStream fileInputStream = null;
// 创建服务ServerSocket
serverSocket = new ServerSocket(80);
while (true) {
try {
// 监听80端口
socket = serverSocket.accept();
// 获取请求的输入流
inputStream = socket.getInputStream();
// 获取响应输出流
outputStream = socket.getOutputStream();
//获取请求的地址
byte[] reqbytes = new byte[1024];
int reqLen = inputStream.read(reqbytes);
String reqContent = new String(reqbytes, 0, reqLen);
String url = reqContent.split("\r\n")[0].split(" ")[1];
//读取文件
File file = new File("./"+url);
fileInputStream = new FileInputStream(file);
byte[] bytes = new byte[20480];
//从硬盘读取到内存中
int respLen = fileInputStream.read(bytes);
fileInputStream.close();
//返回数据给客户端
outputStream.write(bytes, 0, respLen);
} catch (Exception e) {
e.printStackTrace();
} finally {
socket.close();
inputStream.close();
outputStream.close();
}
}
}
}