I have created a function (ListenForJson) in which I am using HttpListener to get requests and send responses from/to a specific address. I am calling this function inside ActionResult Index() so that it can run in the background. However, since I am calling it in the Homepage of my Webapp, whenever I click on the "Go to Home" button I receive an error the error
Failed to listen on prefix because it conficts with an existing registration on the machine
I understand the reason it happens but I do not know how I can stop it from happening.
public ActionResult Index()
{
Thread thread = new Thread(() => ListenForJson());
thread.Start();
return View();
}
public void ListenForJson()
{
string[] prefixes = new string[] { "https://localhost:44337/" };
if (prefixes == null || prefixes.Length == 0)
throw new ArgumentException("prefixes");
HttpListener listener = new HttpListener();
foreach (string s in prefixes)
listener.Prefixes.Add(s);
listener.Start();
while (true)
{
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
//Get Body(Json)
System.IO.Stream body = request.InputStream;
System.Text.Encoding encoding = request.ContentEncoding;
System.IO.StreamReader reader = new System.IO.StreamReader(body, encoding);
string json = reader.ReadToEnd();
JsonObject = DeserializeJson(json);
HttpListenerResponse response = context.Response;
var responseJson = JsonSerialization("SERVICE");
byte[] buffer = new byte[] { };
response.ContentType = "Application/json";
buffer = Encoding.ASCII.GetBytes(responseJson);
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
context.Response.Close();
}
}
Related
I have two projects: client and server.
Server code:
static void Main()
{
var listener = new TcpListener(System.Net.IPAddress.Loopback, 13000);
listener.Start();
using (var client = listener.AcceptTcpClient())
{
var stream = client.GetStream();
var textStream = new StreamReader(stream);
while (true)
{
var line = textStream.ReadLine();
if (line == "") continue;
if (line == null) continue;
var clientIp = IPAddress.Parse(line.Split(':').First());
var clientPort = int.Parse(line.Split(':').Last());
var response = new LobbyConnectionResponse(StatusCode.OK, "Connected", new List<Player>(), new Leader("J", Guid.NewGuid()));
SendResponseToClient(clientIp, clientPort, response);
Console.WriteLine(line);
Thread.Sleep(lobbyResponseTimeout);
}
}
listener.Stop();
}
static Task SendResponseToClient(IPAddress ip, int port, LobbyConnectionResponse response)
{
var task = Task.Run(() =>
{
using (var tcpClient = new TcpClient())
{
tcpClient.Connect(ip, port);
var stream = tcpClient.GetStream();
var textStream = new StreamWriter(stream);
string jsonString = JsonSerializer.Serialize(response);
textStream.WriteAsync(jsonString);
stream.FlushAsync();
}
});
return task;
}
Client code:
static void Main()
{
StartListener();
using (var client = new TcpClient())
{
client.Connect("127.0.0.1", 13000);
var stream = client.GetStream();
var textStream = new StreamWriter(stream);
textStream.WriteLine("127.0.0.1:12000");
textStream.Flush();
}
while(true)
Thread.Sleep(1000);
Thread.Sleep(1000);
}
static Task StartListener()
{
var task = Task.Run(() =>
{
var listener = new TcpListener(System.Net.IPAddress.Loopback, 12000);
listener.Start();
while (true)
{
using (var client = listener.AcceptTcpClient())
{
var stream = client.GetStream();
var textStream = new StreamReader(stream);
while (true)
{
if (stream.DataAvailable)
Console.WriteLine("hfdsgdf");
var line = textStream.ReadLine();
if (line == null || line == "") continue;
var response = (LobbyConnectionResponse)JsonSerializer.Deserialize(stream, typeof(LobbyConnectionResponse));
Console.WriteLine(response.Description);
Thread.Sleep(100);
}
}
Thread.Sleep(100);
}
listener.Stop();
});
return task;
}
On the server's console, I had message:"127.0.0.1:12000" but client's console are empty, why client doesn't receive my text data? 0 Exceptions, clients code just looping infinity and every iteration continues because line == null
When I tried to make that on a single port ( server and client), nothing worked. I want that client get my JSON and deserialize it into class instance.
You need to send data through client/stream variable instead of creating a new connection from server to client.
var listener = new TcpListener(System.Net.IPAddress.Loopback, 13000);
listener.Start();
using (var client = listener.AcceptTcpClient())
{
var stream = client.GetStream();
// send response to client
byte[] buffer = Encoding.UTF8.GetBytes("foo");
stream.Write(buffer, 0, buffer.Length);
stream.Close();
}
I have a .net c# client software that sends data like this:
using(WebClient client = new WebClient())
{
string serialisedData = "";
serialisedData = JsonConvert.SerializeObject(myData);
client.Credentials = new NetworkCredential(config.UserData.Username, config.UserData.Password);
byte[] responsebyte = client.UploadData(config.ServerAddress, System.Text.Encoding.UTF8.GetBytes(serialisedData));
}
In nodejs, I currently have this kind of https setup:
_server = https.createServer({
key: fs.readFileSync(`${__dirname}\\bin\\cert\\${_config.sslkey}`, "utf8"),
cert: fs.readFileSync(`${__dirname}\\bin\\cert\\${_config.sslcert}`, "utf8")
}, _listener);
_server.listen(_config.port, "0.0.0.0");
function _listener(req, res) {
let data = []
req.on('data', chunk => {
data.push(chunk)
})
req.on('end', () => {
JSON.parse(data)
})
}
Now my problem. I cant get any data of the req or res object. Both "on" functionts dont fire and I dont know what to do now. Whats the right way to do this in NodeJS? (c# Code cant be changed)
Edit:
Here is the c# Server Code that works. How can I translate that into nodejs?
HttpListener listener = new HttpListener();
listener.Prefixes.Add($"https://+:{Config.Port}/");
listener.AuthenticationSchemes = AuthenticationSchemes.Basic;
listener.Start();
for (; ; )
{
Console.WriteLine("Listening...");
IAsyncResult result = listener.BeginGetContext(new AsyncCallback(DoWork), listener);
result.AsyncWaitHandle.WaitOne();
result = null;
}
private void DoWork(IAsyncResult asyncResult)
{
HttpListener listener = (HttpListener)asyncResult.AsyncState;
HttpListenerContext context = listener.EndGetContext(asyncResult);
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
HttpListenerBasicIdentity identity = (HttpListenerBasicIdentity)context.User.Identity;
string data;
using (var reader = new StreamReader(request.InputStream, request.ContentEncoding))
{
data = reader.ReadToEnd();
}
....
More Code
....
string responseSerial = JsonConvert.SerializeObject(responseData);
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseSerial);
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
}
You lack res.end() somewhere to let node's server know that it should send response back. Before that you can write your actual response. Tested that on your exact C# code and a simplified version of the node server:
var http = require('http');
var _server = http.createServer(_listener);
_server.listen(1234);
console.log( 'started' );
function _listener(req, res) {
let data = []
req.on('data', chunk => {
data.push(chunk)
})
req.on('end', () => {
JSON.parse(data);
res.write('response')
res.end()
})
}
I've created a small app to get http/https responses:
public static void Listener1(string[] prefixes)
{
if (!HttpListener.IsSupported)
{
Console.WriteLine("Seu ambiente não suporta os recursos da classe HttpListener.");
return;
}
if (prefixes == null || prefixes.Length == 0)
throw new ArgumentException("prefixes");
HttpListener listener = new HttpListener();
foreach (string s in prefixes)
{
listener.Prefixes.Add(s);
}
listener.Start();
Console.WriteLine("Listening...");
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
HttpListenerResponse response = context.Response;
string responseString = "<HTML><BODY> Hello world </BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
listener.Stop();
}
And I'm using this prefixes:
string[] url = { "http://localhost:5324/", "https://localhost:5325/" };
When I type http://localhost:5324/ on Chrome, I get the right response, but when using https://localhost:5325/, nothing happens. Not even errors about certificates.
Description: I am modifying the ASP.NET Core Web API service (hosted in Windows Service) that supports resumable file uploads. This works fine and resumes file uploads in many failure conditions except one described below.
Problem: When the service is on ther other computer and the client is on mine and I unplug the cable on my computer, the client detects the absence of network while the service hangs on fileSection.FileStream.Read(). Sometimes the service detects the failure in 8 min, sometimes in 20, sometimes never.
I also noticed that after I unplug cable and stop the client, the service becomes stuck at Read() function and the file size is x KB, but when the service finally detects the exception some time later, it writes additional 4 KB to the file. This is weird because I turned off buffering and the buffer size is 2 KB.
Question: How to properly detect the absence of network on the service, or timeout properly, or cancel the request
The service code:
public static async Task<List<(Guid, string)>> StreamFileAsync(
this HttpRequest request, DeviceId deviceId, FileTransferInfo transferInfo)
{
var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType), DefaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var section = await reader.ReadNextSectionAsync(_cancellationToken);
if (section != null)
{
var fileSection = section.AsFileSection();
var targetPath = transferInfo.FileTempPath;
try
{
using (var outfile = new FileStream(transferInfo.FileTempPath, FileMode.Append, FileAccess.Write, FileShare.None))
{
var buffer = new byte[DefaultCopyBufferSize];
int read;
while ((read = fileSection.FileStream.Read(buffer, 0, buffer.Length)) > 0) // HANGS HERE
{
outfile.Write(buffer, 0, read);
transferInfo.BytesSaved = read + transferInfo.BytesSaved;
}
}
}
catch (Exception e)
{
...
}
}
}
The client code:
var request = CreateRequest(fileTransferId, boundary, header, footer, filePath, offset, headers, null);
using (Stream formDataStream = request.GetRequestStream())
{
formDataStream.ReadTimeout = 60000;
formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, header.Length);
byte[] buffer = new byte[2048];
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
fs.Seek(offset, SeekOrigin.Begin);
for (int i = 0; i < fs.Length - offset;)
{
int k = await fs.ReadAsync(buffer, 0, buffer.Length);
if (k > 0)
{
await Task.Delay(100);
await formDataStream.WriteAsync(buffer, 0, k);
}
i = i + k;
}
}
formDataStream.Write(footer, 0, footer.Length);
}
var uploadingResult = request.GetResponse() as HttpWebResponse;
private static HttpWebRequest CreateRequest(
Guid fileTransferId,
string boundary,
string header,
byte[] footer,
string filePath,
long offset,
NameValueCollection headers,
Dictionary<string, string> postParameters)
{
var url = $"{_BaseAddress}v1/ResumableUpload?fileTransferId={fileTransferId}";
HttpWebRequest request = (HttpWebRequest) WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "multipart/form-data; boundary=\"" + boundary + "\"";
request.UserAgent = "Agent 1.0";
request.Headers.Add(headers); // custom headers
request.Timeout = 120000;
request.KeepAlive = true;
request.AllowReadStreamBuffering = false;
request.ReadWriteTimeout = 120000;
request.AllowWriteStreamBuffering = false;
request.ContentLength = CalculateContentLength(filePath, offset, header, footer, postParameters, boundary);
return request;
}
What I tried:
I added these in into config files:
Tried to set timeout on the server
var host = new WebHostBuilder().UseKestrel(o => { o.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);})
Used async and non-async Read()
Tried with keep alive and without
Tried to abort the request when network was restored: request?.Abort();
Tried to set formDataStream.ReadTimeout = 60000;
Since I did not find a better way, I decided to add a timeout to the reading stream and saving it to the file. The good example was posted here: https://blogs.msdn.microsoft.com/pfxteam/2012/10/05/how-do-i-cancel-non-cancelable-async-operations/
public static async Task<List<(Guid, string)>> StreamFileAsync(this HttpRequest request, DeviceId deviceId, FileTransferInfo transferInfo)
{
var boundary = GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType), DefaultFormOptions.MultipartBoundaryLengthLimit);
var reader = new MultipartReader(boundary, request.Body);
var section = await reader.ReadNextSectionAsync(_cancellationToken);
if (section != null)
{
var fileSection = section.AsFileSection();
var targetPath = transferInfo.FileTempPath;
try
{
await SaveMyFile(...);
}
catch (OperationCanceledException){...}
catch (Exception){...}
}
}
private static async Task SaveMyFile(...)
{
var cts = CancellationTokenSource.CreateLinkedTokenSource(myOtherCancellationToken);
cts.CancelAfter(streamReadTimeoutInMs);
var myReadTask = StreamFile(transferInfo, fileSection, cts.Token);
await ExecuteMyTaskWithCancellation(myReadTask, cts.Token);
}
private static async Task<T> ExecuteMyTaskWithCancellation<T>(Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(s => ((TaskCompletionSource<bool>) s).TrySetResult(true), tcs))
{
if (task != await Task.WhenAny(task, tcs.Task))
{
throw new OperationCanceledException(cancellationToken);
}
}
return await task;
}
private static async Task<bool> StreamFile(...)
{
using (var outfile = new FileStream(transferInfo.FileTempPath, FileMode.Append, FileAccess.Write, FileShare.None))
{
var buffer = new byte[DefaultCopyBufferSize];
int read;
while ((read = await fileSection.FileStream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
{
if (token.IsCancellationRequested)
{
break;
}
await outfile.WriteAsync(buffer, 0, read);
transferInfo.BytesSaved = read + transferInfo.BytesSaved;
}
return true;
}
}
I am creating a simple HTTP client/server application on my local machine but I don't know why the ListenerCallback is triggered on the server; however, EndGetContext is not completing while throwing 'Web Exception: Unable to connect to remove server" on the client side. Any ideas? here's the code
class Server
{
static void Main(string[] args)
{
NonblockingListener(new string[] {"http://192.168.0.55:5432/"});
}
public static void NonblockingListener(string[] prefixes)
{
HttpListener listener = new HttpListener();
foreach (string s in prefixes)
{
listener.Prefixes.Add(s);
}
listener.Start();
IAsyncResult result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
Console.WriteLine("Waiting for request to be processed asyncronously.");
result.AsyncWaitHandle.WaitOne();
Console.WriteLine("Request processed asyncronously.");
listener.Close();
}
public static void ListenerCallback(IAsyncResult result)
{
HttpListener listener = (HttpListener)result.AsyncState;
// Call EndGetContext to complete the asynchronous operation.
HttpListenerContext context = listener.EndGetContext(result);
HttpListenerRequest request = context.Request;
Stream reader = request.InputStream;
HttpListenerResponse response = context.Response;
string responseString = "<HTML><BODY> Hello World!</BODY></HTML>";
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
System.IO.Stream output = response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
}
}
class Client
{
public static void Main()
{
// Create a request using a URL that can receive a post.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://192.168.0.55:5432");
request.UserAgent = "linkToShare - HTTPWebRequest";
request.Method = "POST";
// Create POST data and convert it to a byte array.
string postData = "data data data data.";
byte[] byteArray = Encoding.UTF8.GetBytes(postData);
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = byteArray.Length;
Stream dataStream = request.GetRequestStream();
dataStream.Write(byteArray, 0, byteArray.Length);
dataStream.Close();
WebResponse response = request.GetResponse();
Console.WriteLine(((HttpWebResponse)response).StatusDescription);
dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
string responseFromServer = reader.ReadToEnd();
Console.WriteLine(responseFromServer);
reader.Close();
dataStream.Close();
response.Close();
}
}
The problem with your code is that in the server you are calling EndGetContext method which will set the WaitHandle and immediately close the server before it had any time to send the response.
Here's a slight modification of your code.
Server:
class Program
{
private static ManualResetEvent _waitHandle = new ManualResetEvent(false);
static void Main()
{
NonblockingListener(new string[] { "http://+:5432/" });
}
public static void NonblockingListener(string[] prefixes)
{
using (var listener = new HttpListener())
{
foreach (string s in prefixes)
{
listener.Prefixes.Add(s);
}
listener.Start();
var result = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
Console.WriteLine("Waiting for request to be processed asyncronously.");
// Block here until the handle is Set in the callback
_waitHandle.WaitOne();
Console.WriteLine("Request processed asyncronously.");
listener.Close();
}
}
public static void ListenerCallback(IAsyncResult result)
{
var listener = (HttpListener)result.AsyncState;
var context = listener.EndGetContext(result);
var response = context.Response;
string responseString = "<HTML><BODY>Hello World!</BODY></HTML>";
byte[] buffer = Encoding.UTF8.GetBytes(responseString);
response.ContentLength64 = buffer.Length;
response.OutputStream.Write(buffer, 0, buffer.Length);
// Finished sending the response, now set the wait handle
_waitHandle.Set();
}
}
Client:
class Program
{
static void Main(string[] args)
{
using (var client = new WebClient())
{
client.Headers[HttpRequestHeader.UserAgent] = "linkToShare - HTTPWebRequest";
var valuesToPost = new NameValueCollection
{
{ "param1", "value1" },
{ "param2", "value2" },
};
var result = client.UploadValues("http://127.0.0.1:5432", valuesToPost);
Console.WriteLine(Encoding.UTF8.GetString(result));
}
}
}