1.HTTP协议简介
超文本传输协议(英文:HyperText Transfer Protocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。HTTP是万维网的数据通信的基础。
1.1HTTP协议概述
HTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TCP)。通过使用网页浏览器或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80)。我们称这个客户端为用户代理程序(user agent)。应答的服务器上存储着一些资源,比如HTML文件和图像。我们称这个应答服务器为源服务器(originserver)。通常,由HTTP客户端发起一个请求,创建一个到服务器指定端口(默认是80端口)的连接。HTTP服务 器则在那个端口监听客户端的请求。一旦收到请求,服务器会向客户端返回一个状态,比如"HTTP/1.1200 OK",以及返回的内容,如请求的文件、错误消息、或者其它信息。
1.2HTTP工作原理
1:首先是客户端连接到Web服务器: 浏览器对url进行域名解析拿到IP地址,然后就封装HTTP请求报文。 2:发送HTTP请求: 操作系统协议栈将应用层的HTTP报文,基于TCP协议再次封装,然后和服务器建立一个TCP套接字连接并且向Web服务器发送一个请求报文,报文由请求行、请求头、空行和请求数据组成。 3:服务器接受请求并且返回HTTP响应: Web服务器解析请求,定位资源,最后将服务资源输出到TCP套接字中,由客户端浏览器读取。一个响应报文由响应行、响应头、空行、响应数据组成。 4:释放连接的TCP连接 若connection 模式为close,则服务器主动关闭TCP连接,客户端被动关闭连接,释放TCP连接;若connection 模式为keepalive,则该连接会保持一段时间,在该时间内可以继续接收请求 5:客户端浏览器解析响应数据 浏览器会根据响应头中的数据,对应的就知道该怎样处理数据,若是HTML就渲染成网页,如果是文本就直接呈现在网页上。 图解其过程:
1.3HTTP报文格式
1.3.1请求报文
1.3.2响应报文
2.套接字(socket)
2.1Socket概览
2.2流socket概述
在网络通讯中,两个程序通过一个双端的通信连接管道实现数据交换,我们称这个管道叫Socket;一般是客户端创建socket连接服务端,与之相对应的服务端会建立一个服务端的socket服务进程等待客户端的socket连接!
图解过程:
2.3流socket原理
通过互联网进行通信,至少需要一对套接字,其中服务端的称之为ServerSocket,客户端的称为Socket,本质上讲服务端的套接字就是为了处理客户端的套接字的,每一个向服务端发起socket连接的都能称之为客户端。
通信过程图:
2.4流socket(网络编程)
了解流Socket以后,来简单的基于TCP来实现服务端与客户端的通信:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Test0721_1 {
private static String name="服务端";
public static void main(String[] args) throws IOException {
ServerSocket server=new ServerSocket(8888);
Scanner scanner=new Scanner(System.in);
Socket accept = server.accept();
InputStream in = accept.getInputStream();
OutputStream os = accept.getOutputStream();
while (true){
int len;
byte[] data=new byte[1024];
while ((len=in.read(data))!=-1){
System.out.print("客户端:");
System.out.println(new String(data,0,len));
if(len<1024){
break;
}
}
System.out.print(name+":");
os.write(scanner.next().getBytes());
}
}
}
===================================================================
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;
public class Test0721_2 {
private static String name="客户端";
public static void main(String[] args) throws IOException {
Socket socket=new Socket();
Scanner scanner=new Scanner(System.in);
socket.connect(new InetSocketAddress(8888));
OutputStream os = socket.getOutputStream();
InputStream in = socket.getInputStream();
while (true){
byte[] data=new byte[1024];
int len;
System.out.print(name+":");
os.write(scanner.next().getBytes());
while ((len=in.read(data))!=-1){
String s=new String(data,0,len);
System.out.println("服务端:"+s);
if(s.equals("bye")){
socket.shutdownInput();
socket.shutdownOutput();
socket.close();
os.close();
in.close();
break;
}
if(len<1024){
break;
}
}
if(socket.isClosed()){
break;
}
}
}
}
运行结果:
2.5数据报套接字(DatagramSocket)
在网络通讯中,数据包套接字,只要知道目的主机的IP地址与端口,即可进行通信,就是通过发送数据包给对方,接受数据包即可!
2.6数据报套接字(DatagramSocket)原理
2.7数据报套接字(网络编程)
了解数据报套接字以后,来简单的基于UDP来实现两台主角的通信:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UdpSeverA {
public static void main(String[] args) throws IOException {
DatagramSocket socket=new DatagramSocket(6666);
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
while (true){
byte[] data=new byte[1024];
DatagramPacket packet=new DatagramPacket(data,0,data.length);
socket.receive(packet);
String msg=new String(packet.getData(),0,packet.getData().length);
System.out.println(msg);
if(msg.trim().equals("bye")){
socket.close();
break;
}
String msgsend=reader.readLine();
byte[] sendData=msgsend.getBytes();
InetAddress localhost = InetAddress.getByName("localhost");
int port=5555;
DatagramPacket packet1=new DatagramPacket(sendData,0,sendData.length,localhost,port);
socket.send(packet1);
}
reader.close();
}
}
===================================================================
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UdpServerB {
private static String flag=null;
public static void main(String[] args) throws Exception {
DatagramSocket socket=new DatagramSocket(5555);
BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
while (true) {
String data = reader.readLine();
byte[] bytes = data.getBytes();
int port = 6666;
InetAddress localhost = InetAddress.getByName("localhost");
DatagramPacket packet = new DatagramPacket(bytes, 0, bytes.length, localhost, port);
socket.send(packet);
if (data.equals("bye")) {
break;
}
Thread t = new Thread(new Runnable() {
@Override
public void run() {
byte[] res = new byte[1024];
DatagramPacket packet1 = new DatagramPacket(res, 0, res.length);
try {
socket.receive(packet1);
} catch (IOException e) {
e.printStackTrace();
}
String msg = new String(packet1.getData(), 0, packet1.getData().length);
System.out.println(msg);
if ("bye".equals(msg)) {
flag=msg;
socket.close();
}
}
});
if("bye".equals(flag)){
t.interrupt();
}
t.start();
}
reader.close();
}
}
运行结果:
2.8补充URL网络下载
通过网络连接到网络上的资源,根据URL可以定位某个资源再根据流,就可以把资源输出到我们想要的地方。
简单编写一个程序,下载网络上的资源:
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class URLTest {
public static void main(String[] args) throws Exception {
URL url = new URL("https://9uu33.com/20210311/bf6f2ac51d8f895372a48ed527d5d59a.mp4");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream inputStream= urlConnection.getInputStream();
FileOutputStream fos = new FileOutputStream("m.mp4");
byte[] buffer = new byte[1024];
int len;
while ((len=inputStream.read(buffer))!=-1){
fos.write(buffer,0, len);
}
fos.close();
inputStream.close();
urlConnection.disconnect();
}
}
3.手动实现一个简单的HTTP服务器
3.1 总体思路与过程如下:
3.2代码实现
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Http {
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket();
server.bind(new InetSocketAddress(6888));
System.out.println("server is running! listening on port 6888!");
while (true){
Socket accept = server.accept();
System.out.println("A person"+accept.getRemoteSocketAddress().toString()+" is coming!");
ExecutorService executorService = Executors.newFixedThreadPool(20);
executorService.submit(new MyTask(accept));
}
}
}
===================================================================
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class MyTask implements Runnable {
private Socket accept;
public MyTask(Socket accept){
this.accept = accept;
}
@Override
public void run() {
InputStream in = null;
OutputStream out = null;
try {
StringBuilder sb = new StringBuilder();
in = accept.getInputStream();
int len;
byte[] buf = new byte[512];
while ((len = in.read(buf)) != -1) {
sb.append(new String(buf, 0, len));
if (len < buf.length) {
accept.shutdownInput();
}
}
Request request = Request.buildRequest(sb.toString());
out = accept.getOutputStream();
Response response = new Response();
response.setData(HttpUtils.getPage(request.getUrl()));
response.write(out);
}catch (Exception e){
e.printStackTrace();
}finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (accept != null){
try {
accept.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
===================================================================
public class HttpUtils {
public static String getPage(String url){
StringBuilder sb = new StringBuilder();
String result = null;
try {
if ("".equals(url) || "/".equals(url) || url == null){
url = "index.html";
}
String path = HttpUtils.class.getProtectionDomain().getCodeSource().getLocation().getPath();
path = path.substring(0,path.lastIndexOf("/")) + "/pages/";
url = path + url;
boolean exists = new File(url).exists();
if (!exists){
url = path +"404.html";
}
InputStream resource = new FileInputStream(url);
byte[] buf = new byte[1024];
int len;
while ((len = resource.read(buf)) != -1){
sb.append(new String(buf,0,len));
}
result = sb.toString().replace("{{name}}", "涉外—攀枝花");
} catch (Exception e){
e.printStackTrace();
}
return result ;
}
}
===================================================================
import java.util.HashMap;
import java.util.Map;
public class Request {
private String type;
private String url;
private String protocol;
private String contentType;
private Map<String,String> headers = new HashMap<>(8);
private Map<String,String> attributes = new HashMap<>(8);
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getContentType() {
return contentType;
}
public void setContentType(String contentType) {
this.contentType = contentType;
}
public Map<String, String> getHeaders() {
return headers;
}
public void setHeaders(Map<String, String> headers) {
this.headers = headers;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
}
public static Request buildRequest(String requestStr){
Request request = new Request();
String[] split = requestStr.split("\r\n\r\n");
String[] lineAndHeader = split[0].split("\r\n");
String[] lines = lineAndHeader[0].split(" ");
request.setType(lines[0]);
request.setUrl(lines[1]);
request.setProtocol(lines[2]);
for (int i = 1; i < lineAndHeader.length; i++) {
String[] header = lineAndHeader[i].split(": ");
request.getHeaders().put(header[0].trim().toLowerCase(),header[1].trim());
}
request.setContentType(request.getHeaders().get("content-type"));
return request;
}
}
===================================================================
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
public class Response {
private String protocol = "HTTP/1.1";
private Integer code = 200;
private String msg = "OK";
private String ContentType = "text/html;charset=utf-8";
private String ContentLength;
private Map<String,String > headers = new HashMap(){{
put("content-type",ContentType);
}};
private String data;
public Response(){}
public Response(String protocol, Integer code, String msg) {
this.protocol = protocol;
this.code = code;
this.msg = msg;
}
public String buildResponse(){
StringBuilder sb = new StringBuilder();
sb.append(this.getProtocol()).append(" ")
.append(this.getCode()).append(" ")
.append(this.getMsg()).append("\r\n");
for (Map.Entry<String,String> entry : this.getHeaders().entrySet()){
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
}
sb.append("\r\n").append(this.getData());
return sb.toString();
}
public void write(OutputStream os){
try {
os.write(buildResponse().getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void addHeader(String key,String value){
this.getHeaders().put(key,value);
}
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public Map<String, String> getHeaders() {
return headers;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
this.setContentLength(buildResponse().getBytes().length+"");
}
public String getContentType() {
return this.getHeaders().get("content-type");
}
public void setContentType(String contentType) {
this.getHeaders().put("content-type",contentType);
}
public String getContentLength() {
return this.getHeaders().get("content-length");
}
public void setContentLength(String contentLength) {
this.getHeaders().put("content-length",this.data.getBytes().length + "");
}
}
3.3打包验证
代码写好后,打成一个Jar包,然后去浏览器进行验证,过程如下: 将Jar包复制到非中文路径下,然后在编写启动脚本start.bat,内容如下: 最后,启动后,使用浏览器进行验证:
|