0

Per the comments, and my own testing, I am certain that my initial TCP port is not clearing out, and I don't really know how to remedy it.

I tried to run through the prefixes like so within my refresh method:

foreach (string item in myWebListener.Prefixes) {
    if (item.Contains("http://127.0.0.1:5000")) {
        myWebListener.Prefixes.Remove(item);
    }
}

This did not make any noticeable change in the behavior.

The only other thing that was popping up in the web logs was something about the favicon.ico file, and just maybe another browser thread is trapping my connection trying to retrieve this and was going to provide it as an async callback to finish things out, though I didn't provide it so this line of thinking makes sense to me (How to provide a favicon.ico file in my byte stream as an embedded resource is My next research project).

If this shows no changes, my next research project is trying to leverage TCPListener:

I ran across this Q/A, but it really didn't help me as I am not familiar with the TCP way of doing what I have done here, and the work-around looks similar to what I was already attempting in my original code.

There was a mention of a using statement in the linked Q/A, and my guess is that it would look something like:

using(TCPListener myTCPListener = new TCPListener()) {
    //Set the necessary TCP statements here.
    //Do what I already did with my existing code here.
}

That's as far as my brain can take me so far, does this sound far off based on the factors at play here?


BELOW WAS THE ORIGINAL QUESTION CONTENT:


I am successfully reaching my running C# HttpListener localhost server, and successfully sending information back out to the local requesting browser page.

But after the first time, I completely lock up the browser web page waiting for a new response.

I debug with no errors on VS, so I don't even know where to look next; Hence that's why I'm here.

My hope is, is that someone can shed some light on how to refresh my server internally, so when the new query is needed, I can respond to it just like it was the first time.

I'm using an http get method to call this, so the call would look similar to the following:

"http://127.0.0.1:5000/?param1=searchForThings&param2=itemToSearchFor";

I have to rip out a lot of proprietary code, but here is what is happening:

public class myWebServer {
    public myWebServer() {
            refresh();
            //The Global is being set When the API 
            //  Loads & is referenced here
            ev1 = _MyGlobalEvents.ev1
    }

    public static HttpListener myWebListener { get; set; }
    public static HttpListenerContext context { get; set; }
    public static AsyncCallback myGetCallback { get; set; }
    public static HttpResponseMessage myResp { get; set; }
    public static Stream output { get; set; }
    public static MyAPIDefinedEventType ev1 { get; set; }

    public static void refresh() {
        if (myWebListener != null) {
                myWebListener.Stop();
                myWebListener.Close();
        }
        if (output != null) { output.Dispose(); }
        if (myGetCallback != null) { myGetCallback = null; }
        myWebListener = new HttpListener();
        myGetCallback = new AsyncCallback(processRequest);
        myWebListener.Prefixes.Add("http://127.0.0.1:5000/");
        myWebListener.Start();
        myResp = new HttpResponseMessage(HttpStatusCode.Created);
        myWebListener.BeginGetContext(myGetCallback, myResp);
    }

    public static void processRequest(IAsyncResult result) {
        context = myWebListener.EndGetContext(result);
        context.Response.KeepAlive = true;
        myResp = new HttpResponseMessage(HttpStatusCode.Accepted);
        context.Response.StatusCode = (int)HttpStatusCode.Accepted;
        output = context.Response.OutputStream;
        string label = context.Request.QueryString["param1"];
        string fiStr = context.Request.QueryString["param2"];

        if (label != null || label != "" || label.Contains("\n") == false) {
            int pass1 = 0;
            int pass2 = 0;
            try {
                int myInt = label.ToCharArray().Length;
                pass1 = 1;
            } catch { }
            if (pass1 > 0) {
                try {
                    int myInt2 = fiStr.ToCharArray().Length;
                    pass2 = 1;
                } catch { }
            }
            if ((pass1 == 1 && pass2 == 0) || (pass1 == 1 && pass2 == 1)) {
                pass1 = 0;
                pass2 = 0;
                if (label == "searchForThings" && (fiStr != null && fiStr != "")) {
                    string respStr = "<html><body><div id=\"status_msg\" style=\"display:inline;\">" + fiStr + "</div></body></html>";
                    byte[] respBuffer = Encoding.ASCII.GetBytes(respStr);
                    context.Response.StatusCode = (int)HttpStatusCode.Accepted;
                    output.Write(respBuffer, 0, respBuffer.Length);
                    _MyGlobalEvents.searchStr = fiStr;
                    ev1.Raise();
                }
            }
        }
    }

    //When the Custom Event is done processing it runs something like the 
    //following to clean up and finalize
    public void _processSearch(string resultStr) { processSearch(resultStr); }
    public static void processSearch(string resultStr) {
        string respStr = "<html><head>" +
                        "<style type=\"text/css\">" +
                        "tr.dispRow{ display:table-row; }\n" +
                        "tr.noRow{ display:none; }" +
                        "</style>" +
                        "</head>" +
                        "<body>" +
                        resultStr +
                        "</body></html>";
        byte[] respBuffer = Encoding.ASCII.GetBytes(respStr);
        output.Flush();
        context.Response.StatusCode = (int)HttpStatusCode.OK;
        output.Write(respBuffer, 0, respBuffer.Length);
        output.Close();
        context.Response.KeepAlive = false;
        context.Response.Close();
        refresh();
    }
}

public static class _MyGlobalEvents {
    public static string searchStr { get; set; }
    public static MyAPIDefinedEventType ev1 { get; set; }
}
Rick Riggs
  • 336
  • 2
  • 12
  • i think you are doing things differently than What people usually do. Usually there is an endless Loop in Main waiting for requests and creates a thread for each requests (Limit Threads ) and let the thread handle the requests. Or make your Life easier all Check Out webapi https://learn.microsoft.com/en-us/aspnet/web-api/overview/getting-started-with-aspnet-web-api/tutorial-your-first-web-api – Mightee Feb 16 '18 at 20:14
  • @Mightee I am limited to an implementation of an API, and this code is actually part of an add-in. I could use all sorts of standard web servers, but I specifically need this to run and die with the software it is running with, so... this comment doesn't really help me. Also, HttpListener is a well known .NET module, its been around for quite a while, and knowledgeable people understand it [ref #1](https://www.codeproject.com/Articles/599978/An-HttpListener-Server-for-Handling-AJAX-POST-Requ), [ref #2](https://codehosting.net/blog/BlogEngine/post/Simple-C-Web-Server). – Rick Riggs Feb 16 '18 at 20:26
  • Wow you are really not nice. Anyways, as I mentioned in my comment earlier, If you want to handle multiple requests, you should make an endless Loop. Check this question: https://stackoverflow.com/questions/9034721/handling-multiple-requests-with-c-sharp-httplistener – Mightee Feb 16 '18 at 20:35
  • It seems like you're missing a `BeginGetContext` at the end of `processRequests`. Since you're only calling it once, the callback will only be triggered once. – fshauge Feb 16 '18 at 20:49
  • @Mightee, I hate to hear that my statement came across mean to you, as I was just really trying to state that in this context, it is not an approach I am quite willing to give up on yet. – Rick Riggs Feb 16 '18 at 21:19
  • @fshauge I only want to handle the request once per request. Also at the very last line of `processSearch()` I'm calling `refresh()` again, which in turn calls `BeginGetContext()` with the intention of effectively setting the original callback to handle any new request. So in other words, I'm failing to see the problem you may be seeing. Everything is seemingly happening OK on the first get request. – Rick Riggs Feb 16 '18 at 21:28
  • "But after the first time, I completely lock up the browser web page waiting for a new response" - are you saying the error is seen on the browser sending an Http request but getting no response ? I'd advise downloading and using Telerik Fiddler (free) to see whats actually being sent and received between the client and server. As other respondees have said - this is not a normal pattern for HttpListener and I'd guess you are having problems with TCP port caching on the server, and your .Stop() and .Close() methods are not freeing the TCP port (Windows caches it) – PhillipH Feb 16 '18 at 21:47
  • @PhillipH You hit the nail on the head, so my real problem is port caching!!! Now the real question becomes, how do you flush the cache using HttpListener. Thank you for that, I'll update my question appropriately. – Rick Riggs Feb 19 '18 at 19:09
  • I'll add it as an answer then. – PhillipH Feb 19 '18 at 19:50

2 Answers2

1

"But after the first time, I completely lock up the browser web page waiting for a new response" - are you saying the error is seen on the browser sending an Http request but getting no response ?

I'd advise downloading and using Telerik Fiddler (free) to see whats actually being sent and received between the client and server.

As other respondees have said - this is not a normal pattern for HttpListener and I'd guess you are having problems with TCP port caching on the server, and your .Stop() and .Close() methods are not freeing the TCP port (Windows caches it)

PhillipH
  • 6,182
  • 1
  • 15
  • 25
  • This answer was extremely helpful to me as it allowed me to see some problems; however it doesn't fully answer my original question. How do I actually refresh the port (unblock my server / kill and recycle my port / etc...). The API I'm running this in does not allow for more than one thread to be running, so I really have to deal with this blocking aspect of my question. – Rick Riggs Feb 19 '18 at 19:56
0

My particular problem was actually the favicon.ico file request from the browser locking up the connection.

Early on I was noticing that my server was being hit twice, and that is why I had my flags in place to make certain that I was only operating on what was the actual Get request based on the URL I was expecting...

Otherwise the URL was erroring as a blank string (turns out this was the favicon.ico request being made - irritating, because when the string is completely empty you have no idea how to troubleshoot it). So I was ignoring it and not touching what I didn't understand.

Well it turns I could NOT ignore this request, because the browser is expecting a response for it. All I had to do was put an else section in my code that executed if my flag options weren't met, and close, dispose and refresh the connection when this happened.

This Q/A really described the solidified solution to the problem fairly well, but my Exception.Message was blank which didn't really get addressed there either, so I'm glad I get to document it here in the hopes that anyone else who may ever run into the same problem finds the answer a little more strait forward.

As for the answer that PhillipH provided, this proved to be the most helpful to me, as it revealed to me that I needed to be checking what was happening from the network traffic eventing.

However I could not really use the tools that this user suggested, but found an alternative with Google Chrome's built in chrome://net-internals/. Just type that into your address bar in chrome and execute it.

Anyway way this was the total answer for me, but PhillipH you deserve it for moving me in the right direction.

Rick Riggs
  • 336
  • 2
  • 12