views:

2024

answers:

3

I'm trying to make a simple C# web server that, at this stage, you can access via your browser and will just do a "Hello World".

The problem I'm having is that the server can receive data fine - I get the browser's header information - but the browser doesn't receive anything I send. Furthermore, I can only connect to the server by going to localhost (or 127.0.0.1). I can't get to it by going to my IP and it's not a network setting because Apache works fine if I run that instead. Also, I'm using a port monitoring program and after I attempt a connection from a browser, the process's port gets stuck in a TIME_WAIT state even though I told the connection to close and it should be back to LISTEN.

Here's the relevant code. A couple calls might not make sense but this is a piece of a larger program.

class ConnectionHandler
{
 private Server server;

 private TcpListener tcp;

 private ArrayList connections;

 private bool listening;

 private Thread listeningThread;

 public Server getServer()
 {
  return server;
 }

 private void log(String s, bool e)
 {
  server.log("Connection Manager: " + s, e);
 }

 private void threadedListen()
 {
  while (listening)
  {
   try
   {
    TcpClient t = tcp.AcceptTcpClient();
    Connection conn = new Connection(this, t);
   }
   catch (NullReferenceException)
   {
    log("unable to accept connections!", true);
   }
  }
  log("Stopped listening", false);
 }

 public void listen()
 {
  log("Listening for new connections", false);
  tcp.Start();
  listening = true;
  if (listeningThread != null && listeningThread.IsAlive)
  {
   listeningThread.Abort();
  }
  listeningThread = new Thread(new ThreadStart(
   this.threadedListen));
  listeningThread.Start();
 }

 public void stop()
 {
  listening = false;
  if (listeningThread != null)
  {
   listeningThread.Abort();
   log("Forced stop", false);
  }
  log("Stopped listening", false);
 }

 public ConnectionHandler(Server server)
 {
  this.server = server;
  tcp = new TcpListener(new IPEndPoint(
   IPAddress.Parse("127.0.0.1"), 80));
  connections = new ArrayList();
 }
}

class Connection
{
 private Socket socket;
 private TcpClient tcp;

 private ConnectionHandler ch;

 public Connection(ConnectionHandler ch, TcpClient t)
 {
  try
  {
   this.ch = ch;
   this.tcp = t;
   ch.getServer().log("new tcp connection to " 
    + this.tcp.Client.RemoteEndPoint.ToString(), false);
   NetworkStream ns = t.GetStream();

   String responseString;
   Byte[] response;
   Int32 bytes;
   responseString = String.Empty;
   response = new Byte[512];
   bytes = ns.Read(response, 0, response.Length);
   responseString = 
    System.Text.Encoding.ASCII.GetString(response, 0, bytes);
   ch.getServer().log("Received: " + responseString);

   String msg = "<html>Hello World</html>";
   String fullMsg = "HTTP/1.x 200 OK\r\n"
     + "Server: Test Server\r\n"
     + "Content-Type: text/html; "
      + "charset=UTF-8\r\n"
     + "Content-Length: " + msg.Length + "\r\n"
     + "Date: Sun, 10 Aug 2008 22:59:59 GMT"
      + "\r\n";
   nsSend(fullMsg, ns);
   nsSend(msg, ns);

   ns.Close();
   tcp.Close();
  }
  catch (ArgumentNullException e)
  {
   ch.getServer().log("connection error: argument null exception: " + e);
  }
  catch (SocketException e)
  {
   ch.getServer().log("connection error: socket exception: " + e);
  }
 }

 private void nsSend(String s, NetworkStream ns)
 {
  Byte[] data = System.Text.Encoding.ASCII.GetBytes(s);
  ns.Write(data, 0, data.Length);
  ns.Flush();
  ch.getServer().log("Sent: " + s);
 }
}

Does anyone have any ideas? It feels like it's gotta be something stupid on my part but I just don't know what. I'd really appreciate any insight

+1  A: 

One thing which isn't a problem at the moment but might be later on is that your content length is based on the Unicode length of the message string, not the binary length.

There's also rather a lot going on in the Connection constructor - stuff that really doesn't belong in a constructor at all, IMO. It also doesn't close things if exceptions occur.

Have you looked at what's going on in a network tracer like WireShark? That would be the easiest way of seeing whether any data is getting sent back to the client.

Alternatively, post a short but complete version of your code which we can compile and run ourselves.

Jon Skeet
+1  A: 

You might like to know that you can use HttpListener to write a basic web-server very easily - this deals with most of the painful bits, letting you concentrate on writing the actual code. The MSDN page gives an example.

Note that this uses HTTP.SYS, which is good - but means that non-admins need to be explicitly given access to open ports; on xp you can do this with httpcfg; on vista you can use netsh. See here for more.

Marc Gravell
Well if you're going to take all the fun out of it... ;)
Jon Skeet
@Jon: bah, humbug ;-p
Marc Gravell
I'm still kind of curious what was wrong with my original code by HttpListener works great. I think rather than waste any more time, I'm just going to go with that for now.Thanks!
ZorroDeLaArena
... original code BUT HttpListener ...
ZorroDeLaArena
A: 

Maybe I'm just missing something but the reason you can only connect on localhost is because the IP you are listening on is 127.0.0.1, this makes the TCPListener only listen on that IP address. And I don't see anywhere you are calling any client disconnect, the TCPListener is a blocking call, so it sits there forever until a connection is made. In my past experience with TCP/IP and the TCPListener, TCPClient classes there is not much of a way to force the listener to drop it's connection until you drop the client connection. Calling TCPListener.Abort() doesn't drop the client connection which keeps the port blocked up.

Jeremy Reagan