电信provisioning系统中,常常需要与远程服务器实时交换一些数据,以完成用户的请求。由于简单对象访问协议(Simple Object Access Protocol, SOAP)的流行,许多涉及到第三方的应用,我们一般都比较乐意使用SOAP来开发。不过,由于可能涉及到公司的机密,本系列教程的开发实例尽量采用在网上已经公开的Web Service资源。
我开发SOAP应用程序已经有一定的经验,在C/C++环境下一般使用gSOAP,而在Java环境下一般采用axis2。比较两者的话,除了开发语言之外,还是有不少差别,处理中文字符就是其中之一。网上分别搜索一下“axis2 乱码”和“gSOAP 乱码”,匹配的结果是相差很远的。Axis2好像比较智能,能够识别服务端的字符编码,这方面的问题也少,而最新版本的gSOAP,很可能还是需要程序员做多很多功夫。
在第一节客户端的教程中,输出的中文股票名称,其实就是乱码,不过为了主次之分,当时做了特别处理,忽略过去。
网上解决gSOAP乱码的主流方案是,初始化soap对象之后对其设置SOAP_C_UTFSTRING参数,例如:
struct soap soap;
soap_init(&soap);
soap_set_mode(&soap, SOAP_C_UTFSTRING);
但是,单纯这样修改,在某些特定设置的机器上可能有效,反正我试过,仍然是乱码,如下图。怎么办呢?
Linux下有一个字符编码转换的工具iconv,同时也提供了一套可编程的接口。利用它,就可以测试出来自于服务端中文字符编码的类型,从而进一步实现在程序中自动转换编码。
Iconv常用用法是:iconv -t=to_charset -f=from_charset filename
因此,把需要转换编码的内容保存为一个文件,然后执行iconv试出需要转换的编码类型。from_charset几乎百分百肯定就是utf8,那么to_charset来来去去就那么几个,一个个试也很快试出来了。最终得出的结果是gbk编码,从而修改客户端程序以解决乱码问题。
- #include
-
- #include "soapH.h"
- #include "ChinaStockWebServiceSoap12.nsmap"
-
- #define OUTPUT_LEN 32
-
- int conv_charset(const char *dest, const char *src, char *input, size_t ilen, char *output, size_t olen) {
- iconv_t conv = iconv_open(dest, src);
- if ( conv == (iconv_t) -1 )
- return -1;
- memset(output, 0, olen);
- if ( iconv(conv, &input, &ilen, &output, &olen) )
- return -1;
- iconv_close(conv);
- return 0;
- }
-
- int main(int argc, char **argv) {
- if ( argc != 2 && argc != 3 ) {
- printf("Usage: %s stock_code [end_point]\n", argv[0]);
- exit(-1);
- }
-
- struct soap soap;
- soap_init(&soap);
- soap_set_mode(&soap, SOAP_C_UTFSTRING);
-
- struct _ns1__getStockInfoByCode request;
- struct _ns1__getStockInfoByCodeResponse response;
-
- request.theStockCode = argv[1];
- char *endpoint = NULL;
- if ( argc == 3 )
- endpoint = argv[2];
- if ( soap_call___ns3__getStockInfoByCode(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {
- int element_counter = response.getStockInfoByCodeResult->__sizestring;
- int i = 0;
- for ( i = 0; i < element_counter; i++ ) {
- switch ( i ) {
- case 0 : printf("Stock code : "); break;
- case 1 : printf("Stock name : "); break;
- case 2 : printf("Timestamp : "); break;
- case 3 : printf("Latest price : "); break;
- case 4 : printf("Closing price T-1 : "); break;
- case 5 : printf("Opening price : "); break;
- case 6 : printf("Ups and downs : "); break;
- case 7 : printf("Mininum price : "); break;
- case 8 : printf("Maxinum price : "); break;
- case 9 : printf("Amount of up/down : "); break;
- case 10 : printf("Trading volume : "); break;
- case 11 : printf("Trading amount : "); break;
- case 12 : printf("Buy price : "); break;
- case 13 : printf("Sell price : "); break;
- case 14 : printf("Agency trans : "); break;
- case 15 : printf("Buy 1 : "); break;
- case 16 : printf("Buy 2 : "); break;
- case 17 : printf("Buy 3 : "); break;
- case 18 : printf("Buy 4 : "); break;
- case 19 : printf("Buy 5 : "); break;
- case 20 : printf("Sell 1 : "); break;
- case 21 : printf("Sell 2 : "); break;
- case 22 : printf("Sell 3 : "); break;
- case 23 : printf("Sell 4 : "); break;
- case 24 : printf("Sell 5 : "); break;
- default : break;
- }
-
- size_t ilen = strlen(response.getStockInfoByCodeResult->string[i]);
- char output[OUTPUT_LEN];
- if ( conv_charset("GBK", "UTF-8", response.getStockInfoByCodeResult->string[i], ilen, output, OUTPUT_LEN) )
- printf("%s\n", response.getStockInfoByCodeResult->string[i]);
- else
- printf("%s\n", output);
- }
- }
- else {
- soap_print_fault(&soap, stderr);
- }
-
- soap_destroy(&soap);
- soap_end(&soap);
- soap_done(&soap);
- return 0;
- }
测试成功,如下图:
我们用一个天气预报客户端的例子,简述一下gSOAP输入的中文文本乱码的问题。
Webxml.com.cn提供的天气预报web服务,endpoint地址是:,大家可以点击进去,查看一下该服务的所有对外提供的接口。其中,利用getWeatherbyCityName接口,可以按给定的城市名字查询该城市的天气预报,如果输入的城市名字不能识别,将统一返回北京的天气预报。
根据前三节的内容,我们可以很快地准备好其客户端存根程序:
1. mkdir –p weather
2. cd weather
3. ../wsdl2h -c -o weather.h
4. ../../bin/linux386/soapcpp2 –C –L –x weather.h
由于程序并不复杂,直接给出其源代码:
- #include
-
- #include "soapH.h"
- #include "WeatherWebServiceSoap12.nsmap"
-
- #define OUTPUT_LEN 2048
-
- int conv_charset(const char *dest, const char *src, char *input, size_t ilen, char *output, size_t olen) {
- iconv_t conv = iconv_open(dest, src);
- if ( conv == (iconv_t) -1 )
- return -1;
- memset(output, 0, olen);
- if ( iconv(conv, &input, &ilen, &output, &olen) )
- return -1;
- iconv_close(conv);
- return 0;
- }
-
- int main(int argc, char **argv) {
- if ( argc != 2 && argc != 3 ) {
- printf("Usage: %s city_name [end_point]\n", argv[0]);
- exit(-1);
- }
-
- struct soap soap;
- soap_init(&soap);
- soap_set_mode(&soap, SOAP_C_UTFSTRING);
-
- struct _ns1__getWeatherbyCityName request;
- struct _ns1__getWeatherbyCityNameResponse response;
-
- size_t ilen = strlen(argv[1]);
- char output[OUTPUT_LEN];
- if ( conv_charset("UTF-8", "GBK", argv[1], ilen, output, OUTPUT_LEN) )
- request.theCityName = argv[1];
- else
- request.theCityName = output;
-
- char *endpoint = NULL;
- if ( argc == 3 )
- endpoint = argv[2];
- if ( soap_call___ns3__getWeatherbyCityName(&soap, endpoint, NULL, &request, &response) == SOAP_OK ) {
- int element_counter = response.getWeatherbyCityNameResult->__sizestring;
- int i = 0;
- for ( i = 0; i < element_counter; i++ ) {
- switch ( i ) {
- case 0 : printf("Province : "); break;
- case 1 : printf("City : "); break;
- case 2 : printf("City code : "); break;
- case 3 : printf("City pic. name : "); break;
- case 4 : printf("Timestamp : "); break;
- case 5 : printf("Temp. of today : "); break;
- case 6 : printf("Summary : "); break;
- case 7 : printf("Wind : "); break;
- case 8 : printf("Icon 1 : "); break;
- case 9 : printf("Icon 2 : "); break;
- case 10 : printf("Description : "); break;
- case 11 : printf("Reserved : "); break;
- case 12 : printf("Temp. of tomorrow : "); break;
- case 13 : printf("Summary : "); break;
- case 14 : printf("Wind : "); break;
- case 15 : printf("Icon 1 : "); break;
- case 16 : printf("Icon 2 : "); break;
- case 17 : printf("Temp. of af. tmr. : "); break;
- case 18 : printf("Summary : "); break;
- case 19 : printf("Wind : "); break;
- case 20 : printf("Icon 1 : "); break;
- case 21 : printf("Icon 2 : "); break;
- case 22 : printf("Introduction : "); break;
- default : break;
- }
- ilen = strlen(response.getWeatherbyCityNameResult->string[i]);
- if ( conv_charset("GBK", "UTF-8", response.getWeatherbyCityNameResult->string[i], ilen, output, OUTPUT_LEN) )
- printf("%s\n", response.getWeatherbyCityNameResult->string[i]);
- else
- printf("%s\n", output);
- }
- }
- else {
- soap_print_fault(&soap, stderr);
- }
-
- soap_destroy(&soap);
- soap_end(&soap);
- soap_done(&soap);
- return 0;
- }
编译命令是:gcc -O2 -o weather weather.c soapC.c soapClient.c ../../stdsoap2.c -I../.. -L../.. –lgsoap
基本上与上一节的股票信息客户端差不多,唯一不同的是,作为输入参数的城市名字,首先需要iconv转换编码,从GBK转到UTF-8,才可以提交给服务端。各位可以试一下,不作转换的话,无论输入什么,服务端只会返回北京的天气预报,因为传入的参数在服务端产生了乱码。
以下为正常的执行结果,输入广州,可以得到广州的天气预报:
阅读(2294) | 评论(0) | 转发(1) |