BeginEndContext de HTTPListener ne réagit pas au sein d'un service Windows

Signaler
Messages postés
4
Date d'inscription
vendredi 16 avril 2021
Statut
Membre
Dernière intervention
16 avril 2021
-
Messages postés
15565
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 mai 2021
-
Bonjour,

Je suis nouveau sur ce forum. Je l'ai découvert par hasard. Je vois qu'il est actif. J'y viendrai plus souvent pour me tenir au courant pour parfaire mon apprentissage de C# et je répondrai à certaines questions que les gens se posent.

En ce moment, j'ai développé un serveur SOAP sous la forme d'un Windows Forms Application. Je suis à même d'écouter sur un port de communication, une transmission sécurisée sans aucun problème. Je démarre mon application avec l'aide d'un bouton « Démarrer » et je l'arrête avec un autre bouton « Arrêter ». Mon service est apte à rouler conformément à mes exigences, sans problème.

Afin d'améliorer celui-ci, je veux transformer mon application en service Windows. Ça donne l'avantage de rouler en arrière-plan et de démarrer dès que le poste redémarre. J'ai déjà expérimenté de nombreuses autres applications en service Windows. Malheureusement pour moi, j'ai un problème majeur. Le service écoute (j'ai fait un test), mais est incapable de récupérer le message SOAP envoyé.

En gros, j'utilise la classe HTTPListener. Afin de démarrer la capture du message entrant, j'utilise la méthode « BeginGetContext ». Je vous rappelle que je fais comme l'application qui fonctionne déjà admirablement ainsi. Normalement, il y a un déclencheur qui nous renverra vers une autre fonction de type « ListenerCallback ».

Je peux mettre le code essentiel au fonctionnement sans être spécifique à mes besoins de lecture du message.

Alors, contrairement à l'application Windows, le service Windows n'exécute rien à partir de ce moment. Il reste figé.

S'il y a une ou des personnes qui ont déjà expérimenté le même problème avec un HTTPListener à l'intérieur d'un service Windows, je peux répondre en mettant le code causant le problème. C'est pas très gros comme code au fond, mais c'est peut-être une cinquantaine de lignes environ. Je juge qu'à cette étape-ci, ça ne ferait qu'alourdir la discussion.

Passez tous une belle journée!

1 réponse

Messages postés
15565
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 mai 2021
849
bonjour,
comment as-tu vérifié que le service écoutait? la session a-t-elle été acceptée par le service?
Messages postés
4
Date d'inscription
vendredi 16 avril 2021
Statut
Membre
Dernière intervention
16 avril 2021

Bonjour yg_be,

J'ai vérifié que le service écoute sur le bon port en tapant la commande suivante dans une fenêtre CMD de Windows :

telnet localhost 30010

parce que je développe sur mon ordinateur local. Lorsque j'exécute cette commande le curseur de cette fenêtre CMD, vide la fenêtre et va s'installer en haut et à gauche. Pour obtenir la commande « telnet », on doit activer cette option dans les paramètres de Windows. Cette commande fonctionnera partout. C'est une commande livrée par Microsoft.

Voici le code simplifié de mon service :


public partial class SOAPServer_Service : ServiceBase {

public string ErrorMessage = "";

private HttpListener Listener;

public SOAPServer_Service(){
InitializeComponent();
}

protected void OnStart(string[] args) {

try {
this.ErrorMessage = "";

string Port = "30010",
HashCertificat = "8f3146c64cb75a716a3543dfc10c2967acab9471",
URLEnvironment = "https://10.182.133.101:30010/WS/";

Process ProcessusDelete = new Process();
ProcessStartInfo StartInfoDelete = new ProcessStartInfo();
StartInfoDelete.WindowStyle = ProcessWindowStyle.Hidden;
StartInfoDelete.FileName = "cmd.exe";
StartInfoDelete.Arguments = "/C C:\\Windows\\System32\\netsh http delete sslcert ipport=0.0.0.0:" + Port;
StartInfoDelete.Verb = "runas";
ProcessusDelete.StartInfo = StartInfoDelete;
ProcessusDelete.Start();
ProcessusDelete.WaitForExit(10000);
ProcessusDelete.Close();

Process ProcessusAdd = new Process();
ProcessStartInfo StartInfoAdd = new ProcessStartInfo();
StartInfoAdd.WindowStyle = ProcessWindowStyle.Hidden;
StartInfoAdd.FileName = "cmd.exe";
StartInfoAdd.Arguments = "/C C:\\Windows\\System32\\netsh http add sslcert ipport=0.0.0.0:" + Port + " certhash=" + HashCertificat + " appid={e2eaacd9-92e6-43cc-b51c-7a7887149607}";
StartInfoAdd.Verb = "runas";
ProcessusAdd.StartInfo = StartInfoAdd;
ProcessusAdd.Start();
ProcessusAdd.WaitForExit(10000);
ProcessusAdd.Close();

if (HttpListener.IsSupported) {
if (this.Listener == null) {
this.Listener = new HttpListener();
}
this.Listener.Prefixes.Add(URLEnvironment);
this.Listener.Start();

// It seems to be not effective
IAsyncResult Result = this.Listener.BeginGetContext(new AsyncCallback(this.ListenerCallback), this.Listener);
} else {
this.ErrorMessage = "The operating system is not up to date!";
return false;
}

} catch (Exception E) {
this.ErrorMessage = this.GetCompleteException(E);
using (FileStream fs = new FileStream(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Exception_OnStart.txt", FileMode.Create)) {
using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) {
sw.WriteLine(this.ErrorMessage);
}
}
}
}

protected override void OnStop(){

try {
this.ErrorMessage = "";

if (this.Listener != null) {
this.Listener.Close();
this.Listener = null;
}

} catch (Exception E) {
this.ErrorMessage = this.GetCompleteException(E);
using (FileStream fs = new FileStream(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Exception_OnStop.txt", FileMode.Create)) {
using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) {
sw.WriteLine(this.ErrorMessage);
}
}
}
}

private void ListenerCallback(IAsyncResult Result) {

try {
string SOAPRequest = "",
SOAPResponse = "";

this.ErrorMessage = "";

HttpListenerContext Context = this.Listener.EndGetContext(Result);
HttpListenerRequest Request = Context.Request;
HttpListenerResponse Response = Context.Response;

if (Request.HasEntityBody) {
Stream S = Request.InputStream;
StreamReader SR = new StreamReader(S, Encoding.UTF8);
SOAPRequest = SR.ReadToEnd();
SR.Close();
S.Close();

using (FileStream fsIn = new FileStream(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Trace_Request_" + DateTime.Now.ToString("s").Replace("/", "").Replace("-", "").Replace(":", "") + ".xml", FileMode.Create)) {
using (StreamWriter swIn = new StreamWriter(fsIn, Encoding.UTF8)) {
swIn.WriteLine(SOAPRequest);
}
}

//... {creation of SOAP response}

using (FileStream fsOut = new FileStream(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Trace_Response_" + DateTime.Now.ToString("s").Replace("/", "").Replace("-", "").Replace(":", "") + ".xml", FileMode.Create)) {
using (StreamWriter swOut = new StreamWriter(fsOut, Encoding.UTF8)) {
swOut.WriteLine(SOAPResponse);
}
}

byte[] byteSOAPResponse = UTF8Encoding.UTF8.GetBytes(SOAPResponse);

using (Stream StreamSender = Response.OutputStream) {
StreamSender.Write(byteSOAPResponse, 0, byteSOAPResponse.Length);
StreamSender.Close();
}

Response.Close();

Result = this.Listener.BeginGetContext(new AsyncCallback(this.ListenerCallback), this.Listener);
}

} catch (Exception E) {
this.ErrorMessage = this.RecupereException(E);
using (FileStream fs = new FileStream(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Exception_ListenerCallback_" + DateTime.Now.ToString("s").Replace("/", "").Replace("-", "").Replace(":", "") + ".txt", FileMode.Create)) {
using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) {
swEntree.WriteLine("TryCatch : ");
swEntree.WriteLine(this.ErrorMessage);
}
}

return;
}
}

protected string RecupereException(Exception E) {
string M = "";
while (E != null) {
M += E.Message;
E = E.InnerException;
}

return M;
}
}


Le bout de code problématique se trouve dans la méthode « OnStart ». Elle ne génère aucune erreur, ni exception. C'est seulement que lorsque l'écoute débute, elle n'ouvre pas un « thread » en appelant la méthode « ListenerCallback ».


IAsyncResult Result = this.Listener.BeginGetContext(new AsyncCallback(this.ListenerCallback), this.Listener);


Ce code en C# fonctionne sans aucun problème depuis 3 ans en Windows Forms Application. Le code de la méthode « OnStart » est simplement écrit dans l'événement « OnClick » d'un bouton par exemple.

Tu peux le copier et l'essayer en quelques minutes. Si tu t'efforces de m'aider, tu as mon code en cadeau! :-)

J'ai aussi réalisé un code avec des événements « Wait », qui fonctionne en Windows Forms Application, mais non en Windows Service. Il réagit de façon identique. Il n'écoute pas (le petit garnement!).

Passe une belle journée mon ami! :-)
Messages postés
15565
Date d'inscription
lundi 9 juin 2008
Statut
Contributeur
Dernière intervention
10 mai 2021
849 >
Messages postés
4
Date d'inscription
vendredi 16 avril 2021
Statut
Membre
Dernière intervention
16 avril 2021

Que fait l'autre code qui ne fonctionne pas non plus comme service? Il fait aussi du SSL?
Tu fais l'hypothèse que tout se passe bien, et que le service n'écoute pas, ou ne récupère pas le message SOAP. Supposons plutôt qu'il ne le reçoive pas.

Je travaillerais ces pistes, pour récolter des information complémentaires:
1) le service a-t-il la même identité et les mêmes privilèges que l'application Forms?
2) tester avec http au lieu de https (l'app Form et le service)
3) quel client SOAP utilises-tu? ne te donne-t-il aucune possibilité de diagnostiquer ce qui se passe, où cela coince (TCPIP, SSL, HTTP, ...)?
4) il vaut mieux éviter de tester avec Telnet. à fortiori quand tu utilises SSL. si tu ne veux pas développer un client de test, il est simple d'utiliser PowerShell pour établir une connexion, avec ou sans SSL. Cela te donnera un meilleur contrôle de ton test. Comme tu fais de l'HTTP, utilise également un navigateur pour tester l'établissement d'une connexion et un échange HTTP, avec et sans SSL De nouveau, bien sûr, teste l'app Forms et le service.
5) tu as sans doute déjà vérifié via netsh http show sslcert
Messages postés
4
Date d'inscription
vendredi 16 avril 2021
Statut
Membre
Dernière intervention
16 avril 2021

En passant, naturellement le service est installé et démarré. Aussi, naturellement, je prends la version compilée en mode « Release » et non celle en « Debug ».

J'ai aussi testé cette version en Windows 7, 8 et 10. Ça fait le même effet!

C'est censé accepter un message SOAP, mais pour les besoins, n'importe quel chaîne de caractères serait très satisfaisante à cette étape-ci.

Afin de voir où ça bloque, j'ai écrit un fichier temporaire sur le disque. Naturellement, il ne dépasse pas le « BeginGetContext » en exécution. J'ai tenté de voir s'il entrait au moins une fois dans la méthode « ListenerCallback », en écrivant un autre fichier, mais jamais il ne l'écrit. Donc, il n'entre même pas à l'intérieur de cette dernière méthode.

Si tu veux écrire un fichier sur le disque, tu peux copier et coller le code suivant où tu voudras :


using (FileStream fs = new FileStream(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + "\\Fichier_Trace_" + DateTime.Now.ToString("s").Replace("/", "").Replace("-", "").Replace(":", "") + ".txt", FileMode.Create)) {
using (StreamWriter sw = new StreamWriter(fs, Encoding.UTF8)) {
swEntree.WriteLine("J'ai passé par là");
}
}