webservice学习(一)socket

 

WebService系列文章: 
【WebService】自定义WebService服务及其调用 
【WebService】wsdl配置详解以及使用注解修改wsdl配置 
【WebService】CXF处理javaBean等复合类型以及Map等复杂类型的数据 
【WebService】CXF拦截器的设置以及自定义CXF拦截器

1. webservice是啥

  准确的来说,webservice不是一种技术,而是一种规范。是一种跨平台,跨语言的规范,用于不同平台,不同语言开发的应用之间的交互。 
  举个例子,比如在Windows Server服务器上有个C#.Net开发的应用A,在Linux上有个Java语言开发的应用B,现在B应用要调用A应用,或者是互相调用,用于查看对方的业务数据,就需要webservice的规范。 
  再举个例子,天气预报接口。无数的应用需要获取天气预报信息,这些应用可能是各种平台,各种技术实现,而气象局的项目,估计也就一两种,要对外提供天气预报信息,这个时候,如何解决呢?webservice就是出于以上类似需求而定义出来的规范。 
  我们一般就是在具体平台开发webservice接口,以及调用webservice接口,每种开发语言都有自己的webservice实现框架。比如Java 就有 Apache Axis1、Apache Axis2、Codehaus XFire、Apache CXF、Apache Wink、Jboss RESTEasyd等等。其中Apache CXF用的比较多,它也可以和Spring整合。

2. 重温socket

在分析如何调用webservice前,先来回忆一下传统的socket是如何通信的,这样更容易理解ws。

2.1 基于socket创建web服务

为什么要使用socket呢?看一下下面的原理图: 
这里写图片描述 
  从图中可以看出,程序A和程序B之间是无法实现直接调用的,那么现在A需要访问B的话,A即创建一个socket并制定B机器的端口号,在此之前B已经在本机创建好了socket等待用户来连接,A和B连接成功后,即可向B发送请求获取数据了,这很好理解,为了回忆一下socket的创建和使用,下面先写一个简单的socket通信的demo,服务端可以将小写字母转大写。

2.2 经典的socket服务

客户端:

<span style="color:#000000"><code class="language-java"><span style="color:#880000">/**
 *<span style="color:#4f4f4f"> @Description</span> Socket客户端,用来发送给服务端请求
 *<span style="color:#4f4f4f"> @author</span> Ni Shengwu
 *
 */</span>
<span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#4f4f4f">SocketClient</span> {

    <span style="color:#000088">public</span> <span style="color:#000088">static</span> <span style="color:#000088">void</span> <span style="color:#009900">main</span>(String[] args) <span style="color:#000088">throws</span> Exception {
        Scanner input = <span style="color:#000088">new</span> Scanner(System.in);

        <span style="color:#880000">// 1: 创建一个基于TCP协议的socket服务,在建立对象时,要指定连接服务器和端口号</span>
        Socket sc = <span style="color:#000088">new</span> Socket(<span style="color:#009900">"127.0.0.1"</span>, <span style="color:#006666">9999</span>);
        <span style="color:#880000">// 2: 通过建立的Socket对象获取Socket中的输出流,调用getOutStream方法</span>
        OutputStream out = sc.getOutputStream();

        System.out.println(<span style="color:#009900">"请输入要转化的字母:"</span>);
        String initData = input.next();<span style="color:#880000">//获取控制台的输入</span>

        <span style="color:#880000">// 3: 写入到Socket输出流中</span>
        out.write(initData.getBytes());
        System.out.println(<span style="color:#009900">"等待服务器端返回数据"</span>);

        <span style="color:#880000">// 4: 通过建立的Socket对象获取Socket中的输入流,输入流会接受来自服务器端数据</span>
        InputStream in = sc.getInputStream();
        <span style="color:#000088">byte</span>[] b = <span style="color:#000088">new</span> <span style="color:#000088">byte</span>[<span style="color:#006666">1024</span>];

        <span style="color:#880000">// 5: 获取输入字节流的数据,注意此方法是堵塞的,如果没有获取数据会一直等待</span>
        <span style="color:#000088">int</span> len = in.read(b);
        System.out.println(<span style="color:#009900">"返回的结果为:"</span> + <span style="color:#000088">new</span> String(b, <span style="color:#006666">0</span>, len));

        <span style="color:#880000">// 关闭Socket</span>
        out.close();
        in.close();
        sc.close();
        input.close();
    }
}</code></span>

服务端:

<span style="color:#000000"><code class="language-java"><span style="color:#880000">/**
 *<span style="color:#4f4f4f"> @Description</span> Socket服务端,用来接收客户端请求,实现转大写功能
 *<span style="color:#4f4f4f"> @author</span> Ni Shengwu
 *
 */</span>
<span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#4f4f4f">SocketServer</span> {

    <span style="color:#000088">public</span> <span style="color:#000088">static</span> <span style="color:#000088">void</span> <span style="color:#009900">main</span>(String[] args) <span style="color:#000088">throws</span> Exception {

        <span style="color:#880000">// 1:建立服务器端的tcp socket服务,必须监听一个端口</span>
        ServerSocket ss = <span style="color:#000088">new</span> ServerSocket(<span style="color:#006666">9999</span>);
        <span style="color:#000088">while</span>(<span style="color:#000088">true</span>) {
            System.out.println(<span style="color:#009900">"等待客户端请求……"</span>);

            <span style="color:#880000">// 2: 通过服务器端的socket对象的accept方法获取连接上的客户端对象,没有则堵塞,等待</span>
            Socket socket = ss.accept();
            System.out.println(<span style="color:#009900">"握手成功……"</span>);

            <span style="color:#880000">// 3: 通过输入流获取数据</span>
            InputStream input = socket.getInputStream();
            <span style="color:#000088">byte</span>[] b = <span style="color:#000088">new</span> <span style="color:#000088">byte</span>[<span style="color:#006666">1024</span>];
            <span style="color:#000088">int</span> len = input.read(b);
            String data = <span style="color:#000088">new</span> String(b, <span style="color:#006666">0</span>, len);
            System.out.println(<span style="color:#009900">"客户端数据为:"</span> + data);

            <span style="color:#880000">// 4: 通过服务器端Socket输出流,写数据,会传送到客户端Socket输入流中</span>
            OutputStream out = socket.getOutputStream();
            out.write(data.toUpperCase().getBytes());

            <span style="color:#880000">// 5: 关闭socket</span>
            out.close();
            input.close();
            socket.close();

        }
    }
}</code></span>

  这个demo很简单,先开启服务端的程序,在那等待,然后开启客户端程序,如果在控制台输入hello过去,就会从服务端返回一个HELLO回来,这说明socket通信是成功的。

2.3 web程序访问socket service

  上面经典的demo是在本地写的两个java程序,我们现在的很多项目都是web项目,也就是通过浏览器来交互的,我们来看下通过浏览器的方式如何来访问socketService服务。服务端还是使用上面的那个java程序,客户端我改成浏览器请求,新写一个jsp如下:

<span style="color:#000000"><code class="language-jsp"><%@ page language=<span style="color:#009900">"java"</span> import=<span style="color:#009900">"java.util.*"</span> pageEncoding=<span style="color:#009900">"UTF-8"</span>%>

<span style="color:#4f4f4f"><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"></span>
<span style="color:#006666"><<span style="color:#4f4f4f">html</span>></span>
  <span style="color:#006666"><<span style="color:#4f4f4f">head</span>></span>   
  <span style="color:#006666"></<span style="color:#4f4f4f">head</span>></span> 
  <span style="color:#006666"><<span style="color:#4f4f4f">body</span>></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">form</span> <span style="color:#4f4f4f">action</span>=<span style="color:#009900">"http://127.0.0.1:9999"</span> <span style="color:#4f4f4f">method</span>=<span style="color:#009900">"post"</span>></span>
        <span style="color:#006666"><<span style="color:#4f4f4f">input</span> <span style="color:#4f4f4f">type</span>=<span style="color:#009900">"text"</span> <span style="color:#4f4f4f">name</span>=<span style="color:#009900">"sname"</span>></span>
        <span style="color:#006666"><<span style="color:#4f4f4f">input</span> <span style="color:#4f4f4f">type</span>=<span style="color:#009900">"submit"</span> <span style="color:#4f4f4f">value</span>=<span style="color:#009900">"提交"</span>></span>
    <span style="color:#006666"></<span style="color:#4f4f4f">form</span>></span>
  <span style="color:#006666"></<span style="color:#4f4f4f">body</span>></span>
<span style="color:#006666"></<span style="color:#4f4f4f">html</span>></span></code></span>

  注意看action的请求地址,包括端口要和服务端的一样,这样当我们提交的时候就可以访问上面的服务端程序了。看一下服务端的运行结果: 
这里写图片描述 
  可以看到,web程序确实和服务端握手成功了,而且数据也是可以传过去的,sname=hello,包括一些Http信息都可以传过去,但是我们再来看看服务端返回给浏览器的数据是啥: 
这里写图片描述 
  我用的是chrome浏览器,其他浏览器可能还没有数据,这都有可能,但是从数据中来看,它只是单纯的把所有信息全部转成了大写……而且它也没有Http的返回格式,也就是说,我所需要的就是个大写的HELLO即可,所以这是有问题的。 
  所以可以总结一下:不同的协议其实也是支持Socket通信的。 web程序可以调用socket请求,但是由于协议不同,因此在处理的时候要过滤http的协议格式,返回的时候还需要添加 http返回的格式,否则就会出现问题,可想而知,如果还要处理协议格式,是很麻烦的。 
  所以到这里,基本上就理解了为什么传统的socket无法满足需求了,其实除了上面的弊端外,还有其他的弊端,比如如果参数一多,就不好维护等等,这里就不多举例了。

3. 调用已发布的WebService

  关于webservice本身,我就不做过多的描述了,在最上面也有简单介绍,既然传统的socket通信无法满足,那么下面开始来调用已发布的ws,真正走进ws的世界。 
  有一个站点:http://www.webxml.com.cn,是上海的一家公司做的,上面提供了很多ws服务,其中有一个查询号码归属地的功能,我们用它来做测试。先来在它们的站点中测试一下,然后再在本地写程序来调用这个ws服务获取查询结果。 
  看一下站点上的查询: 
这里写图片描述 
进入手机号码归属地查询web服务后, 
这里写图片描述
选择getMobileCodeInfo,即可进入查询页面了, 
这里写图片描述 
  调用后就会出现<string xmlns="http://WebXml.com.cn/">18312345678:广东 深圳 广东移动全球通卡</string>的结果。这就是调用ws的结果,接下来我们在程序中来调用这个ws。

3.1 get请求方式

  在java程序中如果要发送http请求,需要使用HttpClient工具。HttpClient 是 Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。 
  为什么要使用HttpClient工具呢?因为原生态的Socket基于传输层,现在我们要访问的WebService是基于HTTP的属于应用层,所以我们的Socket通信要借助HttpClient发HTTP请求,这样格式才能匹配。

<span style="color:#000000"><code class="language-java"><span style="color:#880000">/**
 *<span style="color:#4f4f4f"> @Description</span> get方式请求
 *<span style="color:#4f4f4f"> @param</span> number
 *<span style="color:#4f4f4f"> @throws</span> Exception
 */</span>
<span style="color:#000088">public</span> <span style="color:#000088">void</span> <span style="color:#009900">get</span>(String number) <span style="color:#000088">throws</span> Exception {
    <span style="color:#880000">//HttpClient:在java代码中模拟Http请求</span>
    <span style="color:#880000">// 创建浏览器对象</span>
    HttpClient client = <span style="color:#000088">new</span> HttpClient();
    <span style="color:#880000">// 填写数据,发送get或者post请求</span>
    GetMethod get = <span style="color:#000088">new</span> GetMethod(<span style="color:#009900">"http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx"</span>
            + <span style="color:#009900">"/getMobileCodeInfo?mobileCode="</span> + number + <span style="color:#009900">"&userID="</span>);
    <span style="color:#880000">// 指定传输的格式为get请求格式</span>
    get.setRequestHeader(<span style="color:#009900">"Content-Type"</span>, <span style="color:#009900">"text/xml; charset=utf-8"</span>);
    <span style="color:#880000">// 发送请求</span>
    <span style="color:#000088">int</span> code = client.executeMethod(get);
    System.out.println(<span style="color:#009900">"Http:状态码为:"</span> + code);

    String result = get.getResponseBodyAsString();
    System.out.println(<span style="color:#009900">"返回的结果为:"</span> + result);
}</code></span>

  从程序中可以看出,请求的主机是ws.webxml.com.cn,这些url在ws提供方的网站上都有,我们只需要写对即可请求ws了,在main方法中调用一下该方法即可在控制台获取结果。

3.2 post请求方式

请求的过程都一样,只是url和传输格式不同而已,修改一下相应的地方即可,

<span style="color:#000000"><code class="language-java"><span style="color:#880000">/**
 *<span style="color:#4f4f4f"> @Description</span> post方式请求
 *<span style="color:#4f4f4f"> @param</span> number
 *<span style="color:#4f4f4f"> @throws</span> Exception
 */</span>
<span style="color:#000088">public</span> <span style="color:#000088">void</span> <span style="color:#009900">post</span>(String number) <span style="color:#000088">throws</span> Exception {
    <span style="color:#880000">//HttpClient:在java代码中模拟Http请求</span>
    <span style="color:#880000">// 创建浏览器对象</span>
    HttpClient client = <span style="color:#000088">new</span> HttpClient();
    <span style="color:#880000">// 填写数据,发送get或者post请求</span>
    PostMethod post = <span style="color:#000088">new</span> PostMethod(<span style="color:#009900">"http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx/getMobileCodeInfo"</span>);

    <span style="color:#880000">// 指定传输的格式为默认post格式</span>
    post.setRequestHeader(<span style="color:#009900">"Content-Type"</span>, <span style="color:#009900">"application/x-www-form-urlencoded"</span>);     
    <span style="color:#880000">// 传输参数</span>
    post.setParameter(<span style="color:#009900">"mobileCode"</span>, number);
    post.setParameter(<span style="color:#009900">"userID"</span>, <span style="color:#009900">""</span>);

    <span style="color:#880000">// 发送请求</span>
    <span style="color:#000088">int</span> code = client.executeMethod(post);
    System.out.println(<span style="color:#009900">"Http:状态码为:"</span> + code);

    String result = post.getResponseBodyAsString();
    System.out.println(<span style="color:#009900">"返回的结果为:"</span> + result);
}</code></span>

3.3 SOAP方式请求

这也是在用的多的方式,它有两个版本soap1.1和soap1.2,jdk1.7及以上才可以使用soap1.2。

<span style="color:#000000"><code class="language-java"><span style="color:#880000">/**
 *<span style="color:#4f4f4f"> @Description</span> soap post方式请求,但是传输的数据为xml格式,有利于数据的维护
 *<span style="color:#4f4f4f"> @param</span> number
 *<span style="color:#4f4f4f"> @throws</span> Exception
 */</span>
<span style="color:#000088">public</span> <span style="color:#000088">void</span> <span style="color:#009900">soap</span>(String number) <span style="color:#000088">throws</span> Exception {
    <span style="color:#880000">//HttpClient:在java代码中模拟Http请求</span>
    <span style="color:#880000">// 创建浏览器对象</span>
    HttpClient client = <span style="color:#000088">new</span> HttpClient();
    <span style="color:#880000">// 填写数据,发送get或者post请求</span>
    PostMethod post = <span style="color:#000088">new</span> PostMethod(<span style="color:#009900">"http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx"</span>);

    <span style="color:#880000">// 指定传输的格式为xml格式</span>
    post.setRequestHeader(<span style="color:#009900">"Content-Type"</span>, <span style="color:#009900">"application/soap+xml;charset=utf-8"</span>);
    <span style="color:#880000">// 传输xml,加载soap.txt</span>
    post.setRequestBody(<span style="color:#000088">new</span> FileInputStream(<span style="color:#009900">"E:/github/client/src/soap.txt"</span>));  
    <span style="color:#880000">// 发送请求</span>
    <span style="color:#000088">int</span> code = client.executeMethod(post);
    System.out.println(<span style="color:#009900">"Http:状态码为:"</span> + code);

    String result = post.getResponseBodyAsString();
    <span style="color:#880000">// 如果采用的是soap,则返回的数据也是基于xml的soap格式</span>
    System.out.println(<span style="color:#009900">"返回的结果为:"</span> + result);
}
</code></span>

  由于soap方式需要向服务端发送xml,所以我们可以实现写好一个txt文档,里面是xml的数据,这个模板ws提供方会提供,我们需要写好即可:

<span style="color:#000000"><code class="language-xml"><span style="color:#006666"><?xml version="1.0" encoding="utf-8"?></span>
<span style="color:#006666"><<span style="color:#4f4f4f">soap12:Envelope</span> <span style="color:#4f4f4f">xmlns:xsi</span>=<span style="color:#009900">"http://www.w3.org/2001/XMLSchema-instance"</span> <span style="color:#4f4f4f">xmlns:xsd</span>=<span style="color:#009900">"http://www.w3.org/2001/XMLSchema"</span> <span style="color:#4f4f4f">xmlns:soap12</span>=<span style="color:#009900">"http://www.w3.org/2003/05/soap-envelope"</span>></span>
  <span style="color:#006666"><<span style="color:#4f4f4f">soap12:Body</span>></span>
    <span style="color:#006666"><<span style="color:#4f4f4f">getMobileCodeInfo</span> <span style="color:#4f4f4f">xmlns</span>=<span style="color:#009900">"http://WebXml.com.cn/"</span>></span>
      <span style="color:#006666"><<span style="color:#4f4f4f">mobileCode</span>></span>18312345678<span style="color:#006666"></<span style="color:#4f4f4f">mobileCode</span>></span>
      <span style="color:#006666"><<span style="color:#4f4f4f">userID</span>></span><span style="color:#006666"></<span style="color:#4f4f4f">userID</span>></span>
    <span style="color:#006666"></<span style="color:#4f4f4f">getMobileCodeInfo</span>></span>
  <span style="color:#006666"></<span style="color:#4f4f4f">soap12:Body</span>></span>
<span style="color:#006666"></<span style="color:#4f4f4f">soap12:Envelope</span>></span></code></span>

  上面这些方式推荐使用soap的方式,不过本质上还是http方式调用,只是调用的时候可以传输xml数据而已。而且HttpClient是Java的调用http协议的解决方案,但是不能保证其它语言也拥有类似的工具。所以ws推荐的方案是使用wsimport命令。这也是下面分析的重点。

3.4 使用wsimport

  每个ws都会有一个WSDL,WSDL即WebService Description Language – Web服务描述语言。它是通过XML形式说明服务在什么地方-地址。通过XML形式说明服务提供什么样的方法 – 如何调用。我们可以通过这个WSDL来获取和这个ws有关的信息,包括class和java代码。关于这个WSDL后面我再具体分析,这一节先来看一下如何使用。 
  wsimport是一个命令,jdk1.6及以上才可以使用,ws针对不同的语言都会有个wsimport命令,我们可以在自己安装的jdk的bin目录下找到这个wsimport.exe,正因为有了这个,所以我们可以在命令行中使用wsimport命令。怎么使用呢? 
  每个ws都会有一个WSDL,就拿上面的归属地查询服务来说,上面第二张图上面有个服务说明,点开就可以看到WSDL,当然也可以直接访问浏览器上的url来访问这个WSDL,即xml文档。如下: 
这里写图片描述
  目前只需要复制一下那个url即可,然后打开命令提示符窗口,随便进入一个目录下(该目录要保存等会生成的和ws相关的文件,自己事先建一个即可),运行 
wsimport http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL 
  就会生成相应的javabean,当然了,是.class文件,但是我们不想要class文件,我们想要java文件,所以可以使用如下命令: 
wsimport -s http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL 
  这样不仅生成了class文件,还生成了java文件,如果我们想要在固定的包下生成这些文件,等会方便直接拷贝到项目里,可以使用下面的命令: 
wsimport -s . -p ws.client.c http://ws.webxml.com.cn/WebServices/MobileCodeWS.asmx?WSDL 
  这样就会在目录ws/client/c/下生成所需要的class和java代码,然后我们删掉class文件,直接拷贝ws目录到工程中即可,如下(_Main是我自己写的,用来调用使用的): 
这里写图片描述 
  这样就有了号码归属地查询这个ws服务相关的API了,这是通过官方的WSDL来生成的,然后我们如何在自己的项目中使用呢?我新写一个_Main.java文件,直接使用这些API即可,如下:

<span style="color:#000000"><code class="language-java"><span style="color:#000088">public</span> <span style="color:#000088">class</span> <span style="color:#4f4f4f">_Main</span> {
    <span style="color:#000088">public</span> <span style="color:#000088">static</span> <span style="color:#000088">void</span> <span style="color:#009900">main</span>(String[] args) {

        <span style="color:#880000">// 获取一个ws服务</span>
        MobileCodeWS ws = <span style="color:#000088">new</span> MobileCodeWS();
        <span style="color:#880000">// 获取具体的服务类型:get post soap1.1 soap1.2</span>
        MobileCodeWSSoap wsSoap = ws.getMobileCodeWSSoap();
        String address = wsSoap.getMobileCodeInfo(<span style="color:#009900">"18312345678"</span>, <span style="color:#000088">null</span>);
        System.out.println(<span style="color:#009900">"手机归属地信息为:"</span> + address);
    }
}</code></span>

  这样就很方便了,现在已经完全没有了上面那种连接啊,设置地址啊等等,直接封装好了,我直接调用这些API即可调用远程的webservice。这也是官方推荐的一种方法,当然我们也可以将生成的class文件打包成jar放到工程中。运行一下这个main方法后,也直接返回归属地,没有那些标签的东西了,这才是开发中所需要的东西。 
  到这里基本已经会调用webservice了,最后再简单总结一下,ws中这个WSDL很重要,这里面用xml描述了该ws的信息,所以我们可以通过解析WSDL来获取该ws相关的API,然后在自己的项目中调用这些API即可调用该ws。 
   

猜你喜欢

转载自blog.csdn.net/u014252478/article/details/82589640