之前看了些预备知识,最近在利用这些技巧来实现科协布置的项目:根据tr-069协议实现模拟的ACS端和CPE端。好不容易把CPE端写出了个“形”,迫不及待的就想晒晒心得。先介绍一下这个协议吧:
<!----><!----> <!---->TR-069 规范是 属于 TR-046 规定的 B-NT 自动配置框架中高级复杂业务协议配置部分。主要由自动配置管理服务器 ( Automatic Configuration Server ACS ),用户驻地设备( Customer Premises Equipment CPE ,即被管终端),业务配置管理服务器以及一些必要的管理接口组成。它通过定义一套 ACS 和 CPE 之间自动协商交互协议,实现远程管理的功能。如图1 所示,宏观上主要有两个接口,一个时完成从业务或服务提供商向 ACS 下发业务配置的北向接口( ACS Northbound Interface ),另外一个是完成从 ACS 到 CPE 配置管理的南向接口( ACS Southbound Interface )。
我实现的这个CPE端的功能大致如下:首先,使用HTTPClient提供的API向ACS服务器发送一个没有内容的HTTP-Get请求——模拟CPE上电后的首次建连;在收到服务器返回的响应后,通过HTTP-Post发送一封 SOAP 内含Inform请求——请求建立一个会话;如果紧接着收到一封包含InformResponse方法的SOAP回复,按协议的说法表明会话已经成功建立,再次发送一个空的HTTP请求——告诉服务器可以开始远程管理了……会话的连通过程中,服务器可以发送协议规定的SOAP请求到CPE端,CPE作相应的解析、处理、回复。最后服务器Post一个空的HTTP请求来关闭这次会话。
实现程序里的连接服务器功能的是HTTPClient(Apache的项目)提供的GetMethod和PostMethod方法。大概用法是:
// Create an instance of HttpClient. HttpClient client = new HttpClient(); // Set authentication. client.getState().setCredentials( new AuthScope(AuthScope.ANY_HOST, 8080, AuthScope.ANY_REALM), new UsernamePasswordCredentials("username", "password")); // Create a method instance. //PostMethod is similar GetMethod get = new GetMethod(URL); // Tell the GET method to automatically handle authentication. get.setDoAuthentication(true); // Provide custom retry handler . get.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, new DefaultHttpMethodRetryHandler(3, false)); int status; // Execute the method. try { status = client.executeMethod(get); } catch (HttpException e) { System.err.println("Fatal protocol violation: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { System.err.println("Fatal transport error: " + e.getMessage()); e.printStackTrace(); } finally { // Release the connection. get.releaseConnection(); }
HTTPClient的功能很全面,还能处理、设置各种报头。
SOAP消息的生成和解析就用到了我之前的文章里提供的方法:“建厂”->实现信封->实现header->实现body。而功能逻辑参考了OpenACS的解析方法。在调试程序的各项功能的时候,一个“泥潭”让我深刻认识到SOAP(或者说是解析SOAP的API)是区分大小写的。这个泥潭出现在一个前缀:“SOAP-ENV”上:开始我把body元素的这个前缀弄成了小写,因为这个前缀是API自动生成的,没有在代码里显示,我也压根没考虑它的大小写问题。我用服务器向客户端发送SOAP消息,报头的信息都能正确得到,状态码也是200,可就是获取不了SOAP消息。我尝试删除一些SOAP内容,最后就发现问题出在了加载这个前缀的那行代码上,于是就发现了这个问题。改成大写的,一切OK。
event.setAttribute("SOAP-ENV:arrayType", "xsd:EventStruct[64]");