C语言实现soap 客户端的提交和接收功能

来源:互联网 发布:淘宝美工作品 编辑:程序博客网 时间:2024/06/01 18:09

对于一个C程序员来说,写webservice是件比较头痛的事。好象MS下有相应的Toolkit,在VC2003开始,编译环境已经集成了相关功能,

点"Project->Add Web Reference",显示一个对话框,把一.个wsdl的地址写上,会自动分析生成相关的C++的 class,然后就可以用了。

开源软件,有个很不错的产品叫gSoap,使用起来应该更简单,google上能找出很多文章来介绍。这个产品是多平台的,最简单的使用方法就是下载其二进制文件,一共有两个,wsdl2h 和soapcpp2,看名字就知道了, 前者把wsdl转成.h,后者再根据这个.h生成相关的class 或函数。用的时候,把生成的几个.h,.c放在自己的工程目录下,再到gSoap源码中找出stdsoap2.h和stdsoap2.cpp,都放进工程,不用引入任何lib或dll,直接使用就可以了。stdsoap2.cpp这里面已经实现了socket的通信功能。

MS自己的soap功能已经淘汰一些不太安全的方式,所以有些webservice解析出错,比如最近用到的一个关于rpc的,MS不支持。gSoap兼容性相对好一些, 可以用。

而且,gSoap最大的好处,就是可以跟踪调试源码,比如提交时的HTTP字符串内容,接收到的HTTP字符串,直接可以显示出来,省的再用抓包工具了。在调试方面来讲,gSoap远远超过了MS,想想对com进行调试,没几年的经验是弄不好的。

 

gSoap在Linux下工作也很不错,使用方法和Windwos下完全一样,先把wsdl解析出来,然后直接使用.h和.c。

 

最近调试一个webservice,检查的非常苛刻,比如HTTP里的Content-Length,一般写为0就可以了,然后会检查/r/n/r/n,表示结束。但这个服务器端非要知道Content-Length的数值,然后再检查结束符,如果这里写0,直接返回错误。调试了一下gSoap,发现如果不特意指定的话,Content-Length总是置为0。不过这个问题相对也很好解决,只是在调试代码中,很快就了解了soap底层的一些实现过程,然后自己用socket仿照也实现了一个简单的。

代码演示如下:

 

//*****************

//file: main.c

//*****************

 

#include <winsock.h>
#pragma comment(lib, "ws2_32.lib")

 

int m_hSocket;
int Connect( const char* szHostAddr, int nPort )  
{  
    SOCKADDR_IN sockAddr;   
    memset(&sockAddr,0,sizeof(sockAddr));  
      
    sockAddr.sin_family = AF_INET;  
    sockAddr.sin_addr.s_addr = inet_addr(szHostAddr);  
      
    if (INADDR_NONE == sockAddr.sin_addr.s_addr)  
    {  
        LPHOSTENT lphost;  
        lphost = gethostbyname(szHostAddr);  
        if (lphost != NULL)  
            sockAddr.sin_addr.s_addr = ((LPIN_ADDR)lphost->h_addr)->s_addr;  
        else  
        {  
            WSASetLastError(WSAEINVAL);  
            return FALSE;  
        }  
    }  
      
    sockAddr.sin_port = htons((u_short)nPort);
 
      
    if (SOCKET_ERROR == connect(m_hSocket, (LPSOCKADDR)&sockAddr, sizeof(sockAddr)))  
    {  
        //int nError = WSAGetLastError();  
        return -1;  
    }  
    return 0;  
}  

 

//length应该足够大,timeout单位是秒
int TcpRecvTimeout(int fd, char* buffer, int length, int timeout)
{
 struct timeval TimeOut;
 int recv_lenth = 0;
 //int left_length = length;
 int fd_max=fd+1;
 int sel_ret=0, recv_ret=0;
 fd_set fdset ;

 TimeOut.tv_sec = timeout;
 TimeOut.tv_usec = 0;

 FD_ZERO(&fdset);
 FD_SET(fd, &fdset);
 sel_ret = select(fd_max, &fdset, NULL, NULL, &TimeOut);

 if (sel_ret > 0)
 {
  while (1)
  {
   //while (sel_ret==-1 && errno == EINTR);
   recv_ret = recv(fd, buffer, length, 0/*MSG_NOSIGNAL*/);
   if (recv_ret > 0)
   {
    recv_lenth += recv_ret;
    //left_length -= recv_ret;
    buffer += recv_ret;
   }
   else if (recv_ret == 0)//socket close
   {
    return recv_lenth;
   }
   else if (recv_ret < 0)//SOCKET_ERROR
   {
    return recv_lenth;
   }
  }
 }
 else if (sel_ret < 0)
 {
  return -1;
 }
 else//sel_ret == 0
 {
  //超时
  return 0;
 }
 return recv_lenth;
}

 

int TcpSend(int fd, char* buffer, int length)
{
 int left_length = length;
 int send_ret = 0;

 while (left_length>0)
 {
     send_ret = send(fd, buffer, left_length, 0);
    if (send_ret < 0)
  {
   return -1;
  }

  if (send_ret < left_length)
  {
   left_length -= send_ret;
   buffer += send_ret;
  }
  else
  {
   left_length = 0;
   break;
  }

  if ((left_sec -= 2) <= 0)
  {
   break;
  }
 }

 return (left_length<0) ? length : length-left_length;
}

 

// 发送字符串,最关键的是组HTTP头,如果这里组不好,服务器响应就有问题

int send_xml()
{

char* pSend;//要发送的内容,应该是一组xml字符串

_snprintf(tmp, 4096,
 "POST /monitorService/services/ConfigServiceSOAP HTTP/1.1/r/n"
 "Content-Type: text/xml/r/n"
 "User-Agent: XML Spy/r/n"
 "SOAPAction: /"http://www.sttri.com.cn/test/test/"/r/n"
 "Host: 127.0.0.1:8081/r/n"
 "Content-Length: %d/r/n"
  "/r/n"
 "/r/n"

  %s,

  strlen(pSend),pSend);

 

//组包结束,开始发送

return TcpSendTimeout(m_hSocket,tmp, leng);

 

}

 

 

int main()
{
 int nRet = 0;
 char buff[2048];
 WSADATA wsaData;   
 WSAStartup(MAKEWORD(2,2),&wsaData);
 
 if ((m_hSocket = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("创建套接字失败");
        exit(errno);
    }
    else
    {
        printf("创建套接字成功/n");
    }

 
 nRet = Connect("127.0.0.1", 8081);

 if (nRet != 0)

{

    exit(errno);
}

 

 send_xml();//这里发送字符串

 

 nRet = TcpRecvTimeout(m_hSocket, buff, 2048, 5);
 close(m_hSocket);

 if (nRet > 0)

 {

      //收到webservice值,开始解析,接收的内容先是HTTP信息头,然后跟着是xml格式的字符串

  }

 WSACleanup();

  
 return 0;
}

 

代码中可能有些笔误,稍做修改就能执行。

这里只是演示如果发送一个webservice请求,并读取回应值。

没有解析xml,推荐用tinyXML或libXML来解析,当然也可以自己写一个简单的专用的xml解析器,一两天时间就可以了,比学习xml库还要来的快。

 

如果不考虑做成通用的soap客户端,以上代码稍加修改完全可以用在任何简单的webseivice环境。

如果要做的通用些,可能还要加上base64,SSL,涉及到口令的部分还要用MD5,字符串有些可能要求编码成UNICODE。不过这些内部都比较好实现,只是要求细心一些。

 

想快速开发一个程序,推荐用MS或gSoap或其他公司封装好的soap库。

想自己体验一下编程乐趣,对写C代码非常感兴趣,可以采用我这种自虐方法。不过,在嵌入平台下如果想节省资源,大多还是上面这种方法来实现。这里对程序员的唯一要求就是细心,因为涉及到大量的用C语言对字符串进行操作。

原创粉丝点击