HTTP协议是无状态的
http协议是无状态的,同一个客户端的这次请求和上次请求是没有对应关系,对http服务器来说,它并不知道这两个请求来自同一个客户端。 为了解决这个问题, Web程序引入了Cookie机制来维护状态.
HTTP之请求消息Request
请求行(request line)、请求头部(header)、空行和请求数据四个部分组成。
第一部分:请求行,用来说明请求类型,要访问的资源以及所使用的HTTP版本.
第二部分:请求头部,紧接着请求行(即第一行)之后的部分,用来说明服务器要使用的附加信息
第三部分:空行,请求头部后面的空行是必须的
第四部分:请求数据也叫主体,可以添加任意的其他数据。
下面简单写了一个请求页面:http://localhost:8888/login
看看它的请求头
GET /login HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/48.0.2564.82 Chrome/48.0.2564.82 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
这个请求没有请求数据
HTTP之响应消息Response
HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。
第一部分:状态行,由HTTP协议版本号, 状态码, 状态消息 三部分组成。
第二部分:消息报头,用来说明客户端要使用的一些附加信息
第三部分:空行,消息报头后面的空行是必须的
第四部分:响应正文,服务器返回给客户端的文本信息。
下面看看上面那个请求的响应信息
HTTP/1.1 200 OK
Content-Type: text/html;charset='utf-8'
Date: Thu, 21 Dec 2017 07:01:45 GMT
Connection: keep-alive
Transfer-Encoding: chunked
下面使用telnet命令进行模拟
//1、输入命令
telnet localhost 8888
//2、输入回车,接着会显示如下内容:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
//3、输入回车,然后将上面的请求进入输入,如下所示
GET /login HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/48.0.2564.82 Chrome/48.0.2564.82 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
//4、输入回车,显示内容如下:
HTTP/1.1 200 OK
Content-Type: text/html;charset='utf-8'
Date: Thu, 21 Dec 2017 07:28:05 GMT
Connection: keep-alive
Transfer-Encoding: chunked
141
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>$Title$</title>
</head>
<body>
<form action="/dologin" method="get">
<input type="text" name="username"><br/>
<input type="text" name="password"><br/>
<input type="submit" value="登陆">
</form>
</body>
</html>
0
telnet localhost 8888
也就是说我们的http通信只要遵从其协议就可以进行通信
下面使用代码来实现客户端的请求
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class MyBrowser {
public static void main(String[] args) {
try {
Socket browser = new Socket("localhost", 8888);
PrintWriter pw = new PrintWriter(browser.getOutputStream(),true);
// 封装请求第一行
pw.println("GET /login HTTP/1.1");
// 封装请求头
pw.println("Host: localhost:8888");
pw.println("Connection: keep-alive");
pw.println("Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
pw.println("Upgrade-Insecure-Requests: 1");
pw.println("User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/48.0.2564.82 Chrome/48.0.2564.82 Safari/537.36");
pw.println("Accept-Encoding: gzip, deflate, sdch");
pw.println("Accept-Language: en-US,en;q=0.8");
// 空行
pw.println();
// 封装实体主体
//pw.println("UserName=Mirhunana&Age=18");
// 写入完毕
browser.shutdownOutput();
// 接受服务器返回信息,
InputStream in = browser.getInputStream();
//
int length = 0;
StringBuffer request = new StringBuffer();
byte[] buf = new byte[1024];
//
while ((length = in.read(buf)) != -1) {
String line = new String(buf, 0, length);
request.append(line);
}
System.out.println(request);
//browser.close();
} catch (IOException e) {
System.out.println("出现异常了!");
}finally{
}
}
}
输出结果为:
HTTP/1.1 200 OK
Content-Type: text/html;charset='utf-8'
Date: Thu, 21 Dec 2017 08:03:55 GMT
Connection: keep-alive
Transfer-Encoding: chunked
141
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>$Title$</title>
</head>
<body>
<form action="/dologin" method="get">
<input type="text" name="username"><br/>
<input type="text" name="password"><br/>
<input type="submit" value="登陆">
</form>
</body>
</html>
0
上面的服务端是我们自己提前搭建好了的,下面我们自己用代码来实现服务端
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class MyServer {
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(8888);
System.out.println("服务器启动");
Socket s = server.accept();
byte[] buf = new byte[1024];
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
// 状态行
pw.println("HTTP/1.1 200 OK");
// 封装消息报头
pw.println("Content-Type: text/html;charset='utf-8'");
// 空行
pw.println();
// 封装响应正文
pw.println("<html>");
pw.println("<head>");
pw.println("<title>Mirhunana</title>");
pw.println("</head>");
pw.println("<body>");
pw.println("<p style=\"font-weight: bold;color: red;\">welcome to Server</p>");
pw.println("</body>");
s.close();
server.close();
pw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
上面的服务器的实现是一个单线程的实现,一般情况下的服务器的实现是使用的一个多线程来不断的监听服务端的请求,每次接收到一个请求就创建一个线程来进行处理。
参考文章:
http://www.jianshu.com/p/80e25cb1d81a
http://blog.csdn.net/changhenshui1990/article/details/70052991