[轉貼]C++、C#寫的WebService相互調用

如下宏文(原文在 http://blog.sina.com.cn/s/blog_4e7d38260100ade4.html),是轉貼並進行了修飾編輯:php

首先感謝永和兄提供C++的WebService服務器端及客戶端,而且陪我一塊兒熬夜;而後是火石和我作接口的兄弟,雖然都不知道你叫什麼,若是沒有你的合做,東西也沒那麼快完成。html

1、因爲公司運營火石的《西遊Q記》,火石採用的是C++做爲開發語言,Unix平臺,而咱們一直使用Windows操做平臺,.NET快速開發。咱們之 間須要數據的通信,因此須要利用WebService實現跨平臺的數據通信。儘管WebService是跨平臺的,可是實現起來卻並不容易。

2、用C#實現WebService是至關簡單的事情,咱們只要建立一個Web服務程序,在方法名上面加上[WebMethod],部署到IIS上,就能 像訪問Web站點同樣訪問WebService。用C#編寫客戶端時,只須要將WebService添加到引用,就能像調用本地方法同樣去調用 WebService。像這樣的例子也比比皆是,在這就很少講。

3、用C++實現WebService,通常會用到gsoap,具體方法見: http://www.cppblog.com/qiujian5628/archive/2008/06/19/54019.html

4、當作完了這些以後,並不表明WebService就能相互通信了,如今我簡單列舉一下問題:

一、C#提供的WebService的URL通常形如:http://localhost/WebService.asmx,可是,C++能提供的只能 是:http://localhost/。C++作客戶端的時候調用沒有問題,可是當C#作客戶端的時候,引用C++提供的URL時,會提示沒用執行方法 (HTTP GET method not implemented)。作C#開發的大部分會認爲C++方提供的不是WebService,或者說提供的WebService根本就不全,都不 帶.asmx文件。作C++開發的會認爲他傳輸的數據符合soap協議,靠http傳輸數據,他就是WebService。

二、當咱們解決了第一步後,緊接着會發現另一個問題。當咱們須要傳輸自定義數據類型時(在C++中稱結構體,在C#中稱實體),從C++返回的信息中, C#沒法構建出實體類

三、當傳輸的信息中帶有中文字符時,亂碼滿天飛。

5、爲了解決這些問題,咱們先簡單瞭解一下WebService。
    Web Service互操做協議棧:
    <A>、服務發現 (UDDI)
    <B>、服務描述(WSDL)
    <C>、服務調用(SOAP)
    <D>、消息編碼 (XML)
    <E>、傳輸網絡層(HTTP, TCP/IP)

其中WSDL描述WebService都有什麼方法、方法有什麼參數,什麼返回值等。SOAP(簡單對象訪問協議(Simple Object Access Protocol)是一種輕量的、簡單的、基於XML的協議。傳輸的數據就須要遵循這個協議。我比較簡單得認爲傳輸的數據須要遵循這種格式。

借用微軟的這個圖描述下WebService的調用過程:

6、開始解決問題。做爲.NET開發人員,咱們根本就接觸不到底層的東西,全被封裝了。
C++作的確實是WebService,只是他們須要給提供一個描述文檔,即.WSDL文件。使用.NET提供的wsdl.exe工具,使用命 令:wsdl /o: c:\webservice.cs c:\webservice.wsdl。經過webservice.wsdl文檔,生成代理類,將代理類寫入webservice.cs文件中。咱們拷貝 這個cs文件到項目中,將URL指向http://localhost/,就能像以往那樣使用WebService了。
當出現沒法傳遞複雜類型數據時,是由於使用gsoap生成的wsdl文件與.Net中生成的wsdl文件不同。具體代碼以下:

  <!-- operation response element -->
  < element name="result">
   <complexType>
    <sequence>
     <element name="a" type="xsd:int" minOccurs = "1" maxOccurs="1"/>
     <element name="b" type="xsd:int" minOccurs = "1" maxOccurs="1"/>
    </sequence>
   </complexType>
  </element>

以上爲gsoap生成的。返回實體result,實體有兩個屬性:a,b。

  <s:element name="TestResponse">
    <s:complexType>
     <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="TestResult" type="tns:result" />
     </s:sequence>
    </s:complexType>
   </s:element>
   <s:complexType name="result">
    <s:sequence>
     <s:element minOccurs="1" maxOccurs="1" name="a" type="s:int" />
     <s:element minOccurs="1" maxOccurs="1" name="b" type="s:int" />
    </s:sequence>
   </s:complexType>

以上是.NET生成的。

在下面的文件中,多出

<s:element name="TestResponse">
    <s:complexType>
     <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="TestResult" type="tns:result" />
     </s:sequence>
    </s:complexType>
   </s:element>
這個即是.NET中用來構造實體的。當咱們出現狀況4.2時,gsoap中儘可能使用.NET生成的wsdl文檔,生成.h文件,以免C++中的結構沒法在C#中轉換成實體。

第三個問題,咱們是經過將中文轉換成16進制後傳輸過來,而後再轉換成中文。下面提供C#轉換的代碼:

/// <summary>
/// 從16進制轉換成漢字
/// </summary>
/// <param name="hex"></param>
/// <returns></returns>
public static string GetChsFromHex(string hex)
{
    if (hex == null)
        throw new ArgumentNullException("hex");
    if (hex.Length % 2 != 0)
    {
        hex += "20";//空格
        //throw new ArgumentException("hex is not a valid number!", "hex");
    }
    // 須要將 hex 轉換成 byte 數組。
    byte[] bytes = new byte[hex.Length / 2];

    for (int i = 0; i < bytes.Length; i++)
    {
        try
        {
            // 每兩個字符是一個 byte。
            bytes[i] = byte.Parse(hex.Substring(i * 2, 2),
                System.Globalization.NumberStyles.HexNumber);
        }
        catch
        {
            // Rethrow an exception with custom message.
            throw new ArgumentException("hex is not a valid hex number!", "hex");
        }
    }

    // 得到 GB2312,Chinese Simplified。
    System.Text.Encoding chs = System.Text.Encoding.GetEncoding("gb2312");

    return chs.GetString(bytes);
}

/// <summary>
/// 從漢字轉換到16進制
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public static string GetHexFromChs(string s)
{
    if ((s.Length % 2) != 0)
    {
        s += " ";//空格
        //throw new ArgumentException("s is not valid chinese string!");
    }
    System.Text.Encoding chs = System.Text.Encoding.GetEncoding("gb2312");
    byte[] bytes = chs.GetBytes(s);
    string str = "";
    for (int i = 0; i < bytes.Length; i++)
    {
        str += string.Format("{0:X}", bytes[i]);
    }
    return str;
}

注:以上來轉換代碼源於網絡,C++中轉換的代碼也能夠在網上找到。

三大難題到此結束,其實在整個過程當中還有個最大的難題,那就是人與人的交流。由於一方使用C++,一方使用C#,語言不一樣,各自想問題的方式也不同,所 以須要相互理解,相互站在對方的角度想問題。多交流、多溝通才是解決問題之道。請不要抱怨C#弱智,也請不要怪C++繁瑣,語言既然存在則有他的價值。

======================= 2012-07-19補 =================

typedef char* String;

1.函數聲明如:

    class Result
    {
    public:
        Int ErrorCode;
        String Description;
    };

    int ns1__Register(String Account, Result &RegisterResult);//1.建立賬號ns1__ webservice的名字空間

2.數組聲明例如:

    class Group
    {
    public:
        Int ID;
        Int Version;
        String Name;
        Int State;
        String TestIP;
    };

    class LoginServer
    {
    public:
        String IP;
        Int Port;
    };

    class ArrayOffGroup
    {
    public:
        Group *__ptr;
        int __size;
    };

    class ArrayOffLoginServer
    {
        public:
        LoginServer *__ptr;
        int __size;
    };

    class ResultGetGameGroupList
    {
    public:
        Int ErrorCode;
        String Description;
        ArrayOffGroup Groups;
        ArrayOffLoginServer LoginServers;
    };
    int ns1__GetGameGroupList(void *_, ResultGetGameGroupList &GetGameGroupListResult);//2.獲取當前啓動的服務器組及服務器壓力

3.用gSoap工具從*.h生成C++代碼及*.wsdl文件
    soapcpp2.exe *.h生成WebService的C++代碼
    注:這裏生成的*.wsdl沒法用VS的wsdl工具直接生成.net的代理類,須要手動打開*.wsdl把裏面的String改成.net認識的 string便可.沒法直接把char* typedef 成string 這樣的話會使生成的c++代碼沒法編譯經過

4.用gSoap工具從*.wsdl生成C++代碼
    (1).wsdl2h.exe *.wsdl生成 *.h 文件
    (2).soapcpp2.exe *.h生成Webservice的C++代碼
 
5.用Vs的wsdl工具生成.net代理類命令
    wsdl /out:C:/myProxyClass.cs  *.wsdl

附錄1.
php調用gSoap的WebService的代碼示例:
<?php
    echo "start";
    header ( "Content-Type: text/html; charset=utf-8" );
    $client = new SoapClient ();
    try
    {  
        $params = array('Account' => 'testtest', 'Password' => '12341234', 'IP' => '127.0.0.1', 'IsAdult' => 1, 'Presentee' => 1);
        $result = $client->__soapCall("Register", array( 'parameters' => $params), array('location' => 'http://url:81', 'uri' => 'ns1'));
        echo ($result);
    }
    catch(SoapFault $fault)
    {
        echo ($fault->faultstring);
    }
?> c++

相關文章
相關標籤/搜索