JDK中的URLConnection、socket和Servlet在web应用接口开发中非常流行。本文对http接口方式、socket接口方式等进行了分析,给出代码实例和疑难问题进行了解释。
HttpURLConnection使用指南,主要引用网上一些技术资料。
本文档适合所有Java开发人员。
HTTP 定义了与服务器交互的不同方法,最基本的方法是 GET 和 POST。
l 事实上 GET 适用于多数请求,而保留 POST 仅用于更新站点。根据 HTTP 规范,GET 用于信息获取,而且应该是 安全的和 幂等的。所谓安全的意味着该操作用于获取信息而非修改信息。换句话说,GET 请求一般不应产生副作用。幂等的意 味着对同一 URL 的多个请求应该返回同样的结果。完整的定义并不像看起来那样严格。从根本上讲,其目标是当用户打开一个链接时,她可以确信从自身的角度来看没有改变资源。 比如,新闻站点的头版不断更新。虽然第二次请求会返回不同的一批新闻,该操作仍然被认为是安全的和幂等的,因为它总是返回当前的新闻。反之亦然。POST 请求就不那么轻松了。POST 表示可能改变服务器上的资源的请求。仍然以新闻站点为例,读者对文章的注解应该通过 POST 请求实现,因为在注解提交之后站点已经不同了(比方说文章下面出现一条注解);
l 在FORM 提交的时候,如果不指定Method,则默认为GET请求,Form中提交的数据将会附加在url之后,以?分开与url分开。字母数字字符原样发送,但 空格转换为“+“号,其它符号转换为%XX,其中XX为该符号以16进制表示的ASCII(或ISO Latin-1)值。GET请求请提交的数据放置在HTTP请求协议头中,而POST提交的数据则放在实体数据中;
具体实例参见:POST请求内容实例
1. get是从服务器上获取数据,post是向服务器传送数据。 2. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到。post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML HEADER内一起传送到ACTION属性所指的URL地址。用户看不到这个过程。 3. 对于get方式,服务器端用Request.QueryString获取变量的值,对于post方式,服务器端用Request.Form获取提交的数据。 4. get传送的数据量较小,不能大于2KB。post传送的数据量较大,一般被默认为不受限制。但理论上,IIS4中最大量为80KB,IIS5中为100KB。 5. get安全性非常低,post安全性较高。
A URLConnection with support for HTTP-specific features. See the spec for details.
Each HttpURLConnection instance is used to make a single request but the underlying network connection to the HTTP server may be transparently shared by other instances. Calling the close() methods on the InputStream or OutputStream of an HttpURLConnection after a request may free network resources associated with this instance but has no effect on any shared persistent connection. Calling the disconnect() method may close the underlying socket if a persistent connection is otherwise idle at that time.
URLConnection的对象,如下代码示例:
// 下面的index.jsp由<servlet-mapping>映射到 // 一个Servlet(com.quantanetwork.getClientDataServlet) // 该Servlet的注意点下边会提到 URL url = new URL("http://localhost:8080/TestHttpURLConnectionPro/index.jsp");
URLConnection rulConnection = url.openConnection();// 此处的urlConnection对象实际上是根据URL的 // 请求协议(此处是http)生成的URLConnection类 // 的子类HttpURLConnection,故此处最好将其转化 // 为HttpURLConnection类型的对象,以便用到 // HttpURLConnection更多的API.如下:
HttpURLConnection httpUrlConnection = (HttpURLConnection) rulConnection;
// 设置是否向httpUrlConnection输出,因为这个是post请求,参数要放在 // http正文内,因此需要设为true, 默认情况下是false; httpUrlConnection.setDoOutput(true);
// 设置是否从httpUrlConnection读入,默认情况下是true; httpUrlConnection.setDoInput(true);
// Post 请求不能使用缓存 httpUrlConnection.setUseCaches(false);
// 设定传送的内容类型是可序列化的java对象 // (如果不设此项,在传送序列化对象时,当WEB服务默认的不是这种类型时可能抛java.io.EOFException) httpUrlConnection.setRequestProperty("Content-type", "application/x-java-serialized-object");
// 设定请求的方法为"POST",默认是GET httpUrlConnection.setRequestMethod("POST");
// 连接,从上述第2条中url.openConnection()至此的配置必须要在connect之前完成, httpUrlConnection.connect();
// 此处getOutputStream会隐含的进行connect(即:如同调用上面的connect()方法, // 所以在开发中不调用上述的connect()也可以)。 OutputStream outStrm = httpUrlConnection.getOutputStream();
// 现在通过输出流对象构建对象输出流对象,以实现输出可序列化的对象。 ObjectOutputStream objOutputStrm = new ObjectOutputStream(outStrm);
// 向对象输出流写出数据,这些数据将存到内存缓冲区中 objOutputStrm.writeObject(new String("我是测试数据"));
// 刷新对象输出流,将任何字节都写入潜在的流中(些处为ObjectOutputStream) objOutputStm.flush();
// 关闭流对象。此时,不能再向对象输出流写入任何数据,先前写入的数据存在于内存缓冲区中, // 在调用下边的getInputStream()函数时才把准备好的http请求正式发送到服务器 objOutputStm.close();
// 调用HttpURLConnection连接对象的getInputStream()函数, // 将内存缓冲区中封装好的完整的HTTP请求电文发送到服务端。 InputStream inStrm = httpConn.getInputStream(); // <===注意,实际发送请求的代码段就在这里
// 上边的httpConn.getInputStream()方法已调用,本次HTTP请求已结束,下边向对象输出流的输出已无意义, // 既使对象输出流没有调用close()方法,下边的操作也不会向对象输出流写入任何数据. // 因此,要重新发送数据时需要重新创建连接、重新设参数、重新创建流对象、重新写数据、 // 重新发送数据(至于是否不用重新这些操作需要再研究) objOutputStm.writeObject(new String("")); httpConn.getInputStream();
l HttpURLConnection的connect()函数,实际上只是建立了一个与服务器的tcp连接,并没有实际发送http请求。 无论是post还是get,http请求实际上直到HttpURLConnection的getInputStream()这个函数里面才正式发送出去。
l 在用POST方式发送URL请求时,URL请求参数的设定顺序是重中之重, 对connection对象的一切配置(那一堆set函数) 都必须要在connect()函数执行之前完成。而对outputStream的写操作,又必须要在inputStream的读操作之前。 这些顺序实际上是由http请求的格式决定的。 如果inputStream读操作在outputStream的写操作之前,会抛出例外: java.net.ProtocolException: Cannot write output after reading input.......
l http请求实际上由两部分组成, 一个是http头,所有关于此次http请求的配置都在http头里面定义, 一个是正文content。 connect()函数会根据HttpURLConnection对象的配置值生成http头部信息,因此在调用connect函数之前, 就必须把所有的配置准备好。
l 在http头后面紧跟着的是http请求的正文,正文的内容是通过outputStream流写入的, 实际上outputStream不是一个网络流,充其量是个字符串流,往里面写入的东西不会立即发送到网络,而是存在于内存缓冲区中,待outputStream流关闭时,根据输入的内容生成http正文。 至此,http请求的东西已经全部准备就绪。在getInputStream()函数调用的时候,就会把准备好的http请求 正式发送到服务器了,然后返回一个输入流,用于读取服务器对于此次http请求的返回信息。由于http请求在getInputStream的时候已经发送出去了(包括http头和正文),因此在getInputStream()函数之后对connection对象进行设置(对http头的信息进行修改)或者写入outputStream(对正文进行修改) 都是没有意义的了,执行这些操作会导致异常的发生。
l 对于客户端发送的POST类型的HTTP请求,Servlet必须实现doPost方法,而不能用doGet方法。
l 用HttpServletRequest的getInputStream()方法取得InputStream的对象,比如: InputStream inStream = httpRequest.getInputStream(); 现在调用inStream.available()(该方法用于“返回此输入流下一个方法调用可以不受阻塞地从此输入流读取(或跳过)的估计字节数”)时,永远都反回0。试图使用此方法的返回值分配缓冲区, 以保存此流所有数据的做法是不正确的。那么,现在的解决办法是 Servlet这一端用如下实现: InputStream inStream = httpRequest.getInputStream(); ObjectInputStream objInStream = new ObjectInputStream(inStream); Object obj = objInStream.readObject(); // 做后续的处理 // 。。。。。。 // 。。。 。。。 而客户端,无论是否发送实际数据都要写入一个对象(那怕这个对象不用),如: ObjectOutputStream objOutputStrm = new ObjectOutputStream(outStrm); objOutputStrm.writeObject(new String("")); // 这里发送一个空数据 // 甚至可以发一个null对象,服务端取到后再做判断处理。 objOutputStrm.writeObject(null); objOutputStrm.flush(); objOutputStrm.close();
注意:上述在创建对象输出流ObjectOutputStream时,如果将从HttpServletRequest取得的输入流 (即:new ObjectOutputStream(outStrm)中的outStrm)包装在BufferedOutputStream流里面, 则必须有objOutputStrm.flush();这一句,以便将流信息刷入缓冲输出流.如下: ObjectOutputStream objOutputStrm = new ObjectOutputStream(new BufferedOutputStream(outStrm)); objOutputStrm.writeObject(null); objOutputStrm.flush(); // <======此处必须要有. objOutputStrm.close();
常用的API包括:
l java.net.Socket
此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器之间的通信端点。
套接字的实际工作由 SocketImpl
类的实例执行。应用程序通过更改创建套接字实现的套接字工厂可以配置它自身,以创建适合本地防火墙的套接字。
l public class InetSocketAddress
extends SocketAddress
此类实现 IP 套接字地址(IP 地址 + 端口号)。它还可以是一个对(主机名 + 端口号),在此情况下,将尝试解析主机名。如果解析失败,则该地址将被视为未解析 地址,但是其在某些情形下仍然可以使用,比如通过代理连接。
它提供不可变对象,供套接字用于绑定、连接或用作返回值。
通配符 是一个特殊的本地 IP 地址。它通常表示“任何”,只能用于 bind
操作
public MonthBookDataResponse sendBookDataToBill(MonthBookData mbd,
String BPAdress, int port) throws Exception {
Socket connection = null;
try {
connection = new Socket();
SocketAddress socketAddress = new InetSocketAddress(InetAddress
.getByName(BPAdress), port);
connection.connect(socketAddress, 10000);
connection.setSoTimeout(10000);// 设置取返回消息超时时间
InputStream input = connection.getInputStream();
OutputStream output = connection.getOutputStream();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
mbd.sendMonthDataToStream(byteArrayOutputStream);
byteArrayOutputStream.writeTo(output);
output.flush();
MonthBookDataResponse mbdr = MonthBookDataResponse
.getMonthBookDataResponse(input);
connection.close();
connection = null;
return mbdr;
} catch (UnknownHostException e) {
throw new Exception("向bill系统发送订购请求发生异常", e);
} catch (SocketTimeoutException e) {
throw new Exception("向bill系统发送订购请求发生异常", e);
} catch (InterruptedIOException e) {
throw new Exception("向bill系统发送订购请求发生异常", e);
} catch (IOException e) {
throw new Exception("向bill系统发送订购请求发生异常", e);
} finally {
try {
if (connection != null && !connection.isClosed()) {
connection.close();
connection = null;
}
} catch (IOException e) {
}
}
}
// BufferedOutputStream
/* The class implements a buffered output stream. By setting up such
* an output stream, an application can write bytes to the underlying
* output stream without necessarily causing a call to the underlying
* system for each byte written.
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(httpURLConnection.getOutputStream());
bufferedOutputStream.write(requestMessageXml.getBytes("UTF-8"));
bufferedOutputStream.flush();
bufferedOutputStream.close();
// BufferedReader
* Read text from a character-input stream, buffering characters so as to
* provide for the efficient reading of characters, arrays, and lines.
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(httpURLConnection.getInputStream()));
StringBuilder responseMessageXml = new StringBuilder();
String tempLine = new String();
while((tempLine = bufferedReader.readLine()) != null) {
responseMessageXml.append(tempLine);
}
bufferedReader.close();
1. 异常: java.net.ProtocolException: Cannot write output after reading input.
答:Each HttpURLConnection instance is used to make a single request but the underlying network connection to the HTTP server may be transparently shared by other instances.
2.
1. XDSync项目 https://202.108.60.24/svn/02开发区/XDSync
2. 软件ieHTTPHeaders v1.6,可以获得页面请求信息。
GET /ecbm/users/cfgSubscriberAction.do?layoutCollection=0&&layoutCollectionState=0&pagerPage=1&reqCode=query&issort=false&pageSize=10&reqCode=query HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://localhost:8081/ecbm/users/cfgSubscriberAction.do?reqCode=query
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: localhost:8081
Connection: Keep-Alive
Cookie: JSESSIONID=abc0RNHfd5K_Q53kVCDRr
HTTP/1.1 200 OK
Server: Resin/ 3.0.21
Cache-Control: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html; charset=GB2312
Transfer-Encoding: chunked
Date: Tue, 01 Jul 2008 07:11:21 GMT
GET /ecbm/qframe/calendar/popup.jsp HTTP/1.1
Accept: */*
Referer: http://localhost:8081/ecbm/users/cfgSubscriberAction.do?layoutCollection=0&&layoutCollectionState=0&pagerPage=1&reqCode=query&issort=false&pageSize=10&reqCode=query
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)
Host: localhost:8081
Connection: Keep-Alive
Cookie: JSESSIONID=abc0RNHfd5K_Q53kVCDRr
HTTP/1.1 200 OK
Server: Resin/ 3.0.21
Content-Type: text/javascript; charset=GBK
Transfer-Encoding: chunked
Date: Tue, 01 Jul 2008 07:11:21 GMT
工具为:ieHTTPHeaders v1.6
详细内容:
POST /usboss42/users/cfgSaleAgentAction.do?reqCode=add HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://10.130.33.140:8081/usboss42/users/cfgSaleAgentAction.do?reqCode=gotoAdd
Accept-Language: zh-cn
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; TencentTraveler )
Host: 10.130.33.140:8081
Content-Length: 158
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: ys-ext-comp-1085=null; ys-ext-comp-1084=null; JSESSIONID=abcYVnIUdce 1c 53HnR-Nr
reqCode=&saId=234244&saName=234244&description=&address=&artificialPerson=&taxNumber=&bank=&bankAccount=&areaCode=0086&areaName=%C8%AB%B9%FA®isteredMoney=0
HTTP/1.1 200 OK
Server: Resin/ 3.0.22
Cache-Control: no-cache
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Content-Type: text/html; charset=GBK
Transfer-Encoding: chunked
Date: Mon, 19 May 2008 05:39:57 GMT
GET /usboss42/qframe/calendar/popup.jsp HTTP/1.1
Accept: */*
Referer: http://10.130.33.140:8081/usboss42/users/cfgSaleAgentAction.do?reqCode=add
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; TencentTraveler )
Host: 10.130.33.140:8081
Connection: Keep-Alive
Cookie: ys-ext-comp-1085=null; ys-ext-comp-1084=null; JSESSIONID=abcYVnIUdce 1c 53HnR-Nr
HTTP/1.1 200 OK
Server: Resin/ 3.0.22
Content-Type: text/javascript; charset=GBK
Transfer-Encoding: chunked
Date: Mon, 19 May 2008 05:39:57 GMT
GET /usboss42/images/iconInformation.gif HTTP/1.1
Accept: */*
Referer: http://10.130.33.140:8081/usboss42/users/cfgSaleAgentAction.do?reqCode=add
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
If-Modified-Since: Thu, 10 Apr 2008 07:59:46 GMT
If-None-Match: "/b3/UhPbTjz"
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; TencentTraveler )
Host: 10.130.33.140:8081
Connection: Keep-Alive
Cookie: ys-ext-comp-1085=null; ys-ext-comp-1084=null; JSESSIONID=abcYVnIUdce 1c 53HnR-Nr
HTTP/1.1 304 Not Modified
Server: Resin/ 3.0.22
ETag: "/b3/UhPbTjz"
Content-Length: 0
Date: Mon, 19 May 2008 05:39:57 GMT