HttpListener handling multiple requests [duplicate] - c#

This question already has answers here:
Handling multiple requests with C# HttpListener
(3 answers)
Closed 8 years ago.
I have this HttpListener, which works perfect for a single requesst, but then it shuts down after finishing up the request. What I'm interested in, is a listener that keeps up the connection with the client until there's no more files in the specified URL. I've tried fiddling around with threads and asynchronous calls, but I haven't been able to make anything of it working thus far. I just have a hard time imagining there isn't some relatively easy way to get a HttpListener to keep up the connection instead of shutting down after completing each request.
public static void Listener(string[] prefixes)
{
if (!HttpListener.IsSupported)
{
Console.WriteLine("Windows XP SP2 or Server 2003 is required to use the HttpListener class.");
return;
}
// URI prefixes are required,
// for example "http://contoso.com:8080/index/".
if (prefixes == null || prefixes.Length == 0)
throw new ArgumentException("prefixes");
// Create a listener.
HttpListener listener = new HttpListener();
// Add the prefixes.
foreach (string s in prefixes)
{
listener.Prefixes.Add("http://" + s + "/");
}
listener.Start();
Console.WriteLine("\nListening...");
HttpListenerContext context = listener.GetContext();
Console.WriteLine("Request received...\n");
HttpListenerRequest request = context.Request;
// Obtain a response object.
string url = context.Request.RawUrl;
string[] split = url.Split('/');
int lastIndex = split.Length - 1;
int x, y, z;
x = Convert.ToInt32(split[lastIndex]);
y = Convert.ToInt32(split[lastIndex - 1]);
z = Convert.ToInt32(split[lastIndex - 2]);
HttpListenerResponse response = context.Response;
#region Load image and respond
// Load the image
Bitmap bm = new Bitmap("C:\\MyFolder\\image_1\\");
MemoryStream bmStream = new MemoryStream();
bm.Save(bmStream, ImageFormat.Png);
byte[] buffer = bmStream.ToArray();
// Get a response stream and write the response to it.
response.ContentLength64 = bmStream.Length;
response.ContentType = "image/png";
response.KeepAlive = true;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
// You must close the output stream.
output.Close();
listener.Stop();
#endregion
And here's the Program:
class Program
{
static void Main(string[] args)
{
string name = (args.Length < 1) ? Dns.GetHostName() : args[0];
try
{ //Find the IPv4 address
IPAddress[] addrs = Array.FindAll(Dns.GetHostEntry(string.Empty).AddressList,
a => a.AddressFamily == AddressFamily.InterNetwork);
Console.WriteLine("Your IP address is: ");
foreach (IPAddress addr in addrs)
Console.WriteLine("{0} {1}", name, addr);
//Automatically set the IP address
string[] ips = addrs.Select(ip => ip.ToString()).ToArray();
Response.Listener(ips);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
//Manually setting the IP - not optimal!
//string[] ipstring = new string[1] { "10.10.180.11:8080" };
//Response.Listener(ipstring);
}
}

Yes - you're calling GetContext once, serving that request, then stopping.
Instead, you should be calling GetContext in a loop. Depending on whether you want to be able to handle multiple requests concurrently or not, you might have GetContext in one thread, and then hand off each request to a separate (probably thread-pool) thread to respond to it.
The slightly tricky bit is shutting down - if you want a clean shutdown, you'll need to work out when to stop the loop (and what to do if you're in the middle of a GetContext call), and wait for outstanding requests to complete.

It stops listening after processing one request because you just stop listening. You need to implement something like a waiting loop.
Example which should help you can be found on codeproject - example.
Pay attention to this part of the code and how it is used in the example:
private void startlistener(object s)
{
while (true)
{
////blocks until a client has connected to the server
ProcessRequest();
}
}

Related

How to put vertical line to http GET request without URI encoding

I'm creating an C# app which have to send http GET requests with vertical line symbol inside URI query part and that is impossible due to url encoding by URI class. So in the final url is "%7C" instead of "|" and server app cannot handle it.
The main problem is, that the server side app is developed by our external supplier and I have no access to it's source code and they cannot edit it for some reason, so I really need to do it somehow inside my client app.
I've tryed a lot, but cannot find any solution on the internet. But I have two work-arounds that can almost do what I want:
1) Using sockets
It's works perfectly, but... when I use it asynchoniously, server app seems to cannot handle more sockets at one time, and always run only once, with same response to each socket GET request. Client sockets has same host (my local PC) but run on different local PC port.
2) Using WebBrowser control with Navigate method
Even this solution works almost fine, but I realy don't want to use WebBrowser for every GET request.
Socket GET caller method
private static async Task<string> HttpGetRequestAsync(string url, int timeOut)
{
return await Task.Run(() =>
{
timeOut += Environment.TickCount;
Uri uri = new Uri(url);
using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
socket.Connect(uri.Host, uri.Port);
string request = string.Format(
"GET {0} HTTP/1.1{2}" +
"Host: {1}{2}" +
"Content-Length: 0{2}" +
"{2}", Uri.UnescapeDataString(uri.PathAndQuery), uri.Host, "\r\n");
socket.Send(Encoding.UTF8.GetBytes(request));
StringBuilder response = new StringBuilder();
byte[] responseData = new byte[BUFFER_SIZE];
int bytesRead = socket.Receive(responseData);
while (bytesRead != 0)
{
if (timeOut < Environment.TickCount) { throw new TimeoutException("TimeOut exception...!"); }
response.Append(Encoding.UTF8.GetChars(responseData), 0, bytesRead);
bytesRead = socket.Receive(responseData);
}
socket.Shutdown(SocketShutdown.Both);
socket.Close();
return response.ToString();
}
});
}
So I really need to find a way, how to send GET request in this format:
http://server:port/sms/send_sms.php?value1|value2
instead of this:
http://server:port/sms/send_sms.php?value1%7Cvalue2

How to write to MemoryStream if many messages are sent

Here is how i read data from my stream now:
public List<ServerClient> clients = new List<ServerClient>();
while (true)
{
Update();
}
private void Update()
{
//Console.WriteLine("Call");
if (!serverStarted)
{
return;
}
foreach (ServerClient c in clients.ToList())
{
// Is the client still connected?
if (!IsConnected(c.tcp))
{
c.tcp.Close();
disconnectList.Add(c);
Console.WriteLine(c.connectionId + " has disconnected.");
CharacterLogout(c.connectionId);
continue;
//Console.WriteLine("Check for connection?\n");
}
else
{
// Check for message from Client.
NetworkStream s = c.tcp.GetStream();
if (s.DataAvailable)
{
string data = c.streamReader.ReadLine();
if (data != null)
{
OnIncomingData(c, data);
}
}
//continue;
}
}
for (int i = 0; i < disconnectList.Count - 1; i++)
{
clients.Remove(disconnectList[i]);
disconnectList.RemoveAt(i);
}
}
When data is read it is send to OnIncomingData function which is processing the data. I don't have problems there.
Here is how i send data to the stream:
public void Send(string header, Dictionary data)
{
if (stream.CanRead)
{
socketReady = true;
}
if (!socketReady)
{
return;
}
JsonData SendData = new JsonData();
SendData.header = "1x" + header;
foreach (var item in data)
{
SendData.data.Add(item.Key.ToString(), item.Value.ToString());
}
SendData.connectionId = connectionId;
string json = JsonConvert.SerializeObject(SendData);
var howManyBytes = json.Length * sizeof(Char);
writer.WriteLine(json);
writer.Flush();
Debug.Log("Client World:" + json);
}
Here is my:
public class ServerClient
{
public TcpClient tcp;
public int accountId;
public StreamReader streamReader;
public int connectionId;
public ServerClient(TcpClient clientSocket)
{
tcp = clientSocket;
}
}
Here is my OnConnection function:
private void OnConnection(IAsyncResult ar)
{
connectionIncrementor++;
TcpListener listener = (TcpListener)ar.AsyncState;
NetworkStream s = clients[clients.Count - 1].tcp.GetStream();
clients.Add(new ServerClient(listener.EndAcceptTcpClient(ar)));
clients[clients.Count - 1].connectionId = connectionIncrementor;
clients[clients.Count - 1].streamReader = new StreamReader(s, true);
StartListening();
//Send a message to everyone, say someone has connected!
Dictionary<string, string> SendDataBroadcast = new Dictionary<string, string>();
SendDataBroadcast.Add("connectionId", clients[clients.Count - 1].connectionId.ToString());
Broadcast("001", SendDataBroadcast, clients, clients[clients.Count - 1].connectionId);
Console.WriteLine(clients[clients.Count - 1].connectionId + " has connected.");
}
Normally everything works fine. However if i try to send more request per 1 second the problem occurs. The message received is not full and complete. It just receives a portion of the message sent.
From Debug.Log("Client World:" + json); i can see that the message is full and complete but on the server i see that it is not.
This is not happening if i send less requests.
So for that reason i think i should create a MemoryStream and puts a message there and read it after. However i'm really not sure how i can do that. Can you help ?
The whole code is not very good, but I'll concentrate on your specific problem. It's most likely related to data buffering by StreamReader. StreamReader has buffer size (which you can pass to constructor) which defaults to 1024 bytes. When you call ReadLine - it's perfectly possible for stream reader to read more than one line from the underlying stream. In your case - you have while loop in which you enumerate connected clients and in every iteration of the loop you create new StreamReader and read one line from it. When message rate is low - all looks fine, because between your loop iterations only one line arrives. Now suppose client quickly sent 2 json messages, each of which is 800 bytes, and they both arrived into your socket. Now you call StreamReader.ReadLine. Because buffer size is 1024 - it will read 1024 bytes from socket (NetworkStream) and return first 800 to you (as a line). You process that line and discard StreamReader going to the next iteration of your while loop. By doing that you also discard part of the message (224 bytes of the next message), because they were already read from the socket into StreamReader buffer. I think from that it should be clear how to solve that problem - don't create new StreamReader every time but create one per client (for example store as a member of ServerClient) and use that.
The client looks more suspicious to me than the server.
StreamWriter is not thread-safe. Are you calling it in a thread-safe manner when using ClientWorldServer.Send? Lock up or queue your calls to ClientWorldServer.Send using a lock or BlockingCollection or some other synchronisation primitive. There is also a thread-safe wrapper of streamwriter you might be able to use.

How to quickly ping all ip addresses in a network? [duplicate]

This question already has answers here:
find all ip address in a network
(5 answers)
Closed 8 years ago.
So I am making a network scanner in c# to show all connected devices to the same network as you. The way I am doing this is by doing a ping command on all IP's from 192.168.0.1 to 192.168.0.255.
private void IPlook_Tick(object sender, EventArgs e)
{
Properties.Settings.Default.nextIP += 1;
if (Properties.Settings.Default.nextIP >= 255)
{
IPlook.Stop();
}
string IP = "192.168.0." + Properties.Settings.Default.nextIP.ToString();
CurIP.Text = IP;
//See if online
try
{
Ping ping = new Ping();
PingReply pingreply = ping.Send(IP);
if (pingreply.Status == IPStatus.Success)
{
string Hostname = Dns.GetHostByAddress(IP).HostName;
dataGridView1.Rows.Add(Hostname, IP, "");
}
else
{
}
}
catch (Exception er)
{
MessageBox.Show("Something Went Wrong", "Error Alert", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
This is working fine but the problem is it is taking a very long time and making the program laggy. I have set the interval on the timer to 50.
Any help is appreciated.
Legitimate
How about firing them all off at once (from an async method):
IPAddress start = IPAddress.Parse("192.168.1.1");
var bytes = start.GetAddressBytes();
var leastSigByte= start.GetAddressBytes().Last();
var range= 255 - leastSigByte;
var pingReplyTasks = Enumerable.Range(leastSigByte,range)
.Select(x=>{
var p = new Ping();
var bb = start.GetAddressBytes();
bb[3] = (byte)x;
var destIp = new IPAddress(bb);
var pingResultTask = p.SendPingAsync(destIp);
return new{pingResultTask, addr = destIp};
})
.ToList();
await Task.WhenAll(pingReplyTasks.Select(x=>x.pingResultTask));
foreach(var pr in pingReplyTasks)
{
var tsk = pr.pingResultTask;
var pingResult = tsk.Result; //we know these are completed tasks
var ip = pr.addr;
Console.WriteLine("{0} : {1}",ip,pingResult.Status);
}
You probably don't want to ping addresses ending in 0 or 255.
You are running the pings one after the other. This means that you must wait for the timeout to expire 255 times. Start multiple pings at the same time. Using await would make a lot of sense here.
Also, unblock the UI thread so that the UI is not frozen while this is in progress. Many techniques are available for doing that. await happens to solve this problem as well.
I don't see why you are using a timer. If there is no reason for that simply delete all timer stuff. Use a loop.
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.NetworkInformation;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
// Can build your list of IPs using a for loop
List<IPAddress> ips = new List<IPAddress>
{
new IPAddress(new byte[] {192, 168, 0, 1}),
new IPAddress(new byte[] {192, 168, 0, 2}),
// More ips in this list.
};
// Exactly what do you do with initiated tasks will depend on your specific scenario.
List<Task> tps = new List<Task>();
foreach(var ip in ips)
{
tps.Add(InitiatePing(ip));
}
// Needed so that console app doesn't exit..
Console.ReadLine();
}
private static async Task InitiatePing(IPAddress ip)
{
// Note, this API is different from SendAsync API you are using
// You may also want to reuse Ping instance instead of creating new one each time.
var result = await new Ping().SendPingAsync(ip);
// Process your result here, however you want.
Console.WriteLine(result.Address + "-" + result.Status + "-" + result.RoundtripTime);
}
}
}
Well if I'm reading your code correctly you're allowing the timer to fire 255 times and are pinging exactly one computer in each execution. I'm not sure why you're using a Settings class to store the current iteration number.
You could loop over 1 to 255 and launch each Ping request asynchronously using the Ping.SendAsync method. There is no need for a timer.
See also this question and answers/comments about broadcast pinging.

c# get post form data sent from browser by socket sometimes socket closes.why?

I have webserver receive data by async sockets:
var e = new SocketAsyncEventArgs();
e.Completed += new EventHandler<SocketAsyncEventArgs>(e_Completed);
while (true)
{ allDone.Reset();
mySocket.AcceptAsync(e);
allDone.WaitOne();
}
and the other method:
public void e_Completed(object sender, SocketAsyncEventArgs e)
{
var socket = (Socket)sender;
ThreadPool.QueueUserWorkItem(handleTcpRequest, e.AcceptSocket);
e.AcceptSocket = null;
socket.AcceptAsync(e);
}
this is the handleTcpRequest method.in this part I receive data from socket and do operation:
public void handleTcpRequest(object state)
{
string sBuffer = "";
string BufferTotal = "";
byte[] secureMessage;
Byte[] bReceive = new Byte[1024];
var mySocket = (Socket)state;
do
{
try
{
firstBufferRead = mySocket.Receive(bReceive, bReceive.Length, 0);
}
catch (Exception ex)
{
Console.WriteLine("Error Occurred (:))) " + ex.Message);
}
sBuffer += Encoding.GetEncoding(1252).GetString(bReceive, 0, firstBufferRead);
BufferTotal += Encoding.UTF8.GetString(bReceive, 0, firstBufferRead);
} while (mySocket.Available != 0);
.
.
.
.
mySocket.Close();
}
whats wrong?
sometimes connection resets and closes. this happens when distance is far or post data not multipart. but in multipart happens rarely. more with forms not in multipart.
when and where should I close socket?
when I work with socket in handleTcpRequest method its local. isn't it correct? I can't find the origin of the problem
The only way to know that you've received everything in a HTTP request is to understand the HTTP request. And to understand a HTTP request you have to choices:
Use a complete HTTP server
Create a HTTP parser
The reason to why your code fails for multi-part data is probably because the other party sends one part at a time, which means that your code manages to do a mySocket.Available != 0 before the rest is sent.
If you want to do the latter you have to read the HTTP header (in format headerName: headervalue, do note that there are also white space rules that you have to consider). Search for a header named content-length, parse it's value as an integer. Then wait for two line feeds in a row (\r\n\r\n). Finally start count bytes until you have received the number of bytes that was specified in the content-length header.
ohh.. ony more thing.. pray to God that Transfer-Encoding: Chunkedis not used.
My advice is that you give up on using sockets directly as it's apparent that you don't understand how they work or how to do research on them.
If response has a header of Connection: Close, then the socket closes automatically.

Weird behavior: TcpListener

So, for a bit of background :
This class is created to accept and respond to calls made remotely in an HTTP format.
The problem is when the method of the request is POST, sometimes the request is processed correctly, but most of the times the class just ends up being irresponsive.
Also, the line "Debug1" and "Debug2" are never written to the console, even when the request is processed correctly.
The line "Debug3" appears only when the request is processed correctly.
I know this will probably look messy, C# is only a hobby for me, and I'm learning :)
Thanks for spending some time to go through this code!
Here is the code:
class WebServer
{
private TcpListener myListener;
public WebServer(int port)
{
//Threading the listener
try
{
myListener = new TcpListener(IPAddress.Any, port) ;
myListener.Start();
Thread th = new Thread(new ThreadStart(StartListen));
th.Start() ;
}
catch(Exception e)
{
Logs.Add("WebServer|An Exception Occurred while Listening :" +e.ToString());
}
}
private void StartListen()
{
int iStartPos = 0;
string sHttpVersion;
string sResponse = "";
string sCode = " 200 OK";
while(true)
{
//Accept a new connection
Socket mySocket = myListener.AcceptSocket();
if(mySocket.Connected)
{
Byte[] bReceive = new Byte[1024];
int i = mySocket.Receive(bReceive,bReceive.Length,SocketFlags.None);
string sBuffer = Encoding.ASCII.GetString(bReceive).TrimEnd('\0');
iStartPos = sBuffer.IndexOf("HTTP",1);
sHttpVersion = sBuffer.Substring(iStartPos,8); //http version (ex: "HTTP/1.1")
if (sBuffer.StartsWith("GET / "))
{
Logs.Add("WebServer|Connected:" + mySocket.RemoteEndPoint.ToString());
sResponse = ArrayToJson();
}
else if (sBuffer.StartsWith("POST"))
{
Console.WriteLine("Debug1");
//This is a POST request, so more data is waiting to be retreived...
bReceive = new Byte[2048];
i = mySocket.Receive(bReceive,bReceive.Length,SocketFlags.None);
sBuffer = Encoding.ASCII.GetString(bReceive).TrimEnd('\0');
Console.WriteLine("Debug2");
//Parsing the request
string[] sParams = sBuffer.Split(',');
Console.WriteLine(sParams.Length);
Console.WriteLine("Debug3: {0} - {1} - {2} - {3} - {4}", sParams[0], sParams[1], sParams[2], sParams[3], sParams[4]);
//I do what needs to be done here
Logs.Add("WebServer|BotStartRequest:" + mySocket.RemoteEndPoint.ToString());
sResponse = "Accepted";
}
//Sending response and closing socket
SendHeader(sHttpVersion, "text/html", sResponse.Length, sCode, ref mySocket);
SendToBrowser(sResponse, ref mySocket);
mySocket.Close();
}
}
}
}
}
Implementing HTTP/1.1 is not a simple task. The basic protocol looks quite simple, but it's really hard to get even a minimal server implementation right: You have at least to think about persistent connections, in the case of POST of the Expect: 100-continue header, correctly parsing the header, and much more.
I strongly recommend you have a look at existing libraries/code. For example, the HttpListener class is built into the .NET Framework and probably already provides all you'll ever need.
If you really want to implement a server from scratch, have a look at Microsoft Cassini, a simple HTTP server written in C# licensed under Ms-PL.

Categories