Java Sokect编程之HTTP请求

本文详细介绍了如何使用Java的Socket编程实现HTTP请求,包括最简单的方式、URL的使用、浏览器的工作原理、GET和POST的区别,以及如何通过URLConnection发送GET和POST请求。通过实例代码展示了HTTP请求的实现过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的查询部分。

package com.example.network.http;

import java.net.MalformedURLException;
import java.net.URL;

public class URLDemo {

	public static void main(String[] args) throws MalformedURLException {
		
		String str_url = "http://google.com.hk/search?q=1";
		
		//1.把字符串封装为URL,会抛出MalformedURLException异常。
		URL url = new URL(str_url);
		
		System.out.println("getProtocol:"+url.getProtocol());
		System.out.println("getHost:"+url.getHost());
		System.out.println("getPort:"+url.getPort());
		System.out.println("getPath:"+url.getPath());
		//getFile() = getPath() + getQuery
		System.out.println("getFile:"+url.getFile());
		System.out.println("getQuery:"+url.getQuery());

	}

}


结果:

getProtocol:http
getHost:google.com.hk
getPort:-1
getPath:/search
getFile:/search?q=1
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、服务器代码:

package com.example.network.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;


public class MyHttpServer {

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

		ServerSocket ss = new ServerSocket(8090);
		
		Socket s = ss.accept();
		
		InputStream in = s.getInputStream();
		
		byte[] buf = new byte[1024];
		
		int len = in.read(buf);
		
		System.out.println(new String(buf,0,len));
		
		OutputStream out = s.getOutputStream();
		
		out.write("<font color=red>欢迎访问Http服务器!</font>".getBytes());
		
		s.close();
		ss.close();
	}

}

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、提交表单。

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登录信息</title>
</head>
<body>
	<form method="get" action="http://localhost:8090" >
		<table border="1">
			<tr><td>用户名:</td><td ><input name=name type="text"/></td></tr>
			<tr><td>密 码:</td><td ><input name=pass type="password"/></td></tr>
			<tr><th colspan="2"><input type="submit" value="登录"/></th></tr>
		</table>
	</form>
</body>
</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代码非常简单,如下:

<html>
<head>
	<meta charset="utf-8">
</head>
<body>
	<a href ="http://www.google.com">Goto Google</a>
</body>
</html>

2、我的浏览器代码

package com.example.network.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) throws  IOException {

		//与服务器建立连接
		Socket s = new Socket("127.0.0.1",8080);
		
		PrintWriter out = new PrintWriter(s.getOutputStream(),true);
		
		//模仿浏览器向服务器发送数据,下面三行是必须的,其他消息头信息可以根据需要发送,如果不设置,会以默认值发送(未验证)。
		out.println("GET /myweb/mypage.html HTTP/1.1");	
		out.println("Host: localhost:8090");		
		out.println();
		
		
		//读取服务器返回的数据并打印
		InputStream in = s.getInputStream();
		
		byte[] buf = new byte[1024];
		
		int len = in.read(buf);
		
		System.out.println(new String(buf,0,len));
		
		s.close();
		
		
		
	}

}

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。


代码示例:

package com.example.network.http;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

public class URLConnectionWithGet {

	public static void main(String[] args) throws IOException {
		//1.创建一个字符串形式的URL
		String str_url = "http://localhost:8080/myweb/mypage.html";
		//2.通过URL类的构造函数接收一个字符串形式的URL,来创建URL一个对象
		URL url = new URL(str_url);
		//3.调用openConnection()方法来返回会一个URLConnection对象,它是具体传输协议和URL的封装
		URLConnection conn = url.openConnection();
		//4.通过URLConnection来设置参数和请求属性,根据实际需要,不设置有默认值。
		conn.setRequestProperty("Content-Type", "text/html");
		//5.调用connect()方法建立与服务器的连接,或者直接调用getInputStream()方法,返回输入流,它可以读取服务器的应答体内容,此方法包含connect()的动作,所以可以跳过connect()。
		InputStream in = conn.getInputStream();
		//6.读取输入流并打印
		byte[] buf = new byte[1024];
		
		int len = in.read(buf);
		
		System.out.println(new String(buf,0,len));
		
		in.close();

	}

}


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


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

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



代码示例:

客户端:

package com.example.network.http;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;

public class URLConnecionWithPost {

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

				URL url = new URL("http://localhost:8080/myweb/login.jsp");
				
				URLConnection conn = url.openConnection();
				
				conn.setDoOutput(true);	
				OutputStream out = conn.getOutputStream();
				out.write("name=noodles&pass=26".getBytes());

				InputStream in = conn.getInputStream();
				byte[] buf = new byte[1024];	
				int len = in.read(buf);		
				System.out.println(new String(buf,0,len,"utf-8"));
				
				in.close();
				out.close();

	}

}


服务端:

<%@ page contentType="text/html; charset=UTF-8" language="java" errorPage="" %>
<%
request.setCharacterEncoding("UTF-8");
String name = request.getParameter("name");
String pass = request.getParameter("pass");
if(name.equals("noodles")
	&& pass.equals("26"))
{
	out.println("登录成功!");
}
else
{
	out.println("登录失败!");
}
%>

结果:


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


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值