本篇博客介绍另一种编程方式-基于http协议的网络编程,实现不同主机之间的通信。下面从HTTP、URL、URLConnection给出基于http协议的网络编程。
1、URL
URI:统一资源标识符,采用一种特定语法标识资源的字符串。通用的URI会告诉你资源是什么,是抽象的。
URL也是一种URI,但其除了标识一个资源,还会为资源提供一个特定的网络位置。和URI不同的是,URL会告诉你资源在哪里以及如何得到这个资源,是具体的。
URL类是Java程序在网络上定位和获取数据最简单的方法,我们无需关注所底层所使用的协议的细节,也不需要担心如何与服务器通信。只要把创建一个URL类的实例,就可以获得服务端的数据。下面看一个具体的示例:
package com.wygu.url;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
public class URLNetCommunicate {
public static void main(String[] args) {
BufferedReader reader = null;
try {
URL url = new URL("http://www.baidu.com/");
//利用方法openStream()获取URL的输入流
reader = new BufferedReader(new InputStreamReader(url.openStream()));
String readStr = null;
while((readStr=reader.readLine())!=null){
System.out.println(new String(readStr.getBytes(),"UTF-8"));
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(null!=reader){
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
在上面的示例中,首先创建一个URL的实例,然后利用方法openStream()打开输入流获取输入流BufferedReader对象reader,从reader中读取数据从而得到url所指定的资源文件。 对于所使用的openStream()方法只能读取服务端的数据,而不能发送数据。而如果既要读取数据又要发送数据,可以利用URL类的方法openConnection()创建一个URLConnection类的实例。本篇博客会在下面详细介绍。
2、HTTP
HTTP(超文本传输协议)是Web浏览器和Web服务器之间通信的标准协议。HTTP制定客户端与服务器如何建立连接、客户端如何从服务器请求数据,服务器如何响应请求,以及最后如何关闭连接。HTTP连接采用TCP/IP协议传输数据。
(1)HTTP协议解析
1)HTTP协议版本
HTTP/1.0:一个TCP连接只能发送一个Web请求;HTTP /1.0会为每个请求打开一个新的链接,使用完成后就关闭连接。
HTTP/1.1及以后版本:一个TCP连接可以连续发送对个请求和应答,此外请求和应答可以分为多个块发送;HTTP/1.1中,服务器不必在发送响应后就关闭连接,可以保持连接打开,在同一个socket上等待来自客户端的新请求。通过connection:Keep-Alive实现。
2)HTTP协议报文的请求和应答
在HTTP协议中,每个请求和响应都有同样的基本形式:一个首部行,一个包含元数据的HTTP首部,一个空行,然后是一个消息体:
Post请求
POST http://127.0.0.1:8080/cloud-pos-payment/TransServlet
POST data:
transType=CPU007
Request Headers:
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded;charset=utf-8
Content-Length: 1357
Host: 127.0.0.1:8080
User-Agent: Apache-HttpClient/4.2.6 (java 1.5)
可以看到请求头中字段都是以键-值对的形式出现。
2)HTTP方法
HTTP中主要有4个方法:GET,POST,PUT,DELETE。这四个方法均有特定的语义,应用程序必须遵循这些语义。下面主要介绍两种比较常见的方法:GET、POST。
GET方法:获取一个资源的表示,可以重复执行,输出会有缓存。GET请求在URL中会包括所有的必要信息,可以对GET请求加书签,或者进行链接和搜索,主要应用与非提交的动作。此外GET请求会把报文放在报文头中,因而其是可见的。
POST方法:将资源的一个表示上传到已知URL的服务器中。POST用于不能重复的不安全的操作。POST主要用于提交某个东西的动作。此外POST请求会把报文放在报文体中,因而其是不可见的。
例如:在购物车中增加一个商品应当发送一个GET请求,下订单时就应当发送一个POST请求。
3、URLConnection/HttpURLConnection
URLConnection是一个抽象类,和URL类相比(1)它对与HTTP服务器的交互提供了更多的控制;(2)它可以使用HTTP的请求方法POST、GET等向服务器发送数据。
直接使用URLConnection类的程序遵循以下步骤:
(1)构造一个URL实例;
(2)调用URL实例的方法openConnection()获得URLConnection实例;
(3)个性化配置URLConnection实例;
(4)获得输入流并读取数据;
(5)获得输出流并写入数据;
(7)关闭连接。
下面按照上述步骤,给出一个具体的服务端和客户端事例。
利用HttpServlet创建服务端:
package com.wygu.http;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServerServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doPost(request, response);
}
protected void doPost(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
System.out.println("收到来自客户端的请求报文.......");
//设置请求的编码格式
request.setCharacterEncoding("UTF-8");
//获得HTTP请求参数中的内容
String userName = request.getParameter("username");
String userPwd = request.getParameter("passward");
String strResponse ="";
if("wygu".equals(userName)&&"0123456".equals(userPwd)){
strResponse = "{" +
"respCode:'"+ "00" +"'," +
"respText:'Success'" +
"}";
}else{
strResponse = "{" +
"respCode:'"+ "01" +"'," +
"respText:'Fail'" +
"}";
}
System.out.println("发送客户端的应答报文为:"+strResponse);
//向response返回应答报文
PrintWriter out = response.getWriter();
out.print(strResponse);
//刷新缓冲流,使其立即发送
out.flush();
out.close();
}
}
服务端在web.xml中配置ServerServlet 映射关系:
<servlet>
<servlet-name>ServerServlet</servlet-name>
<servlet-class>com.wygu.http.ServerServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServerServlet</servlet-name>
<url-pattern>/ServerServlet</url-pattern>
</servlet-mapping>
客户端创建HTTP连接,并通过GET方法或者POST方法发送数据到服务端:
package com.wygu.http;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.ProtocolException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
public class Client {
//实现Map型数据转换为key1=value1&key2=value2的类型
public static String converMap2KeyValue(HashMap<String, String> map){
if(null==map || map.size()<1){
return "";
}
Iterator<Entry<String, String>> it= map.entrySet().iterator();
StringBuffer sf = new StringBuffer();
while (it.hasNext()) {
Entry<String, String> en = it.next();
sf.append(en.getKey() + "=" + en.getValue()
+ "&");
}
return sf.substring(0, sf.length() - 1);
}
public static void main(String[] args) {
HttpURLConnection httpURLConnection = null;
URL url = null;
PrintWriter writer = null;
BufferedReader reader = null;
try {
url = new URL("http://localhost:8080/http-communication/"+"ServerServlet"); //创建本地URL
httpURLConnection = (HttpURLConnection) url.openConnection(); //生成HttpURLConnection连接
} catch (IOException e) {
e.printStackTrace();
return;
}
httpURLConnection.setConnectTimeout(3000);// 连接超时时间,单位为毫秒
httpURLConnection.setReadTimeout(3000);// 读取结果超时时间,单位为毫秒
httpURLConnection.setDoInput(true); // 可读
httpURLConnection.setDoOutput(true); // 可写
httpURLConnection.setUseCaches(false);// 取消缓存
httpURLConnection.setRequestProperty("Content-type",
"application/x-www-form-urlencoded;charset=" + "utf-8");
try {
//httpURLConnection.setRequestMethod("GET");
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setRequestProperty("Connection", "Keep-Alive"); // 设置维持长连接
writer =new PrintWriter(new BufferedOutputStream(httpURLConnection.getOutputStream()));//获得HTTP连接的输出流
HashMap<String,String> reqMap = new HashMap<String,String>();
reqMap.put("username", "wygu");
reqMap.put("passward", "0123456");
writer.write(converMap2KeyValue(reqMap));
writer.flush();
int responseCode=httpURLConnection.getResponseCode();
if(responseCode==HttpURLConnection.HTTP_OK){//表示请求成功
reader = new BufferedReader(new InputStreamReader(httpURLConnection.getInputStream()));//获得HTTP连接的输入流
String readStr = null;
while((readStr=reader.readLine())!=null){
System.out.println(new String(readStr.getBytes(),"UTF-8"));
}
}else{
System.out.println("HTTP响应失败,响应码为:"+responseCode);
}
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(null!=writer){
writer.close();
}
if(null!=reader){
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
运行后,服务端输出结果为:
收到来自客户端的请求报文…….
发送客户端的应答报文为:{respCode:’00’,respText:’Success’}
客户端输出结果为:
{respCode:’00’,respText:’Success’}