Utilizando XML-RPC com C#
Introdução
O XML-RPC é um protocolo de chamada de procedimento remoto (RPC) que utiliza XML para codificar suas chamadas e HTTP como um mecanismo de transporte.
É um protocolo simples, definido com poucas linhas de códigos em oposição com a maioria dos sistemas de RPC, onde os documentos padrões são freqüentemente com milhares de páginas e exige apoio de softwares para serem usados. Fonte: http://pt.wikipedia.org/wiki/XML-RPC
O XML-RPC é um protocolo criado em 1989 por Dave Winer na UserLand Software. Ele é o protocolo de WebService utilizado por diversos sistemas. Atualmente ele não é o padrão recomendado para implementações de soluções WebService, sendo o SOAP 1.2 o padrão.
Para maiores informações sobre o SOAP 1.2 recomendo a leitura da sua especificação no W3C disponível em http://www.w3.org/TR/soap/ e do livro Programming Web Services with SOAP de Doug Tidwell, James Snell e Pavel Kulchenko (O’Reilly).
Buscando um pouco na internet encontrei uma biblioteca de XML-RPC para C#, porém achei pouca informação sobre como utilizá-la e explorar todas suas possibilidades. Desta forma resolvi criar este post para quem está iniciando neste mundo de WebService.
Para quem deseja se aprofundar no protocolo XML-RPC recomento a leitura do livro Programming Web Services with XMLRPC de Simon St. Laurent, Joe Johnston e Edd Dumbill (O’Reilly).
Biblioteca
Como base deste aplicativo foi utilizada a biblioteca XML-RPC.NET criada por Charles Cook.
Recursos
- Listagem dos métodos disponíveis no WebService;
- Listagem das estruturas do WebService;
- Chamada de um método do WebService.
Métodos básicos do XML-RPC
O método system.listMethods geralmente está presente nas implementações de servidor WebService. Este método é utilizado pata listar todos os métodos presentes no WebService.
system.listMethods
Na execução deste método há como retorno uma lista contendo o nome de todos os métodos disponíveis no servidor.
Nome:
system.listMethods.
Parametros:
Não há parâmetros
Resultado:
Retorna uma XML-RPC array de Strings que representa o nome dos métodos implementados pelo servidor.
Cada elemento da lista é único, ou seja, não poderá haver duplicidade de nomes.
Não é obrigatório que o servidor retorne todos os métodos implementados pelo servidor. Um exemplo disso é quando deseja-se que um determinado método seja privado.
Exemplo:
Chamada:
<methodCall> <methodName>system.listMethods</methodName> <params></params> </methodCall>
Resposta:
<methodResponse> <params> <param> <value><array><data> <value> <string>system.listMethods</string> </value> <value> <string>system.methodSignature</string> </value> </data></array></value> </param> </params> </methodResponse>
Implementação
Vamos a implementação da API de consulta.
A Primeira coisa a ser criada é a interface se conexão com o Server.
using System; using CookComputing.XmlRpc; namespace WebServiceAPI { public interface IStateName : IXmlRpcProxy { [XmlRpcMethod("system.listMethods")] String[] ListMethods(); } }
Com a interface criada agora podemos realizar a consulta, em meu projeto criarei uma classe chamada APIBase com o seguinte código:
using System; using System.Collections.Generic; using CookComputing.XmlRpc; using System.Text; namespace WebServiceAPI { public class APIBase { internal IStateName proxy; public APIBase(Uri uri) : this(uri, false){} public APIBase(Uri uri, Boolean ignoreCertificateErrors) { if (ignoreCertificateErrors) System.Net.ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true); proxy = (IStateName)XmlRpcProxyGen.Create(typeof(IStateName)); proxy.Url = uri.AbsoluteUri; } public String[] ListMethods() { return proxy.ListMethods(); } } }
Como métodos de instanciamento da classe temos 2 formas, a primeira somente com a URL do WebService (aceitando HTTP e HTTPS), e a segunda com a URL e se serão ignorados os erros de certificado HTTPS inválido.
Nesta classe também há o método ListMethods que por sua vez chama o ListMethods da interface com o WebService.
Vamos ao teste. Instancie a classe de API e execute o método ListMethods conforme demonstrado no código abaixo:
APIBase xmlRpcApi = new APIBase(new Uri("http://servidor_wordpress/xmlrpc.php"), true); String[] WSMethods = xmlRpcApi.ListMethods(); foreach(String methodName in WSMethods) Console.WriteLine(methodName);
Ao executar este código deve retornar o nome de todos os métodos.
Agora vamos implementar na classe APIBase umas funções para facilitar o trabalho no conhecimento das propriedades e métodos do WebService. Ficando o código dessa classe conforme abaixo:
using System; using System.Collections.Generic; using CookComputing.XmlRpc; using System.Text; namespace WebServiceAPI { public class APIBase { internal IStateName proxy; public APIBase(Uri uri) : this(uri, false){} public APIBase(Uri uri, Boolean ignoreCertificateErrors) { if (ignoreCertificateErrors) System.Net.ServicePointManager.ServerCertificateValidationCallback = ((sender, certificate, chain, sslPolicyErrors) => true); proxy = (IStateName)XmlRpcProxyGen.Create(typeof(IStateName)); proxy.Url = uri.AbsoluteUri; } public String[] ListMethods() { return proxy.ListMethods(); } public static String DumpVariable(Object obj){ return DumpVariable(obj, ""); } public static String DumpVariable(Object obj, String printPrefix) { StringBuilder dumpText = new StringBuilder(); try { dumpText.AppendLine(printPrefix + "type ==> " + obj.GetType()); } catch (Exception) { } if (obj is XmlRpcStruct) { foreach(String key in ((XmlRpcStruct)obj).Keys){ dumpText.AppendLine(printPrefix + key); dumpText.AppendLine(DumpVariable(((XmlRpcStruct)obj)[key], printPrefix + "\t")); } } else if (obj is XmlRpcStruct[]) { foreach(XmlRpcStruct r1 in ((XmlRpcStruct[])obj)){ dumpText.AppendLine(DumpVariable(r1, printPrefix + "\t")); } } else if (obj is Object[]) { foreach(Object t in ((Object[])obj)){ dumpText.AppendLine(DumpVariable(t, printPrefix + "\t ")); } } else if (obj is String) { if (obj != null) dumpText.AppendLine(printPrefix + "\t " +obj.ToString()); } else if (obj is String[]) { foreach(Object t in ((String[])obj)){ if (t != null) dumpText.AppendLine(printPrefix + "\t " +t.ToString()); } } else { if (obj != null) dumpText.AppendLine(printPrefix + "\t " + obj.ToString()); } return dumpText.ToString(); } } }
Basicamente foram criados 2 métodos nomeados DumpVariable que realizam o parse dos dados retornados pelo WebService.
Para dar os próximos passos é necessário a documentação do WebService, no tocante a parâmetros de entrada e saída dos métodos. Alguns aplicativos servidores de WebService implementam métodos de consulta da estrutura como é o caso do Webservice da Barracuda (colocarei um código de exemplo para download).
Para o webservice escolhido (WordPress) há uma página de referência dos métodos em :
http://codex.wordpress.org/XML-RPC_wp
Para que se possa utilizar o WebService do wordpress é preciso habilitar em Configurações > Escrita > Ativar os protocolos de publicação XML-RPC do WordPress, Movable Type, MetaWeblog e Blogger.
Para exemplificação escolheremos dois métodos o wp.getUsersBlogs e wp.getPageList. Segue a descrição destes:
wp.getUsersBlogs
Retorna os blogs dos usuários.
Parâmetros:
String username String password
Retorno:
Array Struct Boolean isAdmin String url String blogid String blogName String xmlrpc
wp.getPageList
Retorna uma array com todas as páginas de um blog. Somente informações mínimas são retornadas, para maiores informações pode ser usado o método wp.getPages.
Parâmetros:
Int32 blog_id String username String password
Retorno:
Array Struct Int32 page_id String page_title Int32 page_parent_id DateTime dateCreated
Agora que conhecemos a estrutura dos nossos métodos necessitamos cria-las em nosso projeto. Até o momento criaremos somente a estrutura de retorno do método wp. getUsersBlogs, pois nos próximos passos mostrarei como utilizar as funções DumpVariables para descobrir a estrutura de retorno dos dados.
public struct getUsersBlogsResponse { public Boolean isAdmin; public String url; public String blogid; public String blogName; public String xmlrpc; }
Caso deseje não receber algum parâmetro basta incluir a instrução para ignorar conforme código abaixo. Apenas para exemplificação foi removido o parâmetro xmlrcp. Em nosso exemplo não utilizaremos essa instrução.
[XmlRpcMissingMapping(MappingAction.Ignore)] public struct getUsersBlogsResponse { public Boolean isAdmin; public String url; public String blogid; public String blogName; }
Após a criação das inclua os métodos na interface XML-RPC.
[XmlRpcMethod("wp.getUsersBlogs")] getUsersBlogsResponse[] getUsersBlogs(String username, String password); [XmlRpcMethod("wp.getPageList")] Object getPageList(Int32 blog_id, String username, String password);
Depois inclua as chamadas na classe APIBase
public getUsersBlogsResponse[] UsersBlogs(String username, String password) { return proxy.getUsersBlogs(username, password); } public Object PageList(Int32 blog_id, String username, String password) { return proxy.getPageList(blog_id, username, password); }
Note que o retorno do método PageList foi definido como Object, pois como desejamos conhecer a estrutura o Object é uma forma genérica de retorno para que possamos utilizar a função DumpVariables.
Agora no código principal basta realizar as consultas.
using System; using WebServiceAPI; namespace Test { class Program { public static void Main(string[] args) { APIBase xmlRpcApi = new APIBase(new Uri("http:// servidor_wordpress /xmlrpc.php"), true); String[] WSMethods = xmlRpcApi.ListMethods(); //foreach(String methodName in WSMethods) // Console.WriteLine(methodName); String username = "WordPressAdminUser"; String password = "Senha"; getUsersBlogsResponse[] Blogs = xmlRpcApi.UsersBlogs(username,password); foreach(getUsersBlogsResponse b in Blogs) { Object PList = xmlRpcApi.PageList(Int32.Parse(b.blogid), username,password); Console.WriteLine(b.blogid + " = " + b.blogName); Console.WriteLine(APIBase.DumpVariable(PList)); Console.WriteLine(""); } Console.Write("Press any key to continue . . . "); Console.ReadKey(true); } } }
Executando este código tem-se a seguinte saída:
Pode-se observar pela imagem que o retorno da chamada PageList é um array do tipo XmlRpcStruct e que contem as seguintes variáveis:
String page_id String page_title DateTime dateCreated Datetime date_created_gmt
De posse dessa informação podemos criar a struct e retornar os dados no formato desejado.
public struct getPageListResponse { public String page_id; public String page_title; public DateTime dateCreated; public DateTime date_created_gmt; }
Altere a interface e a API para utilizar essa struct
//Struct [XmlRpcMethod("wp.getPageList")] getPageListResponse[] getPageList(Int32 blog_id, String username, String password); //API public getPageListResponse[] PageList(Int32 blog_id, String username, String password) { return proxy.getPageList(blog_id, username, password); }
E por ultimo a aplicação principal:
using System; using WebServiceAPI; namespace Test { class Program { public static void Main(string[] args) { APIBase xmlRpcApi = new APIBase(new Uri("http:// servidor_wordpress /xmlrpc.php"), true); String[] WSMethods = xmlRpcApi.ListMethods(); //foreach(String methodName in WSMethods) // Console.WriteLine(methodName); String username = "WordPressAdminUser"; String password = "Senha"; getUsersBlogsResponse[] Blogs = xmlRpcApi.UsersBlogs(username,password); foreach(getUsersBlogsResponse b in Blogs) { Console.WriteLine(b.blogid + " = " + b.blogName); getPageListResponse[] PList = xmlRpcApi.PageList(Int32.Parse(b.blogid), username,password); foreach(getPageListResponse pl in PList) { Console.WriteLine("\t" + pl.page_id + ", " + pl.page_title); } Console.WriteLine(""); } Console.Write("Press any key to continue . . . "); Console.ReadKey(true); } } }
Executando este código temos essa saída na tela:
Licença
Este aplicativo pode ser livremente utilizado.
Download
- OSCE, OSED e eCXD: Certificações de desenvolvimento de Exploits - setembro 14, 2021
- Shellcoding – Encontrando endereço da função dinamicamente. Análise da biblioteca block_api - agosto 15, 2021
- OSWE – Uma história de insucessos! - dezembro 29, 2020
Olá meu amigo.
Pesquisando na internet sobre HTTP/XML-RPC achei o seu site e talvez você possa sanar algumas duvidas que tenho que sou leigo em assunto de servidores. Estou querendo alugar um servidor para jogos com hospedagem do servidor nos EUA e o site que aluga os servidores me passou um documento mostrando como acessar e configurar o servidor mas tem umas coisas que não entendo. Quando voce aluga o servidor dedicado voce escolhe o numero de jogadores, localização do servidor e o nome do servidor. Efetuado o pagamento eu preciso configurar o servidor e é ai que eu não entendo o documento. Além dele estar em ingles o documento é falado de uma forma tecnica que deixa o entendimento ruim. Eu vou colocar aqui os trechos mais importantes do documento e depois vou fazer umas perguntas.
RCON CONNECTIVITY
Crysis 3 supports the same HTTP/XML-RPC protocol from Crysis 2. Your server provider will inform you of the IP, port and password required.
Once started, you can use a third party HTTP/XML-RPC client (there is no internally developed HTTP/XML-RPC client available at this time).
Crysis 3 also supports the same rcon protocol from Crysis 2.Your server provider will inform you of the IP, port and password required.
Clients will be able to connect to the dedicated server by using the following command on their in-game console:
rcon_connect addr:%external IP of the dedicated server% port:%port as specified on the dedicated server% pass:%password as specified on the dedicated server%
Once connected, commands may be issued to the dedicated server by using the following command on their in-game console:rcon_command %command%
where %command% is the remote command that they wish to execute on the dedicated server, e.g. rcon_command sv_maxplayers 8
Clients may disconnect from the rcon server by using the following command on their in-game console:rcon_disconnect
Setting the RCON Password:
The rcon password can be set with rcon_password=password in your dedicated.cfg
If rcon_password is not set, rcon will try to use http_password.
If you’re intending to use the same password for http and rcon, then you only need to start rcon with rcon_startserver port:xxxx
LEVEL ROTATION.XML
Custom level rotation files can be created easily with a LevelRotation.xml file, using either built-in or custom playlists and variants. Once the LevelRotation.xml file has been created (see examples below), place it in the root of the build (the same directory as dedicated.cfg).
Note: If you do not intend to change a setting, it doesn’t need to be in the LevelRotation.xml file.
Note: we have provided a number of example LevelRotation.xml files for you to play with
Firstly, using a standard playlist:
If the name of the LevelRotation is one of the existing playlists, i.e. “TDM”,”DM “, “CRASHSPEARS”, “EXTCTF”, “HUNTER”, “ASSAULT”, “MEDLEY”, “CELLREBEL”, “DESIGN”, “TDMPRO”, “DMPRO” then it will use one of these inbuilt playlists for the map/mode combinations. Any map/mode combo’s specified in the LevelRotation.xml will be ignored. These playlists mirror the console playlists, as did Crysis 2. MEDLEY is all levels with all game modes whereas CRASHSPEARS contains a mixture of CRASHSITE and SPEARS game modes.
As minhas duvidas são as seguinte:
dentro do jogo existe um comando quando voce aperta a tecla aspas e crase logo acima do Tab que abre uma tela parecida com o prompt do windows onde fica aparecendo informações do software do jogo e outras coisas. Será que é essa tela que o documento que coloquei aqui chama de console ou o console é realmente um acesso pelo prompt do windows? A outra duvida é que para configurar o server como eu quero fazendo uma configuração Custom eu preciso criar um arquivo chamado level rotation. xml e coloca-lo no mesmo directorio de dedicated.cfg. Onde ficaria esses arquivos na minha maquina ou no servidor e se for no servidor como crio esses arquivos la? via console ou prompt?
Me desculpe o volume de informações Helio!! Esse tipo de informação é tão dificil de ser achar na net e quando vi voce imaginei que pudesse me dar uma força
Obrigado pela atenção e se possivel espero sua resposta!!
Tiago Carneiro Gesto
Tiago,
Como não conheço o seu jogo e a estrutura que o mesmo foi desenvolvido (funcionalidades, integração e etc) fica bem complicado de lhe ajudar. Recomendo conversar com alguém que ja tenha realizado esta integração ou com o fornecedor do servidor.
Abraços.
Cara, comecei a usar a lib mas encontrei uma dificuldade.
Criei as structs de retorno as vezes o retorno do server pode vir diferente.
Quando o retorno é direrente da struct da erro.
Tem uma maneira de deixar o retorno mais genérico?
Abraço