Multiple connections with TcpClient, second connection always hangs/does nothing
So I have a TcpClient in a console app that is listening on port 9096. I want the client to be able to handle multiple connections (simultaneous or not). I also do not want to use Threads. I want to use async/await. I also need to be able to gracefully close the app during certain events, being careful not to lose any data. So I need a cancellation token. I have the code mostly working but there are two issues.
First, when the app starts listening and I send it data; everything works correctly as long as the sender is using the same initial connection to the app. Once a new connection (or socket I guess? not clear on the terminology) is established the app does not process the new data.
Second, when the terminate signal is given to the app and the token is cancelled the app does not close. I am not getting any exceptions and I cannot figure out what I an doing wrong.
I have looked all over and cannot find an example of a TcpClient that uses async/await with a cancellation token. I also cannot find an example that I have been able to get working that correct processes multiple connections, without using Threads or other complicated designs. I want the design as simple as possible with as little code as possible while still meeting my requirements. If using threads is the only way to do it I will, but I am soo close to getting it right I feel like I am just missing a little thing.
I am at my wits end trying to figure this out and have exhausted all my ideas.
EDIT: I moved the AcceptTcpClientAsync
into the loop as suggested below and it did not change anything. App functions the same as before.
Program.cs
class Program
{
private static List<Task> _listeners = new List<Task>();
private static readonly CancellationTokenSource cancelSource = new CancellationTokenSource();
static void Main(string args)
{
Console.TreatControlCAsInput = false;
Console.CancelKeyPress += (o, e) => {
Console.WriteLine("Shutting down.");
cancelSource.Cancel();
};
Console.WriteLine("Started, press ctrl + c to terminate.");
_listeners.Add(Listen(cancelSource.Token));
cancelSource.Token.WaitHandle.WaitOne();
Task.WaitAll(_listeners.ToArray(), cancelSource.Token);
}
}
Listen
public async Task Listen(CancellationToken token){
var listener = new TcpListener(IPAddress.Parse("0.0.0.0"), 9096);
listener.Start();
Console.WriteLine("Listening on port 9096");
while (!token.IsCancellationRequested) {
// Also tried putting AcceptTcpClientAsync here.
await Task.Run(async () => {
var client = await listener.AcceptTcpClientAsync();
using (var stream = client.GetStream())
using (var streamReader = new StreamReader(stream, Encoding.UTF8))
using (var streamWriter = new StreamWriter(stream, Encoding.UTF8)) {
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
vat data = await streamReader.ReadAsync();
await streamWriter.WriteLineAsync("Request received.");
}
}
});
}
Console.WriteLine("Stopped Accepting Requests.");
listener.Server.Close();
listener.Stop();
}
c# .net .net-core tcpclient tcplistener
add a comment |
So I have a TcpClient in a console app that is listening on port 9096. I want the client to be able to handle multiple connections (simultaneous or not). I also do not want to use Threads. I want to use async/await. I also need to be able to gracefully close the app during certain events, being careful not to lose any data. So I need a cancellation token. I have the code mostly working but there are two issues.
First, when the app starts listening and I send it data; everything works correctly as long as the sender is using the same initial connection to the app. Once a new connection (or socket I guess? not clear on the terminology) is established the app does not process the new data.
Second, when the terminate signal is given to the app and the token is cancelled the app does not close. I am not getting any exceptions and I cannot figure out what I an doing wrong.
I have looked all over and cannot find an example of a TcpClient that uses async/await with a cancellation token. I also cannot find an example that I have been able to get working that correct processes multiple connections, without using Threads or other complicated designs. I want the design as simple as possible with as little code as possible while still meeting my requirements. If using threads is the only way to do it I will, but I am soo close to getting it right I feel like I am just missing a little thing.
I am at my wits end trying to figure this out and have exhausted all my ideas.
EDIT: I moved the AcceptTcpClientAsync
into the loop as suggested below and it did not change anything. App functions the same as before.
Program.cs
class Program
{
private static List<Task> _listeners = new List<Task>();
private static readonly CancellationTokenSource cancelSource = new CancellationTokenSource();
static void Main(string args)
{
Console.TreatControlCAsInput = false;
Console.CancelKeyPress += (o, e) => {
Console.WriteLine("Shutting down.");
cancelSource.Cancel();
};
Console.WriteLine("Started, press ctrl + c to terminate.");
_listeners.Add(Listen(cancelSource.Token));
cancelSource.Token.WaitHandle.WaitOne();
Task.WaitAll(_listeners.ToArray(), cancelSource.Token);
}
}
Listen
public async Task Listen(CancellationToken token){
var listener = new TcpListener(IPAddress.Parse("0.0.0.0"), 9096);
listener.Start();
Console.WriteLine("Listening on port 9096");
while (!token.IsCancellationRequested) {
// Also tried putting AcceptTcpClientAsync here.
await Task.Run(async () => {
var client = await listener.AcceptTcpClientAsync();
using (var stream = client.GetStream())
using (var streamReader = new StreamReader(stream, Encoding.UTF8))
using (var streamWriter = new StreamWriter(stream, Encoding.UTF8)) {
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
vat data = await streamReader.ReadAsync();
await streamWriter.WriteLineAsync("Request received.");
}
}
});
}
Console.WriteLine("Stopped Accepting Requests.");
listener.Server.Close();
listener.Stop();
}
c# .net .net-core tcpclient tcplistener
you may have to use a background-worker thread
– JohnB
Nov 24 '18 at 1:26
add a comment |
So I have a TcpClient in a console app that is listening on port 9096. I want the client to be able to handle multiple connections (simultaneous or not). I also do not want to use Threads. I want to use async/await. I also need to be able to gracefully close the app during certain events, being careful not to lose any data. So I need a cancellation token. I have the code mostly working but there are two issues.
First, when the app starts listening and I send it data; everything works correctly as long as the sender is using the same initial connection to the app. Once a new connection (or socket I guess? not clear on the terminology) is established the app does not process the new data.
Second, when the terminate signal is given to the app and the token is cancelled the app does not close. I am not getting any exceptions and I cannot figure out what I an doing wrong.
I have looked all over and cannot find an example of a TcpClient that uses async/await with a cancellation token. I also cannot find an example that I have been able to get working that correct processes multiple connections, without using Threads or other complicated designs. I want the design as simple as possible with as little code as possible while still meeting my requirements. If using threads is the only way to do it I will, but I am soo close to getting it right I feel like I am just missing a little thing.
I am at my wits end trying to figure this out and have exhausted all my ideas.
EDIT: I moved the AcceptTcpClientAsync
into the loop as suggested below and it did not change anything. App functions the same as before.
Program.cs
class Program
{
private static List<Task> _listeners = new List<Task>();
private static readonly CancellationTokenSource cancelSource = new CancellationTokenSource();
static void Main(string args)
{
Console.TreatControlCAsInput = false;
Console.CancelKeyPress += (o, e) => {
Console.WriteLine("Shutting down.");
cancelSource.Cancel();
};
Console.WriteLine("Started, press ctrl + c to terminate.");
_listeners.Add(Listen(cancelSource.Token));
cancelSource.Token.WaitHandle.WaitOne();
Task.WaitAll(_listeners.ToArray(), cancelSource.Token);
}
}
Listen
public async Task Listen(CancellationToken token){
var listener = new TcpListener(IPAddress.Parse("0.0.0.0"), 9096);
listener.Start();
Console.WriteLine("Listening on port 9096");
while (!token.IsCancellationRequested) {
// Also tried putting AcceptTcpClientAsync here.
await Task.Run(async () => {
var client = await listener.AcceptTcpClientAsync();
using (var stream = client.GetStream())
using (var streamReader = new StreamReader(stream, Encoding.UTF8))
using (var streamWriter = new StreamWriter(stream, Encoding.UTF8)) {
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
vat data = await streamReader.ReadAsync();
await streamWriter.WriteLineAsync("Request received.");
}
}
});
}
Console.WriteLine("Stopped Accepting Requests.");
listener.Server.Close();
listener.Stop();
}
c# .net .net-core tcpclient tcplistener
So I have a TcpClient in a console app that is listening on port 9096. I want the client to be able to handle multiple connections (simultaneous or not). I also do not want to use Threads. I want to use async/await. I also need to be able to gracefully close the app during certain events, being careful not to lose any data. So I need a cancellation token. I have the code mostly working but there are two issues.
First, when the app starts listening and I send it data; everything works correctly as long as the sender is using the same initial connection to the app. Once a new connection (or socket I guess? not clear on the terminology) is established the app does not process the new data.
Second, when the terminate signal is given to the app and the token is cancelled the app does not close. I am not getting any exceptions and I cannot figure out what I an doing wrong.
I have looked all over and cannot find an example of a TcpClient that uses async/await with a cancellation token. I also cannot find an example that I have been able to get working that correct processes multiple connections, without using Threads or other complicated designs. I want the design as simple as possible with as little code as possible while still meeting my requirements. If using threads is the only way to do it I will, but I am soo close to getting it right I feel like I am just missing a little thing.
I am at my wits end trying to figure this out and have exhausted all my ideas.
EDIT: I moved the AcceptTcpClientAsync
into the loop as suggested below and it did not change anything. App functions the same as before.
Program.cs
class Program
{
private static List<Task> _listeners = new List<Task>();
private static readonly CancellationTokenSource cancelSource = new CancellationTokenSource();
static void Main(string args)
{
Console.TreatControlCAsInput = false;
Console.CancelKeyPress += (o, e) => {
Console.WriteLine("Shutting down.");
cancelSource.Cancel();
};
Console.WriteLine("Started, press ctrl + c to terminate.");
_listeners.Add(Listen(cancelSource.Token));
cancelSource.Token.WaitHandle.WaitOne();
Task.WaitAll(_listeners.ToArray(), cancelSource.Token);
}
}
Listen
public async Task Listen(CancellationToken token){
var listener = new TcpListener(IPAddress.Parse("0.0.0.0"), 9096);
listener.Start();
Console.WriteLine("Listening on port 9096");
while (!token.IsCancellationRequested) {
// Also tried putting AcceptTcpClientAsync here.
await Task.Run(async () => {
var client = await listener.AcceptTcpClientAsync();
using (var stream = client.GetStream())
using (var streamReader = new StreamReader(stream, Encoding.UTF8))
using (var streamWriter = new StreamWriter(stream, Encoding.UTF8)) {
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
vat data = await streamReader.ReadAsync();
await streamWriter.WriteLineAsync("Request received.");
}
}
});
}
Console.WriteLine("Stopped Accepting Requests.");
listener.Server.Close();
listener.Stop();
}
c# .net .net-core tcpclient tcplistener
c# .net .net-core tcpclient tcplistener
edited Nov 24 '18 at 2:29
Jason R
asked Nov 24 '18 at 1:24
Jason RJason R
11
11
you may have to use a background-worker thread
– JohnB
Nov 24 '18 at 1:26
add a comment |
you may have to use a background-worker thread
– JohnB
Nov 24 '18 at 1:26
you may have to use a background-worker thread
– JohnB
Nov 24 '18 at 1:26
you may have to use a background-worker thread
– JohnB
Nov 24 '18 at 1:26
add a comment |
1 Answer
1
active
oldest
votes
This is actually working the way you designed it, however you have only built to receive one connection. I am not going to write a full socket implementation for you (as this can get fairly in-depth). However, as for you main problem, you need to put the AcceptTcpClientAsync
in the loop otherwise you wont get any more connections
var cancellation = new CancellationTokenSource();
...
var listener = new TcpListener(...);
listener.Start();
try
{
while (!token.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync()
...
}
}
finally
{
listener.Stop();
}
// somewhere in another thread
cancellation.Cancel();
Update
I tried that and no behavior changed. Still does not pick up any
connection after the first.
await ...
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
Its obvious that AcceptTcpClientAsync
will never get called again because you are awaiting the task. This method is what accepts the client, if you cant call it, you dont get any more clients.
You cannot block here, which is what you are doing. Please see some socket server examples to get a better idea of how to write a listener
I tried that and no behavior changed. Still does not pick up any connection after the first.
– Jason R
Nov 24 '18 at 2:30
@JasonR updated
– TheGeneral
Nov 24 '18 at 2:48
@JasonR i missed the most import bit, you are awaiting the task, remove the await and it will work, with theAcceptTcpClientAsync
in the right place
– TheGeneral
Nov 24 '18 at 2:53
@JasonR here is another example gist.github.com/Maxwe11/cf8cc6331ad73671846e
– TheGeneral
Nov 24 '18 at 2:56
OMG! How simple. I cannot believe I missed that. It makes sense in hindsight and I understand everything. Just completely escaped me until you said it. Thanks.
– Jason R
Nov 24 '18 at 4:32
|
show 1 more comment
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53454397%2fmultiple-connections-with-tcpclient-second-connection-always-hangs-does-nothing%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
This is actually working the way you designed it, however you have only built to receive one connection. I am not going to write a full socket implementation for you (as this can get fairly in-depth). However, as for you main problem, you need to put the AcceptTcpClientAsync
in the loop otherwise you wont get any more connections
var cancellation = new CancellationTokenSource();
...
var listener = new TcpListener(...);
listener.Start();
try
{
while (!token.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync()
...
}
}
finally
{
listener.Stop();
}
// somewhere in another thread
cancellation.Cancel();
Update
I tried that and no behavior changed. Still does not pick up any
connection after the first.
await ...
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
Its obvious that AcceptTcpClientAsync
will never get called again because you are awaiting the task. This method is what accepts the client, if you cant call it, you dont get any more clients.
You cannot block here, which is what you are doing. Please see some socket server examples to get a better idea of how to write a listener
I tried that and no behavior changed. Still does not pick up any connection after the first.
– Jason R
Nov 24 '18 at 2:30
@JasonR updated
– TheGeneral
Nov 24 '18 at 2:48
@JasonR i missed the most import bit, you are awaiting the task, remove the await and it will work, with theAcceptTcpClientAsync
in the right place
– TheGeneral
Nov 24 '18 at 2:53
@JasonR here is another example gist.github.com/Maxwe11/cf8cc6331ad73671846e
– TheGeneral
Nov 24 '18 at 2:56
OMG! How simple. I cannot believe I missed that. It makes sense in hindsight and I understand everything. Just completely escaped me until you said it. Thanks.
– Jason R
Nov 24 '18 at 4:32
|
show 1 more comment
This is actually working the way you designed it, however you have only built to receive one connection. I am not going to write a full socket implementation for you (as this can get fairly in-depth). However, as for you main problem, you need to put the AcceptTcpClientAsync
in the loop otherwise you wont get any more connections
var cancellation = new CancellationTokenSource();
...
var listener = new TcpListener(...);
listener.Start();
try
{
while (!token.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync()
...
}
}
finally
{
listener.Stop();
}
// somewhere in another thread
cancellation.Cancel();
Update
I tried that and no behavior changed. Still does not pick up any
connection after the first.
await ...
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
Its obvious that AcceptTcpClientAsync
will never get called again because you are awaiting the task. This method is what accepts the client, if you cant call it, you dont get any more clients.
You cannot block here, which is what you are doing. Please see some socket server examples to get a better idea of how to write a listener
I tried that and no behavior changed. Still does not pick up any connection after the first.
– Jason R
Nov 24 '18 at 2:30
@JasonR updated
– TheGeneral
Nov 24 '18 at 2:48
@JasonR i missed the most import bit, you are awaiting the task, remove the await and it will work, with theAcceptTcpClientAsync
in the right place
– TheGeneral
Nov 24 '18 at 2:53
@JasonR here is another example gist.github.com/Maxwe11/cf8cc6331ad73671846e
– TheGeneral
Nov 24 '18 at 2:56
OMG! How simple. I cannot believe I missed that. It makes sense in hindsight and I understand everything. Just completely escaped me until you said it. Thanks.
– Jason R
Nov 24 '18 at 4:32
|
show 1 more comment
This is actually working the way you designed it, however you have only built to receive one connection. I am not going to write a full socket implementation for you (as this can get fairly in-depth). However, as for you main problem, you need to put the AcceptTcpClientAsync
in the loop otherwise you wont get any more connections
var cancellation = new CancellationTokenSource();
...
var listener = new TcpListener(...);
listener.Start();
try
{
while (!token.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync()
...
}
}
finally
{
listener.Stop();
}
// somewhere in another thread
cancellation.Cancel();
Update
I tried that and no behavior changed. Still does not pick up any
connection after the first.
await ...
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
Its obvious that AcceptTcpClientAsync
will never get called again because you are awaiting the task. This method is what accepts the client, if you cant call it, you dont get any more clients.
You cannot block here, which is what you are doing. Please see some socket server examples to get a better idea of how to write a listener
This is actually working the way you designed it, however you have only built to receive one connection. I am not going to write a full socket implementation for you (as this can get fairly in-depth). However, as for you main problem, you need to put the AcceptTcpClientAsync
in the loop otherwise you wont get any more connections
var cancellation = new CancellationTokenSource();
...
var listener = new TcpListener(...);
listener.Start();
try
{
while (!token.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync()
...
}
}
finally
{
listener.Stop();
}
// somewhere in another thread
cancellation.Cancel();
Update
I tried that and no behavior changed. Still does not pick up any
connection after the first.
await ...
while (!token.IsCancellationRequested) {
// DO WORK WITH DATA RECEIVED
Its obvious that AcceptTcpClientAsync
will never get called again because you are awaiting the task. This method is what accepts the client, if you cant call it, you dont get any more clients.
You cannot block here, which is what you are doing. Please see some socket server examples to get a better idea of how to write a listener
edited Nov 24 '18 at 2:54
answered Nov 24 '18 at 2:13
TheGeneralTheGeneral
28.2k63365
28.2k63365
I tried that and no behavior changed. Still does not pick up any connection after the first.
– Jason R
Nov 24 '18 at 2:30
@JasonR updated
– TheGeneral
Nov 24 '18 at 2:48
@JasonR i missed the most import bit, you are awaiting the task, remove the await and it will work, with theAcceptTcpClientAsync
in the right place
– TheGeneral
Nov 24 '18 at 2:53
@JasonR here is another example gist.github.com/Maxwe11/cf8cc6331ad73671846e
– TheGeneral
Nov 24 '18 at 2:56
OMG! How simple. I cannot believe I missed that. It makes sense in hindsight and I understand everything. Just completely escaped me until you said it. Thanks.
– Jason R
Nov 24 '18 at 4:32
|
show 1 more comment
I tried that and no behavior changed. Still does not pick up any connection after the first.
– Jason R
Nov 24 '18 at 2:30
@JasonR updated
– TheGeneral
Nov 24 '18 at 2:48
@JasonR i missed the most import bit, you are awaiting the task, remove the await and it will work, with theAcceptTcpClientAsync
in the right place
– TheGeneral
Nov 24 '18 at 2:53
@JasonR here is another example gist.github.com/Maxwe11/cf8cc6331ad73671846e
– TheGeneral
Nov 24 '18 at 2:56
OMG! How simple. I cannot believe I missed that. It makes sense in hindsight and I understand everything. Just completely escaped me until you said it. Thanks.
– Jason R
Nov 24 '18 at 4:32
I tried that and no behavior changed. Still does not pick up any connection after the first.
– Jason R
Nov 24 '18 at 2:30
I tried that and no behavior changed. Still does not pick up any connection after the first.
– Jason R
Nov 24 '18 at 2:30
@JasonR updated
– TheGeneral
Nov 24 '18 at 2:48
@JasonR updated
– TheGeneral
Nov 24 '18 at 2:48
@JasonR i missed the most import bit, you are awaiting the task, remove the await and it will work, with the
AcceptTcpClientAsync
in the right place– TheGeneral
Nov 24 '18 at 2:53
@JasonR i missed the most import bit, you are awaiting the task, remove the await and it will work, with the
AcceptTcpClientAsync
in the right place– TheGeneral
Nov 24 '18 at 2:53
@JasonR here is another example gist.github.com/Maxwe11/cf8cc6331ad73671846e
– TheGeneral
Nov 24 '18 at 2:56
@JasonR here is another example gist.github.com/Maxwe11/cf8cc6331ad73671846e
– TheGeneral
Nov 24 '18 at 2:56
OMG! How simple. I cannot believe I missed that. It makes sense in hindsight and I understand everything. Just completely escaped me until you said it. Thanks.
– Jason R
Nov 24 '18 at 4:32
OMG! How simple. I cannot believe I missed that. It makes sense in hindsight and I understand everything. Just completely escaped me until you said it. Thanks.
– Jason R
Nov 24 '18 at 4:32
|
show 1 more comment
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53454397%2fmultiple-connections-with-tcpclient-second-connection-always-hangs-does-nothing%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
you may have to use a background-worker thread
– JohnB
Nov 24 '18 at 1:26