c# curl网络框架_curl c# - CSDN
  • CURL 开源网络

    千次阅读 2014-09-30 16:11:44
    ~~~~我的生活,我的点点滴滴!!

    ~~~~我的生活,我的点点滴滴!!


    游戏之中需要用到大量的客户端与服务端交互的东西,自己动手去实现网络库那是重复造轮子,并且稳定性、效率等不见得好,下面介绍

    一个开源网络库CURL,引用百度百科对CURL的介绍:

          curl是一个基于命令行的应用工具,提供利用URL标准进行文件传输的功能。目前已经支持非常多的流行的互联网协议,如:FTP, 

    FTPS, HTTP, HTTPS, SCP, SFTP, TFTP, TELNET, DICT, LDAP, LDAPS and FILE等。curl支持SSL认证,HTTP POST/PUT,FTP上传,HTTP

    上传、代理、cookies、用户+密码认证、文件续传、代理管道等一系列强大功能。curl是用C语言写的,但是绑定了很多开发语言。大体

    上可以把curl分成命令行工具和libcurl库两个部分,命令行工具可以直接输入指令完成相应功能,libcurl则是一个客户端URL传输库,

    是线程安全且兼容IPv6,可以非常方便地用来做相关开发。


           
    curl可以使用命令行直接操作,也可以使用libcurl库进行上层应用的开发。我们这里主要讲解libcurl库的使用,平台:win7 编

    译器:VS2012 引擎:cocos2dx


           libcurl提供了一组C语言API函数直接调用。首先需要提到的两个函数就是curl_global_init()和curl_global_cleanup()。libcurl

    要用到一系列的全局常量,curl_global_init()初始化这些变量,并分配一些全局资源;curl_global_cleanup()释放这些资源。因此一般情

    况下,在调用libcurl函数之前,先用curl_global_init(CURL_GLOBAL_ALL)做初始化,在调用完毕后,用curl_global_cleanup()退出。需要

    注意的是,这些全局变量和资源不是线程安全的,因此在多线程的环境中,最好不要多次调用curl_global_init()和curl_global_cleanup()。


    libcurl支持3种不同接口调用方式,分别是"easy"、"multi"和"share"模式。

    a、libcurl-easy是一组同步接口,函数都是curl_easy_*形式,这种模式调用curl_easy_perform()函数进行URL数据传输,直到传输完成函数

       才返回;

    b、libcurl-multi是一组异步接口,函数都是curl_multi_*形式,调用curl_multi_perform()函数进行传输,但是每次调用只传一片数据,我

       们可以用select()函数控制多个下载任务进行同步下载,来实现在一个线程中同时下载多个文件;

    c、libcurl-share允许在多线程中操作共享数据。


    下面以libcurl-easy为例讲一下libcurl的函数。


    1、CURL *curl_easy_init()

    此函数需要最先被调用,返回CRUL easy句柄;后续其他函数调用都要用到这个句柄。如果没有调用curl_global_init(),该函数自动调用,但

    是考虑到线程安全的问题,最好自己调用curl_global_init()。


    2、CURLcode curl_easy_setopt(CURL *handle, CURLoption option, parameter)

    所有参数和选项设置都是通过这个函数完成的,它告诉libcurl怎样去进行传输。参数handle即为curl_easy_init()返回的句柄,后面根据option

    的类型,设置相应的parameter值,该函数每次调用只能设置一个选项。具体的option讲解在这篇博文中有较全面的介绍:

    CURLOPT_URL

    字符串类型,该选项设置要处理的URL地址,该选项是进行curl_easy_perform之前唯一必须要设置的选项。

    CURLOPT_COOKIE

    字符串类型,设置http头中的cookie信息。

    CURLOPT_COOKIEFILE

    字符串类型,同CURLOPT_COOKIE,不过cookie信息从文件中读取。

    CURLOPT_FOLLOWLOCATION

    布尔值类型,该参数设置为非零值表示follow服务器返回的重定向信息。

    CURLOPT_POSTFIELDS

    字符串类型,提交http的post操作字符串数据。

    CURLOPT_TIMEOUT

    long数值类型,设置函数执行的最长时间,时间单位为s。

    CURLOPT_CONNECTTIMEOUT

    long数值类型,设置连接服务器最长时间,时间单位为s;当置为0时表示无限长。

    CURLOPT_MAX_RECV_SPEED_LARGE

    curl_off_t类型数据,指定下载过程中最大速度,单位bytes/s。

    CURLOPT_HEADERFUNCTION

    函数指针类型,该选项设置一个处理接收到的header数据的回调函数,函数原型为:

    size_t function( void *ptr, size_t size, size_t nmemb, void *stream);

    其中,ptr指向接收到的header头数据,数据大小为size*nmemb,stream指向调用CURLOPT_WRITEHEADER选项设置的参数。

    该回调函数应返回实际处理的数据量大小,或者出错返回-1。

    CURLOPT_WRITEFUNCTION

    函数指针类型,该选项设置一个处理接收到的下载数据的回调函数,函数原型为:

    size_t function( void *ptr, size_t size, size_t nmemb, void *stream);

    其中,ptr指向接收到的数据,数据大小为size*nmemb,stream指向调用CURLOPT_WRITEDATA选项设置的参数。

    如果函数指针置为NULL,则会调用默认的函数,将数据写入到由CURLOPT_WRITEDATA指定的FILE*中。

    CURLOPT_HTTPHEADER
    curl_slist结构体类型,该选项自定义请求头信息。

    CURLOPT_NOPROGRESS

    布尔值类型,设置该值为非零值关闭PHP为CRUL传输显示的进度条。


    3、void curl_easy_reset(CURL *handle )

    重新初始化CURL句柄的选项设置。


    4、CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ... )

    查询CRUL会话的内部信息,具体说明请参考curl自带文档。


    5、void curl_easy_cleanup(CURL * handle )

    该函数与curl_easy_init函数成对出现,handle即为调用curl_easy_init返回的句柄。该函数在CURL会话结束退出时调用,之后handle无效。


    6、CURLcode curl_easy_perform(CURL *handle)

    执行远程请求


    下面引用别人的一个简单的使用CURL的easy方式(阻塞式连接)的例子:


    .h文件里面添加代码:


    //需要加入头文件
    #include "curl/curl.h"
    
    
    //回调函数声明
    static size_t writehtml(uint8_t* ptr,size_t size,size_t number,void *stream); 
    

    .cpp文件里面添加代码:


    void HallView::denglu(){	//登陆游戏
    	CURL *curl;  
    	CURLcode res;  
    	string cc; 
    	curl=curl_easy_init();  
    	if(curl)  
    	{ 
    		curl_easy_setopt(curl, CURLOPT_URL, ""); //设置请求的地址  
    		curl_easy_setopt(curl, CURLOPT_POST, true); //设置数据类型
    		string caozuo="";
    		curl_easy_setopt(curl, CURLOPT_POSTFIELDS,caozuo.c_str()); //将操作代码,和连接的网站组合,一起发送! 
    		curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
    		curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,HallView::writehtml); //数据处理回调函数  
    		curl_easy_setopt(curl, CURLOPT_WRITEDATA, &cc);//缓冲的内存  
    		curl_easy_setopt(curl,CURLOPT_TIMEOUT_MS,5000);	//设置连接超时时间	
    		res=curl_easy_perform(curl);  
    		if(res!=CURLE_OK)  
    		{  
    			CCDictionary* pDict = CCDictionary::createWithContentsOfFile("chines.xml");
    			string mes=((CCString*)pDict->objectForKey("networking"))->getCString();
    			platform::showMsg(mes);
    		}  
    		curl_easy_cleanup(curl);  
    	}  
    	else  
    	{  
    		CCLog("curl is null");  
    	}  
    }
    
    
    //回调函数处理
    size_t HallView::writehtml(uint8_t* ptr,size_t size,size_t number,void *stream)    
    {     
    	CCString* a=CCString::createWithFormat("%s",ptr);  
    	std::string str1=a->getCString();
    	Json::Reader reader;//json解析  
    	Json::Value value;//表示一个json格式的对象  
    	if(reader.parse(str1,value))//解析出json放到json中区  
    	{  
    		string out=value["gameId"].asString();    
    		gameda->gameId=out;
    		out=value["newIMSI"].asString();
    		gameda->newIMSI=out;
    	}  
    	return size*number;//这里一定要返回实际返回的字节数    
    }
    


    展开全文
  • curl验证,调用webservice接口

    万次阅读 2017-07-13 19:33:11
    概要: wsdl规范的webservice使用的比较广泛,尤其是在异构系统中进行数据交换。验证webserivce的可用性,进而调用webservice的方法,大家都有自己的见解。... 说说自己的方法。 我们将实际的运行环境,分为本地

    概要:

    wsdl规范的webservice使用的比较广泛,尤其是在异构系统中进行数据交换。验证webserivce的可用性,进而调用webservice的方法,大家都有自己的见解。客户端调用遵循wsdl规范的远程webservice服务,发送请求时本质上是http请求,这就为直接通过发送http请求,调用webservic服务提供了理论基础。

    说说自己的方法。

    我们将实际的运行环境,分为本地和线上

    本地,生成客户端(cxf,jdk自带工具都可以),进行调用

    线上,使用curl发送遵循soap协议的http请求,进行调用。


    1、本地验证

    本地验证,就不重复了,自行搜索。

    2、线上环境验证

    线上环境,通常为linux服务器,当然windows也是可以的,协议是通用的。验证环境如下

    环境配置:

              操作系统:linux

             附属工具:curl

     2.1 生成SOAP协议格式的报文参数

    调用远程的webservice服务,是通过http请求,就要构造请求参数,下面给出构造过程和结果

    构造结果是啥样子的呢?

    包含两部分,一个是curl的参数,部分是soap报文

    整体上类似这样子

    curl -H 'content-type: application/xml' -d '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:curlService xmlns:ns2="http://linghushaoixa.github.io"><request><password>yingying</password><userName>linghushaoxia</userName></request></ns2:curlService></soap:Body></soap:Envelope>' http://ip:port/soap/services/curl_soap_service?wsdl


    
    

    格式化一下soap的原文,得到清晰的结构

    soap协议的简要结构:

    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
      <soap:Body>
        <ns2:curlService xmlns:ns2="http://linghushaoixa.github.io">
          <request>
            <password>yingying</password>
            <userName>linghushaoxia</userName>
          </request>
        </ns2:curlService>
      </soap:Body>
    </soap:Envelope>


    基本说明:

    soap:Envelope,报文消息主体声明

    soap:Body,消息体

    ns2:curlService,方法

    xmlns:ns2,目标命名空间,targetNamespace

    request,请求参数,自定义的参数名,由接口的WebParam指定,本例中就命名为了request,不要照搬,导致错误

    
    
    
    
    
    
    
    

    2.2 封装为curl命令

    知道了结构,就可以进行封装了。封装的结果上图已经给出,需要的参数,方法名、参数名、参数的值、wsdl地址

    代码如下

    package com.linghushaoxia.soap.util;
    
    import java.io.StringWriter;
    
    import com.google.gson.Gson;
    import com.thoughtworks.xstream.XStream;
    
    
    /**
     * 功能说明:转换工具类
     * @author: linghushaoxia
     * @time:2017年7月3日上午9:35:28
     * @version:1.0
     *
     */
    public class TransformUtil {
    	/**
    	 * 
    	 * 功能说明:将soap协议的报文封装为curl命令,以便在linux服务器上执行
    	 * @param url
    	 * wsdl的远程地址
    	 * 远程webservice协议的soap报文
    	 * @param soapXml
    	 * @return String
    	 * @time:2017年7月3日上午9:38:59
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	public static String wsdlToCurlWithSoapMsg(String url,String soapXml){
    		String curl ="";
    		StringBuilder curlBuilder = new StringBuilder();
    		curlBuilder.append("curl -H").append(" 'content-type: application/xml'");
    		curlBuilder.append(" -d ").append("'").append(soapXml).append("'");
    		curlBuilder.append(" ").append(url);
    		curl = curlBuilder.toString();
    		return curl;
    	}
    	/**
    	 * 
    	 * 功能说明:将webservice接口调用转为curl命令,以便在linux服务器上执行
    	 * @param wsdlUrl
    	 * wsdl地址
    	 * @param methodName
    	 * 方法名
    	 * @param webParam
    	 * 参数名,webservice客户端声明的名称
    	 * @param request
    	 * 请求参数,pojo
    	 * @param targetNamespace
    	 * 命名空间
    	 * @return String
    	 * @time:2017年7月3日下午3:03:24
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	public static <T> String wsdlToCurl(String wsdlUrl,String methodName,String webParam,T request,String targetNamespace){
    		String curl ="";
    		//封装为curl指令
    		String soapXml  = buildSoapXml(methodName,webParam,request,targetNamespace);
    		curl = wsdlToCurlWithSoapMsg(wsdlUrl, soapXml);
    		return curl;
    	}
    	/**
    	 * 
    	 * 功能说明:将请求参数封装为soap协议的xml报文
    	 * @param methodName
    	 * 方法名
    	 * @param webParam
    	 * 参数名,webservice客户端声明的名称
    	 * @param param
    	 * 请求参数,pojo
    	 * @param targetNamespace
    	 * 命名空间
    	 * @return String
    	 * @time:2017年7月3日下午4:13:03
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	private static <T> String buildSoapXml(String methodName,String webParam,T param,String targetNamespace){
    		/**
    		 * 封装soap协议格式
    		 * 格式从cxf的客户端报文中截取得到
    		 * 包含四部分
    		 * start-body--method-param
    		 */
    		StringBuilder soapBuilder = new StringBuilder();
    		appendSoapEnvelopeStart(soapBuilder);
    		appendSoapBodyStart(soapBuilder);
    		appendSoapMethodStart(soapBuilder,methodName,targetNamespace);
    		appendSoapParam(soapBuilder,webParam,param);
    		appendSoapMethodEnd(soapBuilder,methodName);
    		appendSoapBodyEnd(soapBuilder);
    		appendSoapEnvelopeEnd(soapBuilder);
    		return soapBuilder.toString();
    	}
    	/**
    	 * 
    	 * 功能说明:soap协议声明,开始
    	 * @param soapBuilder
    	 * @time:2017年7月3日下午6:17:06
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	private static void appendSoapEnvelopeStart(StringBuilder soapBuilder){
    		soapBuilder.append("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">");
    	}
    	/**
    	 * 
    	 * 功能说明:soap协议声明,结束
    	 * @param soapBuilder
    	 * @time:2017年7月3日下午6:17:29
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	private static void appendSoapEnvelopeEnd(StringBuilder soapBuilder){
    		soapBuilder.append("</soap:Envelope>");
    	}
    	/**
    	 * 
    	 * 功能说明:soap协议body,开始
    	 * @param soapBuilder
    	 * @time:2017年7月3日下午6:18:09
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	private static void appendSoapBodyStart(StringBuilder soapBuilder){
    		soapBuilder.append("<soap:Body>");
    	}
    	/**
    	 * 
    	 * 功能说明:soap协议body,结束
    	 * @param soapBuilder
    	 * @time:2017年7月3日下午6:18:23
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	private static void appendSoapBodyEnd(StringBuilder soapBuilder){
    		soapBuilder.append("</soap:Body>");
    
    	}
    	/**
    	 * 
    	 * 功能说明:soap协议方法,开始
    	 * @param soapBuilder
    	 * 构建字符串对象
    	 * @param methodName
    	 * 方法名
    	 * @param targetNamespace 
    	 * 命名空间
    	 * @time:2017年7月3日下午6:18:42
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	private static void appendSoapMethodStart(StringBuilder soapBuilder,String methodName,String targetNamespace){
    		soapBuilder.append("<ns2:").append(methodName);
    		soapBuilder.append(" ").append("xmlns:ns2=\"").append(targetNamespace).append("\"").append(">");
    	}
    	/**
    	 * 
    	 * 功能说明:soap协议方法,结束
    	 * @param soapBuilder
    	 * @param methodName
    	 * 方法名
    	 * @time:2017年7月3日下午6:19:03
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	private static void appendSoapMethodEnd(StringBuilder soapBuilder,String methodName){
    		soapBuilder.append("</ns2:").append(methodName).append(">");
    	}
    	/**
    	 * 
    	 * 功能说明:soap协议,封装参数
    	 * @param soapBuilder
    	 * @param webParam
    	 * 参数名,webservice客户端声明的名称
    	 * @param param
    	 * 请求参数,pojo
    	 * @time:2017年7月3日下午6:19:21
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	private static <T>  void appendSoapParam(StringBuilder soapBuilder,String webParam,T param ){
    		soapBuilder.append(beanToXml(param,webParam));
    	}
    
    	/**
    	 * 
    	 * 功能说明:java对象转为xml字符串,XStream
    	 * @param obj
    	 * @return String
    	 * @time:2017年7月3日下午4:03:03
    	 * @author:linghushaoxia
    	 * @exception:
    	 *
    	 */
    	private static <T> String beanToXml(T obj,String webParam){
    		//返回结果
    		String xmlResult="";
    		XStream xstream = new XStream( ); 
    		//类别名,节点的名称,默认是全类名
    		xstream.alias(webParam, obj.getClass());
    		String xmlFormat= xstream.toXML(obj);
    		/**
    		 * 取消格式化
    		 */
    		xmlResult=xmlFormat.replaceAll("\\s*|\\t|\\r|\\n", "");
    		return xmlResult;
    	}
    }
    

    以上代码中几个关键参数,是怎么得到的?找到客户端产生的接口,本例为,ICurlSoapService

    如下图



    测试

    /**
     * 功能说明:curl访问soap
     * @author: linghushaoxia
     * @time:2017年7月5日下午12:52:23
     * @version:1.0
     *
     */
    public class CurlSoapServiceDemo {
    	public static void main(String[] args) {
    		//wsdl地址
    		String wsdlUrl="http://ip:port/cesso/services/curl_soap_service?wsdl";
    		//所要调用的方法名
    		String methodName="curlService";
    		/**
    		 * 请求参数
    		 */
    		SoapRequest request=new SoapRequest();
    		request.setUserName("linghushaoxia");
    		request.setPassword("yingying");
    		String webParam="request";
    		//命名空间
    		String targetNamespace="http://linghushaoixa.github.io";
    		String curl= TransformUtil.wsdlToCurl(wsdlUrl, methodName, webParam,request, targetNamespace);
    		System.out.println("curl=");
    		System.out.println(curl);
    	}


    2.3 调用

    登录到linux服务器,就可以执行了

    curl -H 'content-type: application/xml' -d '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:curlService xmlns:ns2="http://linghushaoixa.github.io"><request><password>yingying</password><userName>linghushaoxia</userName></request></ns2:curlService></soap:Body></soap:Envelope>' http://ip:port/cesso/services/curl_soap_service?wsdl



    结果如图

    可以看到,返回码为10001,msg为succes。这是服务端返回的结果,返回结果仍然以soap协议的报文给出。

    思考:

    怎么得到soap报文的格式呢?这是一个好问题,不同的框架,有不同的方法,欢迎留言讨论。

    参考:

    http://blog.csdn.net/russ44/article/details/53308838


    展开全文
  • C#以太坊基础入门

    2019-05-06 10:54:56
    在这一部分,我们将使用C#开发一个最简单的.Net控制台应用,来接入以太坊节点,并打印 所连接节点旳版本信息。通过这一部分的学习,你将掌握以下技能: 如何使用节点仿真器 如何在命令行访问以太坊节点 如何在C#...
        

    在这一部分,我们将使用C#开发一个最简单的.Net控制台应用,来接入以太坊节点,并打印 所连接节点旳版本信息。通过这一部分的学习,你将掌握以下技能:

    1. 如何使用节点仿真器
    2. 如何在命令行访问以太坊节点
    3. 如何在C#代码中访问以太坊节点

    我们将使用ganache来模拟以太坊节点。ganache虽然不是一个真正的以太坊节点软件, 但它完整实现了以太坊的JSON RPC接口,非常适合以太坊智能合约与去中心化应用开发的 学习与快速验证:

    ganache启动后将在8545端口监听http请求,因此,我们会将JSON RPC调用请求 使用http协议发送到节点旳8545端口。不同的节点软件可能会使用不同的监听端口,但 大部分节点软件通常默认使用8545端口。

    以太坊规定了节点必须实现web3_clientVersion 调用来返回节点软件的版本信息,因此我们可以用这个命令来测试与 节点旳链接是否成功。

    ganache-cli是以太坊节点仿真器软件ganache的命令行版本,可以方便开发者快速进行 以太坊DApp的开发与测试。在windows下你也可以使用其GUI版本。启动ganache很简单,只需要在命令行执行ganache-cli即可:ganache-cli是一个完整的词,-两边是没有空格的。一切顺利的话,你会看到与下图类似的屏幕输出:

    默认情况下,ganache会随机创建10个账户,每个账户中都有100ETH的余额。你可以在 命令行中指定一些参数来调整这一默认行为。例如使用-a--acounts参数来指定 要创建的账户数量为20:

    ganache-cli -a 20
    

    使用curl获取节点版本信息

    以太坊规定了节点必须实现web3_clientVersion 接口来向外部应用提供节点旳版本信息。接口协议的交互流程如下:

    这是一个典型的请求/应答模型,请求包和响应包都是标准的JSON格式。其中,jsonrpc字段用来 标识协议版本,id则用来帮助建立响应包与请求包的对应关系。

    在请求包中,使用method字段来声明接口方法,例如web3_clientVersion,使用params 字段来声明接口方法的参数数组。 在响应包中,result字段中保存了命令执行的返回结果。

    以太坊JSON RPC并没有规定传输层的实现,不过大部分节点都会实现HTTP和IPC的访问。因此 我们可以使用命令行工具curl来测试这个接口:

    curl http://localhost:8545 -X POST -d '{"jsonrpc": "2.0","method": "web3_clientVersion","params": [], "id": 123}'

    使用C#获取节点版本信息

    就像前一节看到的,我们只要在C#代码中按照以太坊RPC接口要求发送http请求包就可以了。 你可以使用任何一个你喜欢的http库,甚至直接使用socket来调用以太坊的JSON RPC API。例如,下面的代码使用.Net内置的HttpClient类来访问以太坊节点,注意代码中的注释:

    using System;
    using System.Net.Http;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace GetVersionByHttpDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("cuiyw-test");
                GetVersion().Wait();
                GetAccounts().Wait();
                Console.ReadLine();
            }
            static async Task GetVersion()
            {
                HttpClient httpClient = new HttpClient();
    
                string url = "http://localhost:7545";
                string payload = "{\"jsonrpc\":\"2.0\",\"method\":\"web3_clientVersion\",\"params\":[],\"id\":7878}";
                Console.WriteLine("<= " + payload);
                StringContent content = new StringContent(payload, Encoding.UTF8, "application/json");
                HttpResponseMessage rsp = await httpClient.PostAsync(url, content);
                string ret = await rsp.Content.ReadAsStringAsync();
                Console.WriteLine("=> " + ret);
            }
    
            static async Task GetAccounts()
            {
                HttpClient httpClient = new HttpClient();
    
                string url = "http://localhost:7545";
                string payload = "{\"jsonrpc\":\"2.0\",\"method\":\"eth_accounts\",\"params\":[],\"id\":5777}";
                Console.WriteLine("<= " + payload);
                StringContent content = new StringContent(payload, Encoding.UTF8, "application/json");
                HttpResponseMessage rsp = await httpClient.PostAsync(url, content);
                string ret = await rsp.Content.ReadAsStringAsync();
                Console.WriteLine("=> " + ret);
            }
        }
    }
    

    序列化与反序列化

    在应用逻辑里直接拼接RPC请求字符串,或者直接解析RPC响应字符串,都不是 令人舒心的事情。

    更干净的办法是使用数据传输对象(Data Transfer Object)层来 隔离这个问题,在DTO层将C#的对象序列化为Json字符串,或者从Json字符串 反序列化为C#的对象,应用代码只需要操作C#对象即可。

    我们首先定义出JSON请求与响应所对应的C#类。例如:

    现在我们获取节点版本的代码可以不用直接操作字符串了:

    如下图,在SerializeDemo中定义了请求与响应的model。

    RpcRequestMessage

    using System;
    using System.Collections.Generic;
    using System.Text;
    using Newtonsoft.Json;
    namespace SerializeDemo
    {
        class RpcRequestMessage
        {
            public RpcRequestMessage(string method, params object[] parameters)
            {
                Id = Environment.TickCount;
                Method = method;
                Parameters = parameters;
            }
    
            [JsonProperty("id")]
            public int Id;
    
            [JsonProperty("jsonrpc")]
            public string JsonRpc = "2.0";
    
            [JsonProperty("method")]
            public string Method;
    
            [JsonProperty("params")]
            public object Parameters;
        }
    }
    

    RpcResponseMessage

    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace SerializeDemo
    {
        class RpcResponseMessage
        {
            [JsonProperty("id")]
            public int Id { get; set; }
    
            [JsonProperty("jsonrpc")]
            public string JsonRpc { get; set; }
    
            [JsonProperty("result")]
            public object Result { get; set; }
        }
    }
    

    RpcHttpDto

    using Newtonsoft.Json;
    using System;
    using System.Collections.Generic;
    using System.Net.Http;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace SerializeDemo
    {
        class RpcHttpDto
        {
            public async Task Run()
            {
                var version = await Rpc("web3_clientVersion");
                Console.WriteLine("version => " + version + " type => " + version.GetType().Name);
                var accounts = await Rpc("eth_accounts");
                Console.WriteLine("accounts => " + accounts + " type => " + accounts.GetType().Name);
            }
    
            public async Task<object> Rpc(string method)
            {
                HttpClient httpClient = new HttpClient();
    
                string url = "http://localhost:7545";
    
                RpcRequestMessage rpcReqMsg = new RpcRequestMessage(method);
                string payload = JsonConvert.SerializeObject(rpcReqMsg);
                Console.WriteLine("<= " + payload);
    
                StringContent content = new StringContent(payload, Encoding.UTF8, "application/json");
                HttpResponseMessage rsp = await httpClient.PostAsync(url, content);
    
                string ret = await rsp.Content.ReadAsStringAsync();
                Console.WriteLine("=> " + ret);
                RpcResponseMessage rpcRspMsg = JsonConvert.DeserializeObject<RpcResponseMessage>(ret);
                return rpcRspMsg.Result;
            }
        }
    }
    

    Program

    using System;
    
    namespace SerializeDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("cuiyw-test");
                Console.WriteLine("Call Ethereum RPC Api with HttpClient");
                RpcHttpDto demo = new RpcHttpDto();
                demo.Run().Wait();
                Console.ReadLine();
            }
        }
    }
    

    使用现成的轮子

    尽管可行,但我还是建议你尽量避免自己去封装这些rpc接口,毕竟 这个事已经做过好几次了,而且rpc接口封装仅仅是整个故事的一部分。

    Nethereum是以太坊官方推荐的.Net下的rpc接口封装库,因此我们优先 选择它。

    下面是使用Nethereum获取节点版本信息的代码:

                    Web3 web3 = new Web3("http://localhost:7545");
                    string version = await web3.Client.SendRequestAsync<string>("web3_clientVersion");
                    Console.WriteLine("version => " + version);
    

    Web3是Nethereum的入口类,我们与以太坊的交互,基本上是通过 这个入口来完成的,实例化Web3需要指定要链接的节点地址,例如本地ganache节点,就 可以使用http://localhost:7545这个地址。

    Web3实例的Client属性是一个IClient接口的实现对象,这个接口抽象了与 节点的RPC接口交互的方法,因此与具体的通信传输机制无关:

    从上图容易看出,Nethereum目前支持通过四种不同的通信机制来访问以太坊: Http、WebSocket、命名管道和Unix套接字。

    容易理解,当我们提供一个节点url作为Web3实例化的参数时,Web3将自动创建 一个基于Http的IClient实现实例,即RpcClient实例。

    一旦获得了Iclient的实现实例,就可以调用其SendRequestAsync<T>()方法来向节点 提交请求了,例如,下面的代码提交一个web3_clientVersion调用请求:

    string version = await web3.Client.SendRequestAsync<string>("web3_clientVersion");
    

    SendRequestAsync()是一个泛型方法,其泛型参数T用来声明返回值的类型。例如, 对于web3_clientVersion调用,其RPC响应的result字段是一个字符串,因此我们使用 string作为泛型参数。

    需要指出的是,SendRequestAsync()不需要我们传入完整的请求报文,其返回的结果 也不是完整的响应报文,只是其中result字段的内容。

    对于需要传入参数的RPC调用,例如用来计算字符串keccak哈希值的 web3_sha3调用, 可以在SendRequestAsync()方法自第3个参数开始依次写入。例如,下面的代码 计算hello,ethereum的keccak哈希:

                    HexUTF8String hexstr = new HexUTF8String("hello,ethereum");
                    Console.WriteLine("hello,ethereum => " + hexstr.HexValue);
                    string hash = await web3.Client.SendRequestAsync<string>("web3_sha3", null, hexstr);
                    Console.WriteLine("keccak hash => " + hash);
    

    SendRequestAsync()方法的第2个参数表示路由名称,可以用来拦截RPC请求,满足 一些特殊的应用需求,我们通常将其设置为null即可。由于web3_sha3调用要求传入 的参数为16进制字符串格式,例如,hello,ethereum应当表示为0x68656c6c6f2c657468657265756d, 因此我们使用HexUtf8String类进行转换:

    使用RPC接口封装类

    如果你倾向于薄薄一层的封装,那么使用IClient的SendRequestAsync()接口, 已经可以满足大部分访问以太坊的需求了,而且基本上只需要参考RPC API的手册, 就可以完成工作了。不过Nethereum走的更远。

    Nethereum为每一个RPC接口都封装了单独的类。

    例如,对于web3_clientVersion调用,其对应的实现类为Web3ClientVersion; 而对于web3_sha3调用,其对应的实现类为Web3Sha3:

    有一点有助于我们的开发:容易根据RPC调用的名字猜测出封装类的名称 —— 去掉 下划线,然后转换为单词首字母大写的Pascal风格的命名。

    由于每一个RPC接口的封装类都依赖于一个IClient接口的实现,因此我们可以直接 在接口封装类实例上调用SendRequestAsync()方法,而无须再显式地使用一个IClient 实现对象来承载请求 —— 当然在创建封装类实例时需要传入IClient的实现对象。

    例如,下面的代码使用类Web3ClientVersion来获取节点版本信息:

                    Web3ClientVersion w3cv = new Web3ClientVersion(web3.Client);
                    string version = await w3cv.SendRequestAsync();
                    Console.WriteLine("version => " + version);
    

    容易注意到封装类的SendRequestAsync()方法不再需要使用泛型参数声明返回值的 类型,这是因为特定RPC接口的对应封装类在定义时已经确定了调用返回值的类型。例如:

    namespace Nethereum.RPC.Web3
    {
        public class Web3ClientVersion : GenericRpcRequestResponseHandlerNoParam<string>
        {
            public Web3ClientVersion(IClient client);
        }
    }
    

    如果RPC接口需要额外的参数,例如web3_sha3,那么在SendRequestAsync() 方法中依次传入即可。例如,下面的代码使用Web3Sha3类来计算一个字符串 的keccak哈希值:

                    HexUTF8String hexstr = new HexUTF8String("hello,ethereum");
                    Web3Sha3 w3s = new Web3Sha3(web3.Client);
                    string hash = await w3s.SendRequestAsync(hexstr);
                    Console.WriteLine("keccak hash => " + hash);
    

    接口封装类比直接使用IClient提供了更多的类型检查能力,但同时也 带来了额外的负担 —— 需要同时查阅RPC API接口文档和Nethereum的接口 封装类文档,才能顺利地完成任务。

    using Nethereum.Hex.HexTypes;
    using Nethereum.RPC.Web3;
    using Nethereum.Web3;
    using System;
    using System.Threading.Tasks;
    
    namespace Web3HeavyDemo
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("cuiyw-test");
                Console.WriteLine("Access Ethereum with Nethereum");
                Task.Run(async () => {
                    Web3 web3 = new Web3("http://localhost:7545");
    
                    Web3ClientVersion w3cv = new Web3ClientVersion(web3.Client);
                    string version = await w3cv.SendRequestAsync();
                    Console.WriteLine("version => " + version);
    
                    HexUTF8String hexstr = new HexUTF8String("hello,ethereum");
                    Web3Sha3 w3s = new Web3Sha3(web3.Client);
                    string hash = await w3s.SendRequestAsync(hexstr);
                    Console.WriteLine("keccak hash => " + hash);
                }).Wait();
                Console.ReadLine();
            }
        }
    }
    

    理解Nethereum的命名规则

    大多数情况下,我们容易从以太坊的RPC接口名称,推测出Nethereum的封装类名称。但是别忘了,在C#中,还有个命名空间的问题。

    Nethereum根据不同的RPC接口系列,在不同的命名空间定义接口实现类。 例如对于web3_*这一族的接口,其封装类的命名空间为Nethereum.RPC.Web3:

    但是,对于eth_*系列的接口,并不是所有的封装类都定义在Nethereum.RPC.Eth 命名空间,Nethereum又任性地做了一些额外的工作 —— 根据接口的功能划分了一些 子命名空间!例如,和交易有关的接口封装类,被归入Nethereum.RPC.Eth.Transactions命名 空间,而和块有关的接口封装类,则被归入Nethereum.RPC.Eth.Blocks命名空间。

    显然,如果你从一个RPC调用出发,尝试推测出它在Nethereum中正确的命名空间和 封装类名称,这种设计并不友好 —— 虽然方便了Nethereume的开发者维护代码, 但会让Nethereum的使用者感到崩溃 —— 不可预测的API只会伤害开发效率。

    using Nethereum.Hex.HexTypes;
    using Nethereum.RPC.Eth;
    using Nethereum.Web3;
    using System;
    using System.Threading.Tasks;
    
    namespace Web3Namerules
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("cuiyw-test");
                Console.WriteLine("Access Ethereum with Nethereum");
                Task.Run(async () => {
                    Web3 web3 = new Web3("http://localhost:7545");
    
                    EthAccounts ea = new EthAccounts(web3.Client);
                    string[] accounts = await ea.SendRequestAsync();
                    Console.WriteLine("accounts => \n" + string.Join("\n", accounts));
    
                    EthGasPrice egp = new EthGasPrice(web3.Client);
                    HexBigInteger price = await egp.SendRequestAsync();
                    Console.WriteLine("gas price => " + price.Value);
                }).Wait();
                Console.ReadLine();
            }
        }
    }
    

    使用Web3入口类

    Netherem推荐通过入口类Web3来使用接口封装类,这可以在某种程度上减轻 复杂的命名空间设计给使用者带来的困扰。

    例如,我们可以使用web3.Eth.Accounts来直接访问EthAccounts类的实例, 而无须引入命名空间来实例化:

    也就是说,在实例化入口类Web3的时候,Nethereum同时也创建好了所有的接口 封装类的实例,并挂接在不同的属性(例如Eth)之下。

    我们可以先忽略Eth属性的具体类型,简单地将其视为接口封装对象的容器。 因此,当我们需要使用EthGetBalance类的时候,通过web3.Eth.GetBalance 即可访问到其实例对象;同样,当我们希望使用EthSendTransaction类时, 则可以通过web3.Eth.Transactions.SendTransaction来访问其实例对象 —— 它在子容器Transactions里:

    例如,下面的代码调用eth_accounts接口获取节点账户列表,然后调用 eth_getBalance接口获取第一个账户的余额:

        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("cuiyw-test");
                Console.WriteLine("Web3 Entry Demo");
                Web3Entry demo = new Web3Entry();
                demo.Run().Wait();
                Console.ReadLine();
            }
        }
    
    using Nethereum.Hex.HexTypes;
    using Nethereum.Web3;
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace Web3EntryDemo
    {
        class Web3Entry
        {
            public async Task Run()
            {
                Web3 web3 = new Web3("http://localhost:7545");
                string[] accounts = await web3.Eth.Accounts.SendRequestAsync();
                Console.WriteLine("account#0 => " + accounts[0]);
                HexBigInteger balance = await web3.Eth.GetBalance.SendRequestAsync(accounts[0]);
                Console.WriteLine("balance => " + balance.Value);
            }
        }
    }
    

    由于eth_getBalance 返回的账户余额采用16进制字符串表示,因此我们需要使用HexBigInteger类型 的变量来接收这个值:

    C#以太坊基础入门

    4624570-7ad1442474a63a1f.gif
    好书推荐、视频分享,与您一起进步
    展开全文
  • 之前做定时器同步方法试过很多方法, 不过都有些问题 1)quartz + IIS 方式(web项目发布到IIS上,出现IIS应用池回收问题) 2)用线程Timer方式 (出现多个线程同步同个任务问题) 3)window计划任务+ api方法 ...

    之前做定时器同步方法试过很多方法, 不过都有些问题

    1)quartz + IIS 方式(web项目发布到IIS上,出现IIS应用池回收问题)

    2)用线程Timer方式 (出现多个线程同步同个任务问题)

    3)window计划任务+ api方法 方式

    4)quartz+ window服务(暂不到怎么弄)

    最后确定用第(3)种方式,解决了定时同步数据问题。

    假如我们需要每隔5分钟定时同步数据,那么做一个api 同步方法映射到外部可调用,

    然后用window计划任务做一个任务,定时调用批处理文件(文件包含调用api方法的语法)

    我这边做一个每隔5分钟调用方法获取数据demo

    1、api方法

             /// <summary>
            /// 获取配送点列表(收货客户或送货客户)
            /// </summary>
            /// <param name="obj">传递数据</param>
            /// <returns></returns>
            [System.Web.Http.HttpPost]
            public string GetDeliveryPointList([FromBody] JObject obj)
            {
                MobileResult result = myService.GetDeliveryPointList(obj);
                return JsonConvert.SerializeObject(result);
            }

    注意:这个方法外部可通过ajax调用的到,如果是要同步数据,那边方法改成可同步数据方法

    2、配置curl表达式

    @echo off 
    curl --data "PAGE_NUM=1&ROW_NUM=5" http://14.23.113.2:9698/api/IPhoneApp/MobileApp/GetDeliveryPointList/1 | iconv -f utf-8 -t gbk >>D:\Sync\SyncFuns_log.txt
    exit

    注意:“PAGE_NUM=1&ROW_NUM=5”这个是方法的参数,“http://14.23.113.2:9698/api/IPhoneApp/MobileApp/GetDeliveryPointList/1”这个是请求路径,

      “| iconv -f utf-8 -t gbk” 这个是为了防止返回数据出现乱码问题,需安装libiconv.exe工具才支持,“>>D:\Sync\SyncFuns_log.txt”:保存调用日志信息

    3、安装curl程序

         下载地址:https://pan.baidu.com/s/13acW5HCatnhb1KPlzNSbYA, 解压压缩文件

    将1(curl.exe)文件复制到C:\Windows\System32\目录下,将2(libiconv.exe)安装后,将安装路径添加到环境变量Path中 ,

    C:\Program Files (x86)\GnuWin32\bin ,将上面curl表达式保存成批处理文件(SyncFuns.bat)

    4、创建window计划任务

     1)创建基本任务

    1.1)常规(选中圈中的的内容)

    1.2)触发器

     新增触发器,选中重复任务间隔5分钟,持续时间为无限期

    1.3)操作

    新增任务,选中批处理文件(SyncFuns.bat)

    1.4) 条件

    电源记得去掉,如果电脑没插电源,计划任务不会触发

    5、启用计划任务

    查看运行结果

     

    转载于:https://www.cnblogs.com/xielong/p/9708469.html

    展开全文
  • curl,就是create,update,remove,list的首字母简写。 curl程序员,就是增改删查程序员,中文说增删改查更加顺口。 curl程序员,做的是低技术含量的增删改查工作。 下面简单回顾一下我做增...有时也用C#curl; Java...
  • 笔者作为一位tool mad,将工作以来用到的各种优秀资料、神器及框架整理在此,毕竟好记性不如烂键盘,此项目可以作为自己的不时之需。 本人喜欢折腾,记录的东西也比较杂,各方面都会有一些,内容按重要等级排序,...
  • C# HttpWebRequest三件需要注意的事

    千次阅读 2016-11-04 14:18:58
    使用微软的.Net框架的时候很可能会接触到HttWebRequest。...开始的时候使用者很可能认为这是网络连接的原因,但是最近通过和wGet和cUrl两个第三方库进行对比发现这种现象的问题关键并不是网络拥塞原因,而是.Net实现
  • MUI框架-11-MUI前端 +php后台接入百度文字识别API 这里后台不止一种,Python,Java,PHP,Node,C++,C# 都可以 这里使用的是 php 来介绍,已经解决所有问题,因为处理很多错误,可能会比较复杂,请大家坚持按...
  • file_create($filename, $mimetype, $postname),均接收3个参数,前端post来到文件数据,通过$_FILES或者各框架中的Request类中接收的文件数据,可以通过临时文件名构造curlFile对象,从而在curl中直接再将数据post...
  • 现在基于c#的开发框架.net core已经开源并支持跨平台运行,现在已经发布.net core2.2,已经非常成熟,很优秀,推荐大家使用,于是我也尝试使用树莓派进行部署并以后用于深入进行物联网的一些操作。。 从淘宝买了一...
  • curl,就是create,update,remove,list的首字母简写。说是CRUD似乎更流行些,不过无所谓,知道是一个意思就好。 curl程序员,就是增改删查程序员,中文说增删改查更加顺口。 curl程序员,做的是低技术含量的增删改...
  • 1、概述 短网址的好处众多,便于记忆,占用字符少等,现在市面上出现了众多的将长网址转变为短网址的方法,但是由于他们都是小的公司在幕后运营,所以很不靠谱,面对随时关闭服务的可能,这样也导致我们将转换好了的...
  • CentOS7下用.NET Core写C#程序

    千次阅读 2017-08-17 20:16:59
    2017 微软一个比较大的动作就是.Net core2.0了,微软出的这个框架野心很大:它可以让用户在几乎所有的PC终端用C#编写自己的应用程序。在win/Linux/mac三大主流操作系统通吃之后,.Net core居然也支持Docker! 本文...
  • 【PHP】远程调用以及RPC框架

    万次阅读 2016-08-24 15:15:44
    前言一个项目,从开始到版本更新,一直到最后的版本维护。功能在不断增多,对应的代码量也在不断增加,也就意味着项目变得更不可维护,这时候,我们需要用拆分的方式将一个项目打散,以便开发团队更好的对项目进行...
  • curl.js的创建者John Hann的访谈 在JavaScript的世界里,约翰·汉恩是一个BAMF他一贯的手柄unscriptable ,但应该是他应该叫过去的事情。 John创建并贡献了许多令人难以置信JavaScript工具-只需查看他的GitHub...
  • 很多来自世界各地的程序员不求回报的写代码为别人造轮子、贡献代码、开发框架。开放源代码使得分散在世界各地的程序员们都能够贡献他们的代码与创新。 Python就是这样一门受到全世界各地开源社区支持的语言。...
  • 框架,库和工具 API 应用框架 应用模板 身份验证和授权 Blockchain 博特 构建自动化 捆绑和缩小 高速缓存 CMS 代码分析和指标 压缩 编译器,管道工和语言 加密 数据库 数据库驱动 数据库工具和...
  • Rider在这里肯定不是骑士的意思啦,Rider是Jetbrains公司推出的跨平台c#开发工具。跨平台意味着它和j家全家桶一样都可以在macOS下安装运行。 那么,安利时间,为什么选择Rider呢?我个人总结了以下几个选择Rider而...
  • c#爬虫程序

    2018-06-06 22:05:08
    这篇文章只是简单展示一个基于HTTP请求如何抓取数据的文章,如觉得简单的朋友,后续我们再慢慢深入研究探讨。图1: 如图1,我们工作过程中,无论平台网站还是企业官网,总少不了新闻展示。如某天产品经理跟我们说,...
1 2 3 4 5 ... 20
收藏数 2,067
精华内容 826
关键字:

c# curl网络框架