原文

1、概述

HTTP是一种协议,全称超文本传输协议,而网页就属于超文本(就是为了它服务的),可以支持多媒体等,比如图片、音频,丰富了用户的体验;它属于网络模型中应用层的协议,底层基于TCP/IP协议,并额外制定了自己的规范,所以它也是Socket编程的一种,只是由于强大和便捷的客户端和服务端软件,常常让人看不到它的本质。

首先HTTP属于网络编程,而且是基于TCP/IP协议的,那么它一定有两个Socket端点,所以具备的基本要素有:

  • 客户端:浏览器,比如IE、Chrome、FireFox、360等
  • 服务端:Web服务器,有Tomcat、Weblogic等
  • 第三个往往会被忽视的:网址,专业术语URL,它包含了网络传输的基本要素:协议、IP、端口等。

2、最简单的方式实现HTTP请求。

2.1 最简单的方式是什么?

对普通用户来说,HTTP请求这个名词显得有点专业和陌生,而用“上网“或者更精确的讲”使用浏览器上网“,更能让人有恍然大悟的感觉,你可能觉得何必搞怎么一个标题呢,那只是我们没有了解到实现HTTP请求可不仅仅只有这种方式,至于还有那些方式,当了解到它的原理就自然清楚了。

即使最简单和熟悉的HTTP请求方式(上网),对软件人员来说,也需要更加专业的去分析它,通过不同的视角和见解,更加透视的看清它的内在。

2.2 流程图

通过浏览器实现HTTP请求的基本流程如下:

2.3 URL(见Java API:java.net.URL)

2.3.1 概述

Uniform Resource Locator,中文翻译,统一资源定位符,它是指向互联网"资源"的指针。资源可以是简单的文件或目 录,也可以是对更复杂对象的引用,例如对数据库或搜索引擎的查询。它的组成是有一定规则的,是信息的一种集合,一般由协议名、主机、端口、资源组成,格式如下:
  protocol://host:port//resourceName,比如:http://www.crazyit.org/index.php,当然URL不仅仅支持http这一种 协议,比如https、ftp等。
  有了它以后,客户端就可以对它进行解析,比如网络通信的基本要素:协议、IP、端口,都可以拿到,这样才能确定通信的目的地,而通过一个简单URL就可以对这些内容进行封装,是不是很方便呢。

2.3.2 URL常用方法:

String getProtocol(),获取URL的协议名称。
String getHost(),获取此URL的主机名。
String getPort(), 获取此URL的端口号。
String getPath(), 获取此URL的路径名称
String getFile() ,获取此URL的文件名称
String getQuery ,获取此URL的查询部分。

  1. package com.example.network.http;

  2. import java.net.MalformedURLException;

  3. import java.net.URL;

  4. public class URLDemo {

  5. public static void main(String[] args) throws MalformedURLException {

  6. String str_url = "http://google.com.hk/search?q=1";

  7. //1.把字符串封装为URL,会抛出MalformedURLException异常。

  8. URL url = new URL(str_url);

  9. System.out.println("getProtocol:"+url.getProtocol());

  10. System.out.println("getHost:"+url.getHost());

  11. System.out.println("getPort:"+url.getPort());

  12. System.out.println("getPath:"+url.getPath());

  13. //getFile() = getPath() + getQuery

  14. System.out.println("getFile:"+url.getFile());

  15. System.out.println("getQuery:"+url.getQuery());

  16. }

  17. }

结果:

  1. getProtocol:http

  2. getHost:google.com.hk

  3. getPort:-1

  4. getPath:/search

  5. getFile:/search?q=1

  6. getQuery:q=1

2.3.3 URL、URI、URN区别?

URI是统一资源标识符,总体来说,每一个URL都是URI,但不是每一个URI都是URL。这是因为URI还包括一个子类,即统一 资源名称(URN),它命名资源但不知道如何定位资源,比如:mailto:java-net@java.sum.com。

2.4 浏览器做了哪些工作?

2.4.1 概述

对于TCP/IP协议而言,只需明确IP、端口号及传输的数据,而要弄清楚HTTP协议与此有什么不同,就需要看看浏览器到底给服务器发送了什么数据,那么怎么去查看呢?既然HTTP是基于TCP/IP协议,完全可以通过ServerSocket对象实现一个服务器,接收浏览器的访问,并打印出接收到的数据。

2.4.2 代码示例

1、服务器代码:

  1. package com.example.network.http;

  2. import java.io.IOException;

  3. import java.io.InputStream;

  4. import java.io.OutputStream;

  5. import java.net.ServerSocket;

  6. import java.net.Socket;

  7. public class MyHttpServer {

  8. public static void main(String[] args) throws IOException {

  9. ServerSocket ss = new ServerSocket(8090);

  10. Socket s = ss.accept();

  11. InputStream in = s.getInputStream();

  12. byte[] buf = new byte[1024];

  13. int len = in.read(buf);

  14. System.out.println(new String(buf,0,len));

  15. OutputStream out = s.getOutputStream();

  16. out.write("<font color=red>欢迎访问Http服务器!</font>".getBytes());

  17. s.close();

  18. ss.close();

  19. }

  20. }

2、在浏览器中输入服务器地址并回车。

3、查看浏览器的返回内容和服务器的控制台输出。

正如控制台中打印出来的消息,浏览器不仅仅只是简单的接收一个URL,它需要解析这个URL,并且组装出控制台中格式化数据,并且把它发给服务器,服务器接收请求并且返回应答消息,最后浏览器行使另外一个强大的功能:解析数据,如上所示我给返回字体添加了一个红色属性,最终浏览器解析并显出来。

2.4.3、发送消息的组成分析。

发送的消息分为三部分:

图中红色1表示:消息行,分为三部分:

请求方式:GET和POST。

请求的资源:遗憾的是只看到一个/,它表示无,因为我没有指定,实际它是指URL的getFile()的内容。

协议版本:目前有HTTP 1.0和HTTP 1.1。

图中红色2表示:消息头,它主要以键值对的方式显示,向服务器传递了该浏览器的一些属性,以便于服务器返回浏览器支持的数据,比如:

Accept:浏览器支持的格式,如text(文本)、HTML、XML等。

Accpet-Language:浏览器支持的语言:zh-CN,简体中文;zh 中文;en 英文等

Host:主机地址

这些内容还没有深入的了解,只是做简单的介绍。

图中红色3表示:消息体,可惜是空白,那它是什么?看完下面章节就知道了。

2.4.1 怎么向服务器发送个人数据。

不知道你发现一个问题没有,虽然浏览器给服务器发送了数据,但是这些数据都是规范化的,如果想给服务器发送点自己的数据怎么办呢?

比如想登录某个网站,必须把客户端填写的用户名和密码发送给服务器;或者在搜索引擎中查询某个关键词,也需要把这个关键词发送到服务器进行检索。

以一个简单的表单提交为例,看看浏览器到底怎么向服务器发送数据。

1、提交表单。

  1. <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

  2. <html>

  3. <head>

  4. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

  5. <title>登录信息</title>

  6. </head>

  7. <body>

  8. <form method="get" action="http://localhost:8090" >

  9. <table border="1">

  10. <tr><td>用户名:</td><td ><input name=name type="text"/></td></tr>

  11. <tr><td>密 码:</td><td ><input name=pass type="password"/></td></tr>

  12. <tr><th colspan="2"><input type="submit" value="登录"/></th></tr>

  13. </table>

  14. </form>

  15. </body>

  16. </html>

2、服务器代码还是上面2.5.1中的第一段代码。

3、假定在表单中填写用户名和密码。

4、提交后,查看服务器控制台内容。

你会发现最后多了一行,而这一行的内容就是你在表单中输入的用户名和密码,它以键值对的方式存储数据,并且每一组用&符号连接;另外,这一行也属于之前所说的消息体,注意消息体与消息头之间有一个空行。

2.4.2 GET和POST区别

你可能还注意到的一个细节就是在第一行中,请求方式是POST而不是GET,那么GET和POST到底有什么区别?你只需把表单以get的方式提交一次就会发现了。

首先,把第1块提交表单代码中method=“post”改为method=“get”,然后再重新运行一遍上面的流程,你会发现如下区别:

  • 控制台中最后一行的消息体没有了,而第一行POST变为GET,并且请求资源变为“/?name=noodles$pass=26”,如下图:

  • 第二个不易察觉的变化就是在地址栏中多了点东西,就是你的用户名和密码的信息,如下图:

所以提交个人数据的方式与请求方式密切相关,如果是POST方法,提交的数据将放置在消息体中;而GET方式,提交的数据将放置在地址栏(URL)中。

这个特点导致GET方式提交数据存在几个缺点:

1、数据不安全,假如你登录一个网站时,别人站在你后面,就可以通过地址栏查看到你用户名和密码,是不是很可怕。

2、地址栏的空间太小。

所以,如果是提交重要的数据一般都是通过POST的方式,当然不是提交数据都用POST方式,通过google或者baidu检索信息时,就是以get的方式提交数据,你可以通过地址栏查到查询的信息。

2.6 HTTP服务器向客户端返回了什么内容?

通过上面的内容,我们已经了解到客户端浏览器做了哪些工作,现在就要进一步了解服务端到底做为了什么,或者准确的讲服务器给浏览器返回了什么信息,怎么去查看呢?

这就需要我们去实现一个浏览器,然后我们模仿浏览器给服务器发送相同的消息,最后打印出服务器返回的数据。

1、你需要一个HTTP服务器,比如Tomcat,安装后,在webapp下建立一个myweb文件下,里面放置一个mypage.html文件。

mypage.html代码非常简单,如下:

  1. <html>

  2. <head>

  3. <meta charset="utf-8">

  4. </head>

  5. <body>

  6. <a href ="http://www.google.com">Goto Google</a>

  7. </body>

  8. </html>

2、我的浏览器代码

  1. package com.example.network.http;

  2. import java.io.IOException;

  3. import java.io.InputStream;

  4. import java.io.PrintWriter;

  5. import java.net.Socket;

  6. public class MyBrowser {

  7. public static void main(String[] args) throws IOException {

  8. //与服务器建立连接

  9. Socket s = new Socket("127.0.0.1",8080);

  10. PrintWriter out = new PrintWriter(s.getOutputStream(),true);

  11. //模仿浏览器向服务器发送数据,下面三行是必须的,其他消息头信息可以根据需要发送,如果不设置,会以默认值发送(未验证)。

  12. out.println("GET /myweb/mypage.html HTTP/1.1");

  13. out.println("Host: localhost:8090");

  14. out.println();

  15. //读取服务器返回的数据并打印

  16. InputStream in = s.getInputStream();

  17. byte[] buf = new byte[1024];

  18. int len = in.read(buf);

  19. System.out.println(new String(buf,0,len));

  20. s.close();

  21. }

  22. }

3、执行我的浏览器代码,查看控制台信息,如下:

同样分为三部分:

图中红色1表示:应答行,分为两部分:

传输协议:HTTP1.0和HTTP1.1。

状态码:200表示成功;404表示无该页面。

图中红色2表示:应答头,因键值对的形式标识服务器或者是数据属性,如:

Server:Apache-Coyote/1.1,服务器类型。

Content-Length:125,字体长度。

Last-Modified:Wed,21 Sep 2016 15:39:17 GMT,资源的最后修改时间。

图中红色3表示:应答体,服务器返回浏览器请求的资源。

虽然我的浏览器也可以访问HTTP服务器,并且返回资源,但是与真正的浏览器还是有差距的,主要区别如下:

1、它只需要输入一个URL,就可以自动完成消息的组装并发送给服务器。

2、它可以接收数据后,一方面会解析并屏蔽掉对用户无用的应答行和应答头,另外它最强大的功能就是可以解析应答体, 并显示更加生动的页    面给用户。

3、不使用浏览器,除了通过Socket模拟浏览器的方式,还有办法发送HTTP请求嘛?

在个人的程序中,往往是不太方便去调用浏览器实现HTTP请求,即使可以也存在一个问题,就是浏览器返回的应答体,并不能被我们所利用;而通过Socket实现HTTP请求,也会出现一个问题就是它返回的IO流数据中包含了应答行和应该头,我们要自己去剔除这部分内容,保留应答体,这才是我们所需要的,有没有更好的方式呢?答案是肯定的。

 3.1 请求方式GET:

1、通过字符串,构建一个URL对象。

2、调用URL的URLConnection openConnection()方法返回一个URLConnection对象,该对象是是HTTP协议和URL的封装。

3、通过URLConnection类提供的方式,设置参数或者请求属性。

设置参数,如:

void setConnectTimeout(int timeOut) 连接超时。

void setReadTimeout(int timeout),读取数据超时。

void setDoOutput(boolean dooutput),连接服务器后是否可以向其传输数据,如果是需要提交数据,则需要明   确设置为true,因为默认值是false。

一般请求属性,如:

void setRequsetProperty(String key,String value)

4、调用void connect()连接服务器。

5、调用URLConnection类提供的方式,访问应答头信息或者是返回数据。

OutputStream getInputStream(),返回输入流。

String getHeaderField(String name),返回指定的头字段值。

因为应该头信息需要经常访问,所以API中也提供了单独的方法,比如:

String getContentType(),返回content-type 头字段的值。

int getContentLength(),返回content-length 头字段的值。

long getDate(),返回date 头字段的值。

long getLastModifed,返回last-modified 头字段的值。

注意:这里并没有明确指定请求方式为GET,因为默认是GET。

代码示例:

  1. package com.example.network.http;

  2. import java.io.IOException;

  3. import java.io.InputStream;

  4. import java.net.URL;

  5. import java.net.URLConnection;

  6. public class URLConnectionWithGet {

  7. public static void main(String[] args) throws IOException {

  8. //1.创建一个字符串形式的URL

  9. String str_url = "http://localhost:8080/myweb/mypage.html";

  10. //2.通过URL类的构造函数接收一个字符串形式的URL,来创建URL一个对象

  11. URL url = new URL(str_url);

  12. //3.调用openConnection()方法来返回会一个URLConnection对象,它是具体传输协议和URL的封装

  13. URLConnection conn = url.openConnection();

  14. //4.通过URLConnection来设置参数和请求属性,根据实际需要,不设置有默认值。

  15. conn.setRequestProperty("Content-Type", "text/html");

  16. //5.调用connect()方法建立与服务器的连接,或者直接调用getInputStream()方法,返回输入流,它可以读取服务器的应答体内容,此方法包含connect()的动作,所以可以跳过connect()。

  17. InputStream in = conn.getInputStream();

  18. //6.读取输入流并打印

  19. byte[] buf = new byte[1024];

  20. int len = in.read(buf);

  21. System.out.println(new String(buf,0,len));

  22. in.close();

  23. }

  24. }

3.2 POST方式:与GET方式不同的地方在于:

1、必须要调用setDoOutput(true)方法,设置该参数,允许向服务器输入数据。

2、必须在connect()或者getInputStream()方法之前调用getOutputStream()返回输出流,并且写入数据,数据的格式为:name1=value1&name2=value2。

代码示例:

客户端:

  1. package com.example.network.http;

  2. import java.io.IOException;

  3. import java.io.InputStream;

  4. import java.io.OutputStream;

  5. import java.net.URL;

  6. import java.net.URLConnection;

  7. public class URLConnecionWithPost {

  8. public static void main(String[] args) throws IOException {

  9. URL url = new URL("http://localhost:8080/myweb/login.jsp");

  10. URLConnection conn = url.openConnection();

  11. conn.setDoOutput(true);

  12. OutputStream out = conn.getOutputStream();

  13. out.write("name=noodles&pass=26".getBytes());

  14. InputStream in = conn.getInputStream();

  15. byte[] buf = new byte[1024];

  16. int len = in.read(buf);

  17. System.out.println(new String(buf,0,len,"utf-8"));

  18. in.close();

  19. out.close();

  20. }

  21. }

服务端:

  1. <%@ page contentType="text/html; charset=UTF-8" language="java" errorPage="" %>

  2. <%

  3. request.setCharacterEncoding("UTF-8");

  4. String name = request.getParameter("name");

  5. String pass = request.getParameter("pass");

  6. if(name.equals("noodles")

  7. && pass.equals("26"))

  8. {

  9. out.println("登录成功!");

  10. }

  11. else

  12. {

  13. out.println("登录失败!");

  14. }

  15. %>

结果:

注意:这里也没有明确的指定以POST方式,但是它默认以POST的方式。

补充:URLConnection还有一个子类HttpURlConnection,它是特定支持http协议的,它提供了一些额外的便捷方法,如void SetRequestMethod(String method),设置发送请求的范式;void disconnect()可以在连接空闲足够长时间后关闭。具体使用于URLConnection没有太多差别,一般就是要添加上面所说的两个方法。

Java Sokect编程之HTTP请求相关推荐

  1. Java网络编程之TCP、UDP

    Java网络编程之TCP.UDP 2014-11-25 15:23 513人阅读 评论(0) 收藏 举报 分类: java基础及多线程(28) 版权声明:本文为博主原创文章,未经博主允许不得转载. J ...

  2. 浅谈Java网络编程之Socket (1)

    和大家一起分享的是Java网络编程之Socket.在Java中Socket可以理解为客户端或者服务器端的一个特殊的对象,这个对象有两个关键的方法,一个是getInputStream方法,另一个是get ...

  3. zbb20180929 thread java并发编程之Condition

    java并发编程之Condition 引言 在java中,对于任意一个java对象,它都拥有一组定义在java.lang.Object上监视器方法,包括wait(),wait(long timeout ...

  4. 浅谈Java网络编程之Socket (2)

    <浅谈Java网络编程之Socket (1)>中我们已经和大家说到客户端的网络编程,下面和大家分享的是服务器的实现代码. import java.net.*; import java.io ...

  5. java并发编程之4——Java锁分解锁分段技术

    转载自 java并发编程之4--Java锁分解锁分段技术 并发编程的所有问题,最后都转换成了,"有状态bean"的状态的同步与互斥修改问题.而最后提出的解决"有状态bea ...

  6. Java网络编程之NIO编程(待补充)

    Java网络编程之NIO编程(待补充) 学习网站1:http://ifeve.com/java-nio-all/ 学习网站2:http://www.ibm.com/developerworks/cn/ ...

  7. Java 并发编程之美:并发编程高级篇之一-chat

    借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...

  8. Java 并发编程之美:并发编程高级篇之一

    借用 Java 并发编程实践中的话:编写正确的程序并不容易,而编写正常的并发程序就更难了.相比于顺序执行的情况,多线程的线程安全问题是微妙而且出乎意料的,因为在没有进行适当同步的情况下多线程中各个操作 ...

  9. Java并发编程之CAS第三篇-CAS的缺点

    Java并发编程之CAS第三篇-CAS的缺点 通过前两篇的文章介绍,我们知道了CAS是什么以及查看源码了解CAS原理.那么在多线程并发环境中,的缺点是什么呢?这篇文章我们就来讨论讨论 本篇是<凯 ...

最新文章

  1. mysql删除有外链索引数据,Cannot delete or update a parent row: a foreign key constraint fails 问题的解决办法
  2. python3 第三十四章 - 聊聊File对象
  3. 修改oracle用户登录密码
  4. 前缀和与差分的使用(新手快速入门)
  5. JQ表单序列化变成 对象
  6. SQL SERVER 2000 创建挂起的文件操作 解决方法
  7. 超实用Mac软件分享
  8. [C#]System.Timers.Timer
  9. 免费开源PDF复制格式整理神器
  10. 2010年的读书计划
  11. 数值优化(Numerical Optimization)学习系列-序列二次规划和内点法(SQP、Interior-Point)
  12. word流程图怎么使箭头对齐_word流程图箭头怎么画
  13. 现代西班牙语第一册 单词表
  14. 自监督论文阅读笔记 Synergistic Self-supervised and Quantization Learning
  15. vue运行(Emitted value instead of an instance of Error)
  16. BZOJ3168. [HEOI2013]钙铁锌硒维生素(线性代数+二分图匹配)
  17. ELK环境部署(一)--基础环境配置
  18. 搜索引擎的爬虫技术研究
  19. Adobe软件试用下载
  20. STM32F4基于DMA的多通道ADC采集——遇到的问题解决

热门文章

  1. 网信办:网络音视频等平台不得诱导未成年人参与应援集资等活动
  2. 4万家公司没了!这个吸血房客的行业,终于要崩了?
  3. iPhone 13 Pro原型机曝光:全新玫瑰金配色,女性首选
  4. 索尼斥资11.7亿美元收购AT&T动漫业务Crunchyroll
  5. 外媒:全球芯片短缺已影响洗衣机和烤面包机等小家电生产
  6. 何小鹏谈“小米造车”:我们要为勇敢者鼓掌
  7. 抖音联合九大博物馆推出“云游”活动 网友可在家看展
  8. 知名插画师描绘nova6“5G”潮乐园 十城nova6自拍互动装置即将引爆
  9. 被限高消费后,王思聪又有新动作:新增对外投资...
  10. 腾讯首届“科学探索奖”获奖名单揭晓,每人将获300万奖金