文章目录
- 1. 断开与服务器的连接
- 2. 处理服务器响应
- 3. 错误条件
- 4. 重定向
- 5. 代理
- 6. 流模式
1. 断开与服务器的连接
HTTP1.1 支持持久连接,允许通过一个TCP socket发送多个请求和响应。不过使用Keep-Alive时,服务器不会因为已经向客户端发送了最后一个字节的数据就关闭连接。服务器会超时并关闭连接,可能有5秒处于非活动状态。不过最好还是通过客户端在确认工作结束时关闭连接。HttpURLConnection类透明的支持HTTP Keep-Alive,除非显示将其关闭。也就是说,在服务器关闭连接之前,如果再次连接同一个服务器,它会重用Socket。一旦知道与一个特定主机的会话结束,disconnect方法允许客户端断开连接。
public abstract void disconnect()
如果这个连接上还有打开的流,disconnect将会关闭这些流。
2. 处理服务器响应
通常响应消息中我们只需要数字响应码,HttpURLConnection还有一个getResponse()方法,会以int返回这个响应码
public int getResponseCode()throws IOException
响应码后面的文本字符称为响应消息,可以由一个getResponseMessage方法返回
public String getResponseMessage()throws IOException
HTTP 1.0 定义了16个响应码。HTTP 1.1扩展为了40个不同的响应码。虽然有些数字(比如著名的404)几乎已经成为其语义含义的代名词,但大多数我们都还不太熟悉。HttpURLConnectiooon类包括36个命名常量,如HttpURLConnection.OK 和Http URLConnection和HttpURLConnection.NOT_FOUND,表示最常见的一些响应码。
下面程序获取响应首部和响应内容
public class QuizCardBuilder {
public static void main(String[] args) throws IOException {
try{
URL u=new URL("http://www.baidu.com");
HttpURLConnection urlConnection=(HttpURLConnection) u.openConnection();
int code=urlConnection.getResponseCode();
String response=urlConnection.getResponseMessage();
System.out.println("HTTP/1.x"+code+" "+response);
for(int j=1;;j++){
String header=urlConnection.getHeaderField(j);
String key=urlConnection.getHeaderFieldKey(j);
if(header==null|| key==null)break;
System.out.println(urlConnection.getHeaderField(j)+": "+header);
}
System.out.println();
try(InputStream in=urlConnection.getInputStream()){
InputStream buffer=new BufferedInputStream(in);
InputStreamReader reader=new InputStreamReader(buffer);
int c;
while((c=reader.read())!=-1){
System.out.print((char)c);
}
}
}catch (MalformedURLException e) {
System.err.println("www.baidu.com is not a parseable URL");
}catch (IOException e){
System.err.println(e);
}
}
}
3. 错误条件
有些情况下,服务器如果没有检索到用户的URL,它可能并不会返回404,而是在消息主体中返回有用的消息(帮我检索信息的页面)。getErrorStream
方法返回一个InputStream,其中就包含这个页面。如果URL没有错误时这个流就返回null。
public class QuizCardBuilder {
public static void main(String[] args) throws IOException {
try{
URL u=new URL("http://www.baidu.com");
HttpURLConnection urlConnection=(HttpURLConnection) u.openConnection();
int code=urlConnection.getResponseCode();
String response=urlConnection.getResponseMessage();
System.out.println("HTTP/1.x"+code+" "+response);
for(int j=1;;j++){
String header=urlConnection.getHeaderField(j);
String key=urlConnection.getHeaderFieldKey(j);
if(header==null|| key==null)break;
System.out.println(urlConnection.getHeaderField(j)+": "+header);
}
System.out.println();
try(InputStream in=urlConnection.getInputStream()){
InputStream buffer=new BufferedInputStream(in);
InputStreamReader reader=new InputStreamReader(buffer);
int c;
while((c=reader.read())!=-1){
System.out.print((char)c);
}
}
System.out.println("此时的errorStream:"+urlConnection.getErrorStream());
}catch (MalformedURLException e) {
System.err.println("www.baidu.com is not a parseable URL");
}catch (IOException e){
System.err.println(e);
}
}
}
4. 重定向
300一级的响应码都会表示某种重定向,即请求的资源不在期望的位置上,但有可能会在其他位置找到。遇到这种情况,浏览器会自动从新位置加载文档。但这是存在风险的,因为这样很有可能将用户从一个可信的网站转移到一个不可信的网站,而用户毫无察觉。默认情况下,HttpURLConnection会跟随重定向,不过HttpURLConnection有两个静态方法,允许你确定是否跟随重定向:
public static boolean getFollowRedirects()
public static void setFollowRedirects(bool)
如果跟随重定向,getFollowRedirects()方法就会返回true,否则返回false。如果系统安全管理器不允许修改重定向值,那么set方法会返回一个SecurityException
。Java有两个方法可以针对各个实例配置重定向(上面静态方法是类所以,相当于给所有实例配置重定向)
public boolean getInstanceFollowRedirects()
public void setInstanceFollowRedirects()
public class QuizCardBuilder {
public static void main(String[] args) throws IOException {
try{
URL u=new URL("http://www.baidu.com");
HttpURLConnection urlConnection=(HttpURLConnection) u.openConnection();
System.out.println("实例重定向信息:"+urlConnection.getInstanceFollowRedirects());
System.out.println("类重定向信息:"+HttpURLConnection.getFollowRedirects());
}catch (MalformedURLException e) {
System.err.println("www.baidu.com is not a parseable URL");
}catch (IOException e){
System.err.println(e);
}
}
}
5. 代理
许多防火墙后面的用户或者使用AOL或其他大吞吐量ISP的用户会通过代理服务器访问Web。usignProxy()方法可以指处某个HttpURLConnection是否通过代理服务器
public abstract boolean usingProxy()
如果使用了代理,这个方法就会返回true,否则返回false。在某些环境中,使用代理服务器可能存在安全方面的信息
public class QuizCardBuilder {
public static void main(String[] args) throws IOException {
try{
URL u=new URL("http://www.baidu.com");
HttpURLConnection urlConnection=(HttpURLConnection) u.openConnection();
System.out.println("是否使用代理:"+urlConnection.usingProxy());
}catch (MalformedURLException e) {
System.err.println("www.baidu.com is not a parseable URL");
}catch (IOException e){
System.err.println(e);
}
}
}
6. 流模式
每个发送给HTTP服务器的请求都有一个HTTP首部,首部中有一个Content-length字段,即请求主体中的字节数。首部在主体前面。不过,要写入首部,需要知道主体的长度,而在写首部的时候可能还不知道主体的长度,正常情况下,对于这个两难的问题,Java的解决办法是:对于HttpURLConnection获取的OutputStream,将写入此OutputStream的所有内容缓存,直到流关闭。此时它就可知道主体中有多少字节,所有有足够的信息来写入Content-length首部。这种模式对于响应典型的web表达的短请求载合适不过,不过对于非常长的表达或一些SOAP消息(SOAP 是基于 XML 的简易协议,可使应用程序在 HTTP 之上进行信息交换),消息负担会很大。用HTTP put发送中等到大型文档时会很浪费,也很慢,如果Java通过网络发送第一个字节之前,不需要等待写入的最后一个字节,将会高效很多。Java为这个问题提供了两种解决方法,如果你知道数据的大小,例如可以将数据的大小告诉HttpURLConnection对象。如果预先不知道数据大小,可以使用分块传输编码方式,在分块传输编码方式中,请求主体以多个部分发送,每个部分都有自己单独的内容长度。要启用分块传输编码方式,只要在连接URI之前将分块大小传入setChunkedSteamingMode(int chunkLength)方法,不过分块传输编码方式会妨碍身份认证和重定向。如果试图重定向URL或需要口令认证的URL发送分块文件,就会抛出HttpRetryException。除非确实需要,否则不要使用分块传输编码方式。
如果恰好知道请求数据的大小,可以将这个信息提供给HttpURLConnection对象,从而优化连接。如果这样做,Java会立即通过网络将数据以流方式发送。否则它必须缓存你写入的数据来确定内容长度,而且只有在你关闭流之后才能通过网络发送数据。如果知道具体数据的大小,可以将这个数据传递给下面方法:
public void setFixedLengthStreamingMode(int contentLength)
public void setFixedLengthStreamingMode(long contentLength)
java会在HTTP首部的Content-Length中使用这个数。不过,如果接下来试图写入数据多于或少于给出的这个字节数,Java会抛出IOException异常。