Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3092
  • 博文数量: 1
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 12
  • 用 户 组: 普通用户
  • 注册时间: 2014-01-06 02:42
文章分类
文章存档

2014年(1)

我的朋友

分类: C#/.net

2014-01-06 02:51:05

1. 乱谈SOA


        SOA的核心思想是整合,把旧平台的应用通过重构、提炼,整合成服务,通过众所周知的协议暴露服务接口,进而实现更大范围的互联互通。服务成为企业的核心价值,借助于安全的访问机制,消费者可以通过特定的方式使用生产者的企业资源,从而任何身处互联网的用户都在消费服务的同时,也可能为他人提供力所能及的服务,服务的集合成为云,云是普世价值。在可预见的将来,孤岛式的应用软件将不复存在,取而代之是一朵朵特定领域的信息云。从软件设计的角度看,云计算的基础是从系统到部件各级别均规定了标准的整合模式,SOA因此应运而生。在C语言的时代,软件被称为模块及其集合,模块之间的整合机制不外两种:源码级的函数与二级制级的动态库,二者都需要头文件加以规约;到了C++的时代,类的概念被引入,但本质上软件之间的整合机制并无变化,具有革新意义的是人们开始意识到软件内部需要更考究更成熟的创建手法,即模式与重构,分别从设计到实现把软件开发从个体化的艺术创作带向团队级的批量制造新纪元。软件越来越大,以至于PC时代不得不过早的离我们渐行渐远。Internet的诞生,对软件生产提出了新课题。原本是各大软件厂商自发考虑的平台化软件互联方法,逐渐演化为超越软件生产线品牌的不得不共同缔造的全贯通互联计算新思路。COM+、EJB、CORBA……打着不同标签的分布式软件构建标准,在彼此的融合中催生了SOA。经过十多年的蜕变,SOA从最初的抽象概念到今天得以落地实施,成为云计算的软件标准架构。微软的SOA也即WCF,同样关注整合与互联,其自身C++~COM~DCOM~COM+~.NET Remoting+Enterprise Service+ASMX+WSE~Indigo~WCF的发展脉络,对从VC6.0开始从事软件开发的人,并不陌生。理解WCF,是传统软件开发人员迈向云时代的一条捷径。


        WCF作为SOA的微软方言,它与其他平台如何整合?最普遍的,是通过JAVA访问WCF服务。简单搜索发现,一般采用Apache的Axis框架,通过WSDL文件生成JAVA类,进而直接调用WCF服务提供的方法。WSDL是web服务描述语言,是一种可扩展标记语言(XML),是一种接口定义语言。WSDL用于暴露服务接口,与服务本身使用何种语言开发无关,不同语言产出的服务都可以通过WSDL互相认识。因此,只要有了WSDL文件,JAVA就能访问WCF服务。Apache官方把Axis描述为W3C SOAP协议的一种实现。SOAP是比WSDL更广泛的标准,它规定了一种消息传输方式,通过对HTTP加以修饰,使得远程过程调用得以实施,采用XML构建的文档得以传递。SOAP和WSDL分别关注服务整合过程中的不同方面,它们共同支撑起了跨越软件媒介的庞大的SOA架构。由于HTTP协议的大获成功,而且构建在HTTP基础上的WEB也空前流行,软件服务要遵循怎样的通信协议,要采取怎样的互联方式,也就毫无疑义的落在HTTP之上。客户端通过URI标识服务器,使用底层TCP / IP网络连接到它,发出一个HTTP请求消息,并接收在同一个TCP连接下的HTTP响应消息。——这就是HTTP的基于TCP连接的请求-响应模式。作为传输XML数据(区别于HTML,可以是WSDL,也可以是一般XML)的HTTP,SOAP通过请求头中的“SOAPAction: uri/service/operation”和“Content-Type: application/soap+xml ”字段加以标识。HTTP服务器在收到这样的请求后,就知道对方想要调用一个SOAP服务,而SOAP服务在微软平台,也就是WCF。


2.问题的引出


        既然JAVA可以通过SOAP框架Axis调用WCF服务,那么其他语言所编写的软件呢?比如非.NET框架的C++应用程序、采用C语言标准库实现的嵌入式终端等。同样的,也都可以采用SOAP实现WCF调用。这意味着非WCF客户必须按照SOAP协议规定的格式编写消息,然后通过TCP Socket发送给WCF服务器,后者在检查消息语法无误后,提取其中的RPC调用,将参数传递给相应的服务方法,并同样通过SOAP消息向客户发送返回值。本文将以一个简单的例子描述WCF与底层Socket之间的双向通信模式。



3.解决方案描述


        首先,在Visual Studio中新建两个C#类库项目和两个C#控制台应用项目,以及一个C++控制台应用项目。两个类库项目分别用于构建服务契约和服务实现,两个C#控制台应用项目分别用于构建服务宿主和服务客户,C++控制台应用项目用于构建底层Socket程序。


        服务契约如下:

namespace MathServiceContract
{
    [ServiceContract]
    public interface IMathService
    {
        [OperationContract]
        double Sum(double x, double y);

        [OperationContract]
        void Send2TcpSocket(string msg);
    }
}

        服务暴露了两个接口方法:用于返回两数之和的Sum,和用于向TCP客户端发送消息的Send2TcpSocket。Sum主要被底层Socket程序调用,Send2TcpSocket则被WCF服务客户端调用。系统结构如下:



       
       Send2TcpSocket的实现如下:



public void Send2TcpSocket(string msg)
{
    string host = "localhost";
    IPHostEntry iphe = Dns.GetHostEntry(host);
    IPAddress[] addList = iphe.AddressList;
    EndPoint ep = new IPEndPoint(addList[0], 9997);
    Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    socket.Connect(ep);
    if (socket.Connected)
        Console.WriteLine("Connected to socket at " + ep.ToString());
    else
        Console.WriteLine("Error: Unable to connect to " + ep.ToString());

    string sendAsString = msg;
    byte[] sendAsBytes = Encoding.UTF8.GetBytes(sendAsString);
    Console.WriteLine("Sending message to TCP Socket.");
    int numBytesSent = socket.Send(sendAsBytes, sendAsBytes.Length, SocketFlags.None);
}
        简单的通过Socket建立TCP连接向远端发送一个字符串。这个方法实现了WCF客户端(特别有意义的是对于Web页面)通过调用WCF服务向底层TCP Socket发送消息。


        解决了从WCF到Socket的消息传递,问题搞定了一半。接下来需要通过手写SOAP消息实现从Socket到WCF的调用。这里最困难之处在于保证SOAP消息的正确性,在WCF范畴内,不需要如此赤裸裸地面对SOAP,因为.NET框架帮我们做掉了。事实上,即使有经验的开发人员也难以默写一段完整的SOAP消息。这时候有必要借助于调试助手:TCPTrace()。该工具是一个路由器,可以如实记录从A端口到B端口传输的TCP消息。因此,在WCF服务宿主的App.config文件中如此设置:



  
    
      
        
          
        
      
    
    
      
        
                  listenUri=""
                  listenUriMode="Explicit" />
      
    
  

       
       
没有使用netTcpBinding,是因为:“The NetTcpBinding …… is an appropriate Windows Communication Foundation (WCF) system-provided choice for communicating over an Intranet.……, but it is intended only for WCF-to-WCF communication.”

        采用security mode为None的ws2007HttpBinding作为绑定方式,并在不同于服务端口9998的另一端口9990监听,设置监听模式为Explicit。并在WCF客户端的App.config中如下设置:



  
    
      
        
          
        
      
    
    
      
    
  

        启动服务,运行客户端、TCP Trace,设置监听端口为9998,目标端口为9990,即可拦截从9998端口向9990端口发送的SOAP消息。消息格式如下:


POST /MathService HTTP/1.1
Content-Type: application/soap+xml; charset=utf-8
VsDebuggerCausalityData: uIDPozAxkCh8sGZAoJrDfraeXxkAAAAANox4e5shDkmA8Nvs09vtEtFoH79KlihAgc3Vl1ohdnkACQAA
Host: 127.0.0.1:9998
Content-Length: 527
Expect: 100-continue
Accept-Encoding: gzip, deflate
Connection: Keep-Alive


  
    
    urn:uuid:89e91c90-2480-41fe-8971-903e969955e8
    
      /anonymous
    
    
  
  
    
      1.1
      2.2
    
  

        可见纯手写难度之大。好在有了这个模板,在程序中照猫画虎也不难:




#define BUF_SIZE 4096

//......

// 设置发送字符串
char soapBody1[] = "urn:uuid:15C02280-5779-4C60-98C8-BE2D3C76B843/anonymous";
char soapBody2[] = "";
char soapBody3[] = "";

char soapBody[BUF_SIZE];

double input1 = 1.1, input2 = 2.2;
sprintf(soapBody, "%s%.1f%s%.1f%s", soapBody1, input1, soapBody2, input2, soapBody3);

char soapHead1[] = "POST /MathService HTTP/1.1\r\nContent-Type: application/soap+xml; charset=utf-8\r\nHost: 127.0.0.1:9998\r\nContent-Length: ";
char soapHead2[] = "\r\nExpect: 100-continue\r\nConnection: Close\r\n\r\n";

char soapHead[BUF_SIZE];
sprintf(soapHead, "%s%d%s", soapHead1, strlen(soapBody), soapHead2);
	
char soapMessage[BUF_SIZE];
sprintf(soapMessage, "%s%s", soapHead, soapBody);

        然后向TCP Socket发送上述soapMessage即可。发送成功后,WCF服务器会返回如下响应消息:


HTTP/1.1 200 OK
Content-Length: 413
Content-Type: application/soap+xml; charset=utf-8
Server: Microsoft-HTTPAPI/1.0
Date: Sun, 05 Jan 2014 14:06:12 GMT
Connection: close


  
    IMathService/SumResponse
    urn:uuid:15C02280-5779-4C60-98C8-BE2D3C76B843
  
  
    
      3.3000000000000003
    
  

        至此,WCF和底层Socket之间的双向通信就全部完成了。


4.参考文献


         [1] 使用套接字进行 WCF 服务测试 
        [2] 。
        [3] 。

阅读(1031) | 评论(1) | 转发(0) |
0

上一篇:没有了

下一篇:没有了

给主人留下些什么吧!~~

4大爷2014-01-07 09:43:50