Reputation: 101
I try to implement multithreaded server. Few clients (< 10) will send some data to the server (PUT
request). On the GET
server has to send collection with this data. Here is my server code:
class Server
{
private HttpListener httpListener;
private static System.Threading.AutoResetEvent listenForNextRequest = new System.Threading.AutoResetEvent(false);
public static BlockingCollection<NodeInfo> locations = new BlockingCollection<NodeInfo>();
public Server()
{
const string ip = "127.0.0.1";
const int port = 9999;
string prefix = String.Format("http://{0}:{1}/", ip, port.ToString());
httpListener = new HttpListener();
httpListener.Prefixes.Clear();
httpListener.Prefixes.Add(prefix);
Console.WriteLine("HTTP server is running ...");
Console.WriteLine("Listening on {0} ...\n", prefix);
}
public void Start()
{
httpListener.Start();
ThreadPool.SetMaxThreads(10, 10);
ThreadPool.QueueUserWorkItem(new WaitCallback(Listen));
Thread.Sleep(7000);
}
internal void Stop()
{
httpListener.Stop();
}
private void Listen(object state)
{
while (httpListener.IsListening)
{
httpListener.BeginGetContext(new AsyncCallback(ListenerCallback), httpListener);
listenForNextRequest.WaitOne();
}
}
private static void ListenerCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
// Request
HttpListenerContext context = listener.EndGetContext(result);
if (context.Request.HttpMethod.Equals("PUT"))
{
PutRequestProcess(context);
}
if (context.Request.HttpMethod.Equals("GET"))
{
GetRequestProcess(context);
}
// Process next request
result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
}
private static void PutRequestProcess(HttpListenerContext context)
{
// Request object
HttpListenerRequest request = context.Request;
using (Stream stream = request.InputStream)
{
// Deserialize
DataContractSerializer ser = new DataContractSerializer(typeof(NodeInfo));
XmlDictionaryReader reader = XmlDictionaryReader.CreateTextReader(stream, new XmlDictionaryReaderQuotas());
// Deserialize the data and read it from the instance.
NodeInfo nodeInfo = (NodeInfo)ser.ReadObject(reader, true);
reader.Close();
stream.Close();
locations.Add(nodeInfo);
}
// Response object
HttpListenerResponse response = context.Response;
response.StatusCode = (int)HttpStatusCode.OK;
using (Stream stream = response.OutputStream)
{
stream.Close();
}
}
private static void GetRequestProcess(HttpListenerContext context)
{
// Response object
HttpListenerResponse response = context.Response;
response.StatusCode = (int)HttpStatusCode.OK;
using (Stream stream = response.OutputStream)
using (XmlDictionaryWriter writer = XmlDictionaryWriter.CreateTextWriter(stream))
{
DataContractSerializer ser = new DataContractSerializer(typeof(List<NodeInfo>));
{
writer.WriteStartDocument();
ser.WriteObject(writer, locations.ToList());
}
stream.Close();
}
}
}
PUT
request seems to be working. But when I try to receive this collection (GET
request) I have an exception at deserialization: Unexpected end of file...
In debugger there are not all tags in the end of XML
string. I think writing thread (server) doesn't finish. How can I fix it?
Also I have few questions. Is this implementation thread-safe? If I use concurrent collections I don't need to use semaphore or other synchronization?
Thank you very much!
Upvotes: 1
Views: 354
Reputation: 70701
It looks like you are closing the output stream prematurely. In GetRequestProcess()
, just remove that call to stream.Close()
. That will let the writer flush to the stream when it's closed. You don't need to explicitly close anything as long as you are correctly disposing objects (as you appear to be here).
As for the rest of your questions, I recommend you post a different question, without all the I/O code to confuse things, if you are having specific problems getting thread-safety to work. If you just want someone to review your code, use the Code Review site on Stack Exchange.
Upvotes: 1