I was following a tutorial to connect my bluetooth module to pc and read data from it, but in the tutorial he designed the program to run as a GUI, now I'm trying to edit it to run in console, which I will merge the new code in unity engine to move my game by the input data.
The GUI
used and the code,
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Threading;
using InTheHand;
using InTheHand.Net.Bluetooth;
using InTheHand.Net.Sockets;
using System.IO;
namespace Bluetooth
{
public partial class Form1 : Form
{
List<string> items;
public Form1()
{
items = new List<string>();
InitializeComponent();
}
private void bGo_Click(object sender, EventArgs e)
{
if(serverStarted)
{
updateUI("Server already started!");
return;
}
if(rbClient.Checked)
{
connectAsClient();
}
else
{
connectAsServer();
}
}
private void connectAsServer()
{
Thread bluetoothServerThread = new Thread(new ThreadStart(serverConnectThread));
bluetoothServerThread.Start();
}
Guid mUUID = new Guid("00001101-0000-1000-8000-00805F9B34FB");
bool serverStarted = false;
public void serverConnectThread()
{
serverStarted = true;
updateUI("Server started, waiting for clients");
BluetoothListener blueListener = new BluetoothListener(mUUID);
blueListener.Start();
BluetoothClient conn = blueListener.AcceptBluetoothClient();
updateUI("Client has connected");
Stream mstream = conn.GetStream();
while (true)
{
try
{
//handle server connection
byte[] received = new byte[1024];
mstream.Read(received, 0, received.Length);
updateUI("Received: " + Encoding.ASCII.GetString(received));
byte[] sent = Encoding.ASCII.GetBytes("Hello world\n");
mstream.Write(sent, 0, sent.Length);
}
catch (IOException exception)
{
updateUI("Client has disconnected!!");
break;
}
}
}
private void connectAsClient()
{
startScan();
}
private void startScan()
{
listBox1.DataSource = null;
listBox1.Items.Clear();
items.Clear();
Thread bluetoothScanThread = new Thread(new ThreadStart(scan));
bluetoothScanThread.Start();
}
BluetoothDeviceInfo[] devices;
private void scan()
{
updateUI("Start scanning...");
BluetoothClient client = new BluetoothClient();
devices = client.DiscoverDevicesInRange();
updateUI(devices.Length.ToString() + " devices discovered");
updateUI("Scan complete");
foreach(BluetoothDeviceInfo d in devices)
{
items.Add(d.DeviceName);
}
updateDeviceList();
}
private void updateUI(string message)
{
Func<int> del = delegate ()
{
tbOutput.AppendText(message + System.Environment.NewLine);
return 0;
};
Invoke(del);
}
private void updateDeviceList()
{
Func<int> del = delegate ()
{
listBox1.DataSource = items;
return 0;
};
Invoke(del);
}
BluetoothDeviceInfo DeviceInfo;
private void listBox1_DoubleClick(object sender, EventArgs e)
{
DeviceInfo = devices.ElementAt(listBox1.SelectedIndex);
updateUI(DeviceInfo.DeviceName + " was Selected, attempting connect");
if(PairDevice())
{
updateUI("Device paired");
updateUI("starting connect thread");
Thread bluetoothClientThread = new Thread(new ThreadStart(ClientConnectThread));
bluetoothClientThread.Start();
}
else
{
updateUI("pair failed");
}
}
private void ClientConnectThread()
{
BluetoothClient client = new BluetoothClient();
updateUI("Attempting connect");
client.BeginConnect(DeviceInfo.DeviceAddress, mUUID, this.BluetoothClientConnectCallback, client);
}
void BluetoothClientConnectCallback(IAsyncResult result)
{
BluetoothClient client = (BluetoothClient)result.AsyncState;
client.EndConnect(result);
Stream stream = client.GetStream();
stream.ReadTimeout = 10000;
while(true)
{
try
{
byte[] received = new byte[1024];
stream.Read(received, 0, received.Length);
updateUI(Encoding.ASCII.GetString(received));
}
catch (IOException exception)
{
updateUI("Client has disconnected!!");
break;
}
}
}
string mypin = "1234";
private bool PairDevice()
{
if(!DeviceInfo.Authenticated)
{
if (!BluetoothSecurity.PairRequest(DeviceInfo.DeviceAddress, mypin)) ;
{
return false;
}
}
return true;
}
bool ready = false;
byte[] message;
private void tbText_KeyPress(object sender, KeyPressEventArgs e)
{
if(e.KeyChar == 13)
{
byte[] b = Encoding.ASCII.GetBytes(tbText.Text);
ready = true;
tbText.Clear();
}
}
}
}
I tried to edit my code but I don't know why scan method can't be called.
class Program
{
Guid mUUID = new Guid("00001101-0000-1000-8000-00805F9B34FB");
List<string> items = new List<string>();
BluetoothDeviceInfo[] devices;
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Thread bluetoothScanThread = new Thread(new ThreadStart(scan));
bluetoothScanThread.Start();
}
public void scan()
{
Console.WriteLine("Start scanning...");
BluetoothClient client = new BluetoothClient();
devices = client.DiscoverDevicesInRange();
Console.WriteLine(devices.Length.ToString() + " devices discovered");
Console.WriteLine("Scan complete");
foreach (BluetoothDeviceInfo d in devices)
{
items.Add(d.DeviceName);
}
}
}
Related
I'm running into some issues trying to get NetMQ to work. My code compiles fine but I'm not achieving my desired outcome. Testing both Pub and Sub side from one PC.
I have a UI Handler that is linked to four buttons, start/stop pub/sub.
Would love if someone could shed some light into the issue.
Desired outcome: To be able to transfer test.wav from "Sending Folder" to "Receiving Folder"
Publisher Logic
using System.Threading;
using NetMQ;
using NetMQ.Sockets;
using System.IO;
public class Publisher
{
private readonly Thread _publisherThread;
private readonly Thread _subscriberThread;
public Publisher()
{
_publisherThread = new Thread(PublisherWork);
_publisherThread.Start();
_subscriberThread = new Thread(SubscriberWork);
_subscriberThread.Start();
}
public void Start()
{
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind("tcp://*:5557");
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Sending Folder/test.wav";
byte[] fileBytes = File.ReadAllBytes(filePath);
pubSocket.SendMoreFrame("File").SendFrame(fileBytes);
}
}
private void PublisherWork()
{
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind("tcp://*:5557"); // [C] Port
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Sending Folder/test.wav"; // [C] Filepath
byte[] fileBytes = File.ReadAllBytes(filePath);
while (true)
{
pubSocket.SendMoreFrame("File").SendFrame(fileBytes); // [C] Topic
Thread.Sleep(1000);
}
}
}
private void SubscriberWork()
{
using (var subSocket = new SubscriberSocket())
{
subSocket.Connect("tcp://localhost:5557"); // [C] Port
subSocket.Subscribe("File"); // [C] Topic
while (true)
{
var topic = subSocket.ReceiveFrameString();
var fileBytes = subSocket.ReceiveFrameBytes();
SaveWavFile(fileBytes);
}
}
}
private void SaveWavFile(byte[] fileBytes)
{
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Receiving Folder/test.wav";
File.WriteAllBytes(filePath, fileBytes);
}
}
Subscriber Logic
using System.Threading;
using NetMQ;
using NetMQ.Sockets;
using UnityEngine;
using UnityEngine.UI;
using System.IO;
public class Subscriber : MonoBehaviour
{
public Button startButton;
public Button stopButton;
private Thread _publisherThread;
private Thread _subscriberThread;
private bool _isRunning;
private void Start()
{
startButton.onClick.AddListener(StartThreads);
stopButton.onClick.AddListener(StopThreads);
}
private void StartThreads()
{
_isRunning = true;
_publisherThread = new Thread(PublisherWork);
_publisherThread.Start();
_subscriberThread = new Thread(SubscriberWork);
_subscriberThread.Start();
}
private void StopThreads()
{
_isRunning = false;
_publisherThread.Join();
_subscriberThread.Join();
}
private void PublisherWork()
{
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind("tcp://*:5557"); // [C] Port
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Sending Folder/test.wav"; // [C] Filepath
byte[] fileBytes = File.ReadAllBytes(filePath);
while (_isRunning)
{
pubSocket.SendMoreFrame("File").SendFrame(fileBytes); // [C] Topic
Thread.Sleep(1000);
}
}
NetMQConfig.Cleanup();
}
private void SubscriberWork()
{
using (var subSocket = new SubscriberSocket())
{
subSocket.Connect("tcp://localhost:5557"); // [C] Port
subSocket.Subscribe("File"); // [C] Topic
while (_isRunning)
{
var topic = subSocket.ReceiveFrameString();
var fileBytes = subSocket.ReceiveFrameBytes();
SaveWavFile(fileBytes);
}
}
NetMQConfig.Cleanup();
}
private void SaveWavFile(byte[] fileBytes)
{
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Receiving Folder/test.wav"; // [C]
File.WriteAllBytes(filePath, fileBytes);
}
}
UI Handler
using System.Threading;
using NetMQ;
using NetMQ.Sockets;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class UIHandler : MonoBehaviour
{
public Button startSubscriberButton;
public Button stopSubscriberButton;
public Button startPublisherButton;
public Button stopPublisherButton;
public Text pubStartStatus;
public Text pubStopStatus;
public Text subStartStatus;
public Text subStopStatus;
private Publisher _publisher;
private readonly Thread _publisherThread;
private readonly Thread _subscriberThread;
private bool _publisherRunning = false;
private bool _subscriberRunning = false;
public UIHandler()
{
// Initializing the threads
_publisherThread = new Thread(PublisherWork);
_subscriberThread = new Thread(SubscriberWork);
}
private void Start()
{
_publisher = new Publisher();
startPublisherButton.onClick.AddListener(StartPublisher);
stopPublisherButton.onClick.AddListener(StopPublisher);
startSubscriberButton.onClick.AddListener(StartSubscriber);
stopSubscriberButton.onClick.AddListener(StopSubscriber);
}
public void StartPublisher()
{
// Starting the publisher thread
_publisherThread.Start();
_publisherRunning = true;
pubStartStatus.text = "pubStartStatus: Started";
}
public void StopPublisher()
{
// Stopping the publisher thread
_publisherRunning = false;
_publisherThread.Join();
pubStopStatus.text = "pubStartStatus: Stopped";
}
public void StartSubscriber()
{
// Starting the subscriber thread
_subscriberThread.Start();
_subscriberRunning = true;
subStartStatus.text = "subStartStatus: Stopped";
}
public void StopSubscriber()
{
// Stopping the subscriber thread
_subscriberRunning = false;
_subscriberThread.Join();
subStopStatus.text = "subStartStatus: Stopped";
}
private void PublisherWork()
{
// Creating a publisher socket and binding it to the specified address
using (var pubSocket = new PublisherSocket())
{
pubSocket.Bind("tcp://*:5557");
// Reading the file bytes from the specified location
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Sending Folder/test.wav";
byte[] fileBytes = File.ReadAllBytes(filePath);
// Publishing the file bytes continuously
while (_publisherRunning)
{
pubSocket.SendMoreFrame("File").SendFrame(fileBytes);
Thread.Sleep(1000);
}
}
NetMQConfig.Cleanup();
}
private void SubscriberWork()
{
// Creating a subscriber socket and connecting it to the specified address
using (var subSocket = new SubscriberSocket())
{
subSocket.Connect("tcp://localhost:5557");
subSocket.Subscribe("File");
// Receiving and saving the file bytes continuously
while (_subscriberRunning)
{
var topic = subSocket.ReceiveFrameString();
var fileBytes = subSocket.ReceiveFrameBytes();
SaveWavFile(fileBytes);
}
}
NetMQConfig.Cleanup();
}
private void SaveWavFile(byte[] fileBytes)
{
// Saving the received file bytes to the specified location
string filePath = "C:/Users/XXXXX/Desktop/0MQ Demo/Receiving Folder/test.wav";
File.WriteAllBytes(filePath, fileBytes);
}
}
I have a problem: the server doesn't receive any data from the client.
Here is server initialization:
public void Start()
{
var listener = new TcpListener(IPAddress.Any, Port);
listener.Start();
Task.Run(
async () =>
{
while (!this.cancellationToken.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync();
var stream = client.GetStream();
string request = await ReceiveRequestAsync(stream);
await RequestHandlerAsync(request, stream);
}
listener.Stop();
}, this.cancellationToken);
}
Here is requesting client code (it is from unit test so server is initialized right here):
var server = new SimpleFtpServer();
server.Start();
using (TcpClient client = new TcpClient(RequestUri, Port))
{
NetworkStream stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream)
{
AutoFlush = true,
};
writer.Write("zapros");
using (StreamReader reader = new StreamReader(stream))
{
Console.Writeline(reader.ReadToEnd());
}
}
server.Stop();
It worth saying that I have started learning async/await in C# really recently so probably the problem is in usage of them.
Thank you in advance!
its probably not perfect however I am in the same situation as you and created a Async TCP Client/Server for practice and experimentation.
The below is an excerpt of my implementation, it works s
Server:
public class AsyncServerDemo
{
private CancellationTokenSource cancel;
private readonly TcpListenerEx listener;
private Task WaitingForConnections;
private Timer timerCallAcceptClients;
public bool IsRunning { get; private set; }
public AsyncServerDemo(int port)
{
cancel = new CancellationTokenSource();
listener = new TcpListenerEx(IPAddress.Any, port);
}
private Task<string> WaitForMessageAsync(TcpClient client, CancellationToken token)
{
return Task.Run(() =>
{
StringBuilder sb = new StringBuilder();
bool dataAvailable = false;
while (!token.IsCancellationRequested)
{
while (client.Client.Available > 0)
{
dataAvailable = true;
int buffered = client.Client.Available;
byte[] buffer = new byte[buffered];
client.Client.Receive(buffer);
sb.Append(Encoding.ASCII.GetString(buffer));
}
if (dataAvailable)
{
dataAvailable = false;
return sb.ToString();
}
};
return string.Empty; //timeout
});
}
private Task AcceptClientAsync()
{
return Task.Factory.StartNew(async () =>
{
IsRunning = true && !cancel.IsCancellationRequested;
while (!cancel.IsCancellationRequested)
{
if (!listener.Pending())
{
continue;
}
TcpClient newClient = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
Stopwatch timeout = new Stopwatch();
timeout.Restart();
string message = await WaitForMessageAsync(newClient, new CancellationTokenSource(TimeSpan.FromSeconds(5)).Token);
if (message != null)
{
//TODO: Message recieved
}
timeout.Stop();
}
});
}
public void Start()
{
listener.Start();
timerCallAcceptClients = new Timer(new TimerCallback((state) =>
{
AcceptClientAsync();
}), null, 0, (int)TimeSpan.FromSeconds(1).TotalMilliseconds);
}
public async void Stop()
{
if (!IsRunning) return;
using (cancel)
cancel.Cancel();
timerCallAcceptClients.Dispose();
if (WaitingForConnections != null)
await WaitingForConnections;
cancel = null;
listener.Stop();
IsRunning = false;
cancel = new CancellationTokenSource();
}
}
Client:
public class ClientExDemo
{
private Task<string> WaitForMessage;
private NetworkStream currentStream;
private CancellationTokenSource messageToken;
public EventHandler<ClientEx> OnServerFound;
public TcpClient Connection;
public EventHandler<string> OnMessage;
public async Task StartListenAsync(CancellationTokenSource token = null)
{
if (token == null)
messageToken = new CancellationTokenSource();
else
messageToken = token;
currentStream = Connection.GetStream();
string message = "";
if (message.Length > 0)
OnMessage?.Invoke(this, message);
if (!messageToken.IsCancellationRequested)
{
await StartListenAsync(token);
}
Timeout();
}
protected virtual void Timeout()
{
}
public async Task WaitForServerAsync(string ip, int port)
{
do
{
try
{
await Connection.ConnectAsync(ip, port);
}
catch (SocketException x)
{
}
await Task.Delay(50);
} while (!Connection.Connected);
}
public void StopListen()
{
using (messageToken)
{
messageToken.Cancel();
}
try
{
WaitForMessage.GetAwaiter().GetResult();
}
catch (AggregateException)
{
}
currentStream.Close();
messageToken = null;
currentStream = null;
WaitForMessage = null;
}
public ClientExDemo()
{
Connection = new TcpClient();
OnServerFound += ServerFound;
}
private void ServerFound(object sender, ClientEx args)
{
}
public void Send(string message)
{
Connection.Client.Send(Encoding.ASCII.GetBytes(message));
}
}
You can send messages from the client in a simple console application:
ClientEx client= new ClientEx();
await client.WaitForServerAsync(ip, port);
string msg = string.Empty;
do
{
Console.Write("Send Message: ");
msg = Console.ReadLine();
shell.Send(msg);
} while (msg != "q");
Console.WriteLine();
Console.WriteLine("BYE");
Console.ReadKey();
I wrote a client-server app, this is a console chat application for many clients. When only one client is connected, the application works well, but when two or more clients are connected I have a bug, after sending one message to the second client, he lost connection to the server and only first client can send a message to the server...
I used Task for asynchronous operations like listening port and sending messages. When one client sends a message to the server, it adds it to the list messages and resends to all clients to refresh all windows.
Server application:
using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace Server
{
class Program
{
private static List<Client> clients = new List<Client>();
private static TcpListener listener = null;
private static StreamReader reader = null;
private static StreamWriter writer = null;
private static List<Task> clientTasks = new List<Task>();
private static List<string> messages = new List<string>();
public static void Main()
{
Console.Title = "Server";
try
{
listener = new TcpListener(IPAddress.Parse("127.0.0.1"), 8080);
listener.Start();
Console.WriteLine("Server started...");
var connectTask = Task.Run(() => ConnectClients());
//var listenTask = Task.Run(() => ListenClients());
Task.WaitAll(connectTask);
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
if (listener != null)
{
listener.Stop();
}
}
}
private static void ConnectClients()
{
Console.WriteLine("Waiting for incoming client connections...");
while (true)
{
if (listener.Pending()) //if someone want to connect
{
clients.Add(new Client(listener.AcceptTcpClient(), "Client: " + (clients.Count + 1)));
Console.WriteLine(clients[clients.Count - 1].clientName + " connected to server.");
var newClientTask = Task.Run(() => HandleClient(clients[clients.Count - 1]));
clientTasks.Add(newClientTask); //start new task for new client
}
}
}
private static void HandleClient(Client TCPClient)
{
Console.WriteLine("Starting handle client");
string s = string.Empty;
writer = new StreamWriter(TCPClient.client.GetStream());
reader = new StreamReader(TCPClient.client.GetStream());
try
{
while (!(s = reader.ReadLine()).Equals("Exit") || (s == null))
{
if(!TCPClient.client.Connected)
{
Console.WriteLine("Client disconnected.");
clients.Remove(TCPClient);
}
Console.WriteLine("From client: " + TCPClient.clientName + " -> " + s);
messages.Add(TCPClient.clientName + ": " + s); //save new message
//Console.WriteLine(s);
foreach (Client c in clients) //refresh all connected clients
{
c.writer.WriteLine("%C"); //clear client
foreach (string msg in messages)
{
c.writer.WriteLine(msg);
c.writer.Flush();
}
}
}
//CloseServer();
}
catch (Exception e) { Console.WriteLine(e); }
Console.WriteLine("ending handle client");
}
private static void CloseServer()
{
reader.Close();
writer.Close();
clients.ForEach(tcpClient => tcpClient.client.Close());
}
}
}
Client information class:
using System.Net.Sockets;
using System.IO;
namespace Server
{
class Client
{
public TcpClient client;
public StreamWriter writer;
public string clientName;
public Client(TcpClient client, string clientName)
{
this.client = client;
writer = new StreamWriter(client.GetStream());
this.clientName = clientName;
}
}
}
Client application:
using System.Net.Sockets;
using System.Net;
using System.IO;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace Client
{
class Program
{
private static TcpClient client = new TcpClient();
private static StreamReader reader;
private static StreamWriter writer;
private static bool refresh;
private static List<string> messages = new List<string>();
public static void Main()
{
Console.Title = "Client";
ConnectLoop();
//Task.WaitAll(sendTask, recieveTask); //wait for end of all tasks
}
private static void ConnectLoop()
{
bool refreshTask = false;
Task sendTask = null, recieveTask = null, updateConvTask = null;
while (true)
{
if(!client.Connected) //try to connect
{
refreshTask = true;
if(sendTask != null || recieveTask != null || updateConvTask != null)
{
sendTask.Dispose();
recieveTask.Dispose();
updateConvTask.Dispose();
sendTask = recieveTask = updateConvTask = null;
}
Console.WriteLine("Connecting to server...");
try
{
client.Connect(IPAddress.Parse("127.0.0.1"), 8080);
}
catch (SocketException) { }
Thread.Sleep(10);
}
else if(refreshTask) // \/ CONNECTED \/
{
Console.WriteLine("Connected.");
reader = new StreamReader(client.GetStream());
writer = new StreamWriter(client.GetStream());
sendTask = Task.Run(() => SendMessage()); //task for sending messages
recieveTask = Task.Run(() => RecieveMessage()); //task for recieving messages
updateConvTask = Task.Run(() => UpdateConversation()); //task for update console window
refreshTask = false;
}
}
}
private static void SendMessage()
{
string msgToSend = string.Empty;
do
{
Console.WriteLine("Enter a message to send to the server");
msgToSend = Console.ReadLine();
writer.WriteLine(msgToSend);
writer.Flush();
} while (!msgToSend.Equals("Exit"));
EndConnection();
}
private static void RecieveMessage()
{
try
{
while (client.Connected)
{
//Console.Clear();
string msg = reader.ReadLine();
if(msg != string.Empty)
{
if (msg == "%C") //special message from server, clear messages if recieve it
{
messages.Clear();
}
else
{
messages.Add(msg);
refresh = true; //refresh console window
}
}
//Console.Clear();
//Console.WriteLine(msgFromServer);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
private static void UpdateConversation()
{
//string conversationTmp = string.Empty;
try
{
while (true)
{
if (refresh) //only if refresh
{
refresh = false;
Console.Clear();
messages.ForEach(msg => Console.WriteLine(msg)); //write all messages
Console.WriteLine();
}
}
}
catch (Exception) { }
}
private static void EndConnection()
{
reader.Close();
writer.Close();
client.Close();
}
}
}
I know that my bug will be something stupid. I'm new to TCP/IP applications, could you give me links to some tutorials that use it and Tasks?
Thanks for any help.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Tester
{
public partial class Form1 : Form
{
FileDownload fw;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Download()
{
fw = new FileDownload("http://download.microsoft.com/download/E/E/2/EE2D29A1-2D5C-463C-B7F1-40E4170F5E2C/KinectSDK-v1.0-Setup.exe", #"D:\KinetSDK.exe", 5120);
// Display progress...
Task.Factory.StartNew(() =>
{
while (!fw.Done)
{
//Console.SetCursorPosition(0, Console.CursorTop);
label1.Text = string.Format("ContentLength: {0} | BytesWritten: {1}", fw.ContentLength, fw.BytesWritten);
}
});
// Start the download...
fw.Start();
//Console.ReadKey();
}
public class FileDownload
{
private volatile bool _allowedToRun;
private string _source;
private string _destination;
private int _chunkSize;
private Lazy<int> _contentLength;
public int BytesWritten { get; private set; }
public int ContentLength { get { return _contentLength.Value; } }
public bool Done { get { return ContentLength == BytesWritten; } }
public FileDownload(string source, string destination, int chunkSize)
{
_allowedToRun = true;
_source = source;
_destination = destination;
_chunkSize = chunkSize;
_contentLength = new Lazy<int>(() => Convert.ToInt32(GetContentLength()));
BytesWritten = 0;
}
private long GetContentLength()
{
var request = (HttpWebRequest)WebRequest.Create(_source);
request.Method = "HEAD";
using (var response = request.GetResponse())
return response.ContentLength;
}
private async Task Start(int range)
{
if (!_allowedToRun)
throw new InvalidOperationException();
var request = (HttpWebRequest)WebRequest.Create(_source);
request.Method = "GET";
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
request.AddRange(range);
using (var response = await request.GetResponseAsync())
{
using (var responseStream = response.GetResponseStream())
{
using (var fs = new FileStream(_destination, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
{
while (_allowedToRun)
{
var buffer = new byte[_chunkSize];
var bytesRead = await responseStream.ReadAsync(buffer, 0, buffer.Length);
if (bytesRead == 0) break;
await fs.WriteAsync(buffer, 0, bytesRead);
BytesWritten += bytesRead;
}
await fs.FlushAsync();
}
}
}
}
public Task Start()
{
_allowedToRun = true;
return Start(BytesWritten);
}
public void Pause()
{
_allowedToRun = false;
}
}
private void btnPause_Click(object sender, EventArgs e)
{
if (btnPause.Text == "Pause")
{
fw.Pause();
btnPause.Text = "Continue";
}
if (btnPause.Text == "Continue")
{
fw.Start().ContinueWith(t => label2.Text = "Done");
btnPause.Text = "Pause";
}
}
private void button1_Click(object sender, EventArgs e)
{
Download();
}
}
}
using a breakpoint it's getting to the line:
using (var fs = new FileStream(_destination, FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
when i click continue i see the form1 but nothing happen. No progress on label1 and when i click on the Pause button nothing happen. And if i put a breakpoint after this line on the next line it will not get there.
It will never get to this line:
while (_allowedToRun)
I have a listener:
listener = new HttpListener();
listener.Prefixes.Add(#"http://+:8077/");
listener.Start();
listenerThread = new Thread(HandleRequests);
listenerThread.Start();
And I am handling requests:
private void HandleRequests()
{
while (listener.IsListening)
{
var context = listener.BeginGetContext(new AsyncCallback(ListenerCallback), listener);
context.AsyncWaitHandle.WaitOne();
}
}
private void ListenerCallback(IAsyncResult ar)
{
var listener = ar.AsyncState as HttpListener;
var context = listener.EndGetContext(ar);
//do some stuff
}
I would like to write void Stop() in such a way, that:
It will block until all currently handled requests will end (ie. will wait for all threads to "do some stuff").
While it will wait for already started requests, it will not allow any more requests (ie. return at the beginning of ListenerCallback).
After that it will call listener.Stop() (listener.IsListening became false).
How could it be write?
EDIT: What do you think about this solution? Is it safe?
public void Stop()
{
lock (this)
{
isStopping = true;
}
resetEvent.WaitOne(); //initially set to true
listener.Stop();
}
private void ListenerCallback(IAsyncResult ar)
{
lock (this)
{
if (isStopping)
return;
resetEvent.Reset();
numberOfRequests++;
}
var listener = ar.AsyncState as HttpListener;
var context = listener.EndGetContext(ar);
//do some stuff
lock (this)
{
if (--numberOfRequests == 0)
resetEvent.Set();
}
}
For completeness, here is what it would look like if you manage your own worker threads:
class HttpServer : IDisposable
{
private readonly HttpListener _listener;
private readonly Thread _listenerThread;
private readonly Thread[] _workers;
private readonly ManualResetEvent _stop, _ready;
private Queue<HttpListenerContext> _queue;
public HttpServer(int maxThreads)
{
_workers = new Thread[maxThreads];
_queue = new Queue<HttpListenerContext>();
_stop = new ManualResetEvent(false);
_ready = new ManualResetEvent(false);
_listener = new HttpListener();
_listenerThread = new Thread(HandleRequests);
}
public void Start(int port)
{
_listener.Prefixes.Add(String.Format(#"http://+:{0}/", port));
_listener.Start();
_listenerThread.Start();
for (int i = 0; i < _workers.Length; i++)
{
_workers[i] = new Thread(Worker);
_workers[i].Start();
}
}
public void Dispose()
{ Stop(); }
public void Stop()
{
_stop.Set();
_listenerThread.Join();
foreach (Thread worker in _workers)
worker.Join();
_listener.Stop();
}
private void HandleRequests()
{
while (_listener.IsListening)
{
var context = _listener.BeginGetContext(ContextReady, null);
if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
return;
}
}
private void ContextReady(IAsyncResult ar)
{
try
{
lock (_queue)
{
_queue.Enqueue(_listener.EndGetContext(ar));
_ready.Set();
}
}
catch { return; }
}
private void Worker()
{
WaitHandle[] wait = new[] { _ready, _stop };
while (0 == WaitHandle.WaitAny(wait))
{
HttpListenerContext context;
lock (_queue)
{
if (_queue.Count > 0)
context = _queue.Dequeue();
else
{
_ready.Reset();
continue;
}
}
try { ProcessRequest(context); }
catch (Exception e) { Console.Error.WriteLine(e); }
}
}
public event Action<HttpListenerContext> ProcessRequest;
}
Well there are several ways to solve this... This is a simple example that uses a semaphore to track ongoing work, and a signal that is raised when all workers are finished. This should give you a basic idea to work from.
The solution below is not ideal, ideally we should acquire the semaphore before calling BeginGetContext. That makes shutdown more difficult, so I've chosen to use this more simplified approach. If I were doing this for 'real' I'd probably write my own thread management rather than relying on the ThreadPool. This would allow a more dependable shutdown.
Anyway here is the complete example:
class TestHttp
{
static void Main()
{
using (HttpServer srvr = new HttpServer(5))
{
srvr.Start(8085);
Console.WriteLine("Press [Enter] to quit.");
Console.ReadLine();
}
}
}
class HttpServer : IDisposable
{
private readonly int _maxThreads;
private readonly HttpListener _listener;
private readonly Thread _listenerThread;
private readonly ManualResetEvent _stop, _idle;
private readonly Semaphore _busy;
public HttpServer(int maxThreads)
{
_maxThreads = maxThreads;
_stop = new ManualResetEvent(false);
_idle = new ManualResetEvent(false);
_busy = new Semaphore(maxThreads, maxThreads);
_listener = new HttpListener();
_listenerThread = new Thread(HandleRequests);
}
public void Start(int port)
{
_listener.Prefixes.Add(String.Format(#"http://+:{0}/", port));
_listener.Start();
_listenerThread.Start();
}
public void Dispose()
{ Stop(); }
public void Stop()
{
_stop.Set();
_listenerThread.Join();
_idle.Reset();
//aquire and release the semaphore to see if anyone is running, wait for idle if they are.
_busy.WaitOne();
if(_maxThreads != 1 + _busy.Release())
_idle.WaitOne();
_listener.Stop();
}
private void HandleRequests()
{
while (_listener.IsListening)
{
var context = _listener.BeginGetContext(ListenerCallback, null);
if (0 == WaitHandle.WaitAny(new[] { _stop, context.AsyncWaitHandle }))
return;
}
}
private void ListenerCallback(IAsyncResult ar)
{
_busy.WaitOne();
try
{
HttpListenerContext context;
try
{ context = _listener.EndGetContext(ar); }
catch (HttpListenerException)
{ return; }
if (_stop.WaitOne(0, false))
return;
Console.WriteLine("{0} {1}", context.Request.HttpMethod, context.Request.RawUrl);
context.Response.SendChunked = true;
using (TextWriter tw = new StreamWriter(context.Response.OutputStream))
{
tw.WriteLine("<html><body><h1>Hello World</h1>");
for (int i = 0; i < 5; i++)
{
tw.WriteLine("<p>{0} # {1}</p>", i, DateTime.Now);
tw.Flush();
Thread.Sleep(1000);
}
tw.WriteLine("</body></html>");
}
}
finally
{
if (_maxThreads == 1 + _busy.Release())
_idle.Set();
}
}
}
I have consulted my code in EDIT part of my question and I've decided to accept it with some modifications:
public void Stop()
{
lock (locker)
{
isStopping = true;
}
resetEvent.WaitOne(); //initially set to true
listener.Stop();
}
private void ListenerCallback(IAsyncResult ar)
{
lock (locker) //locking on this is a bad idea, but I forget about it before
{
if (isStopping)
return;
resetEvent.Reset();
numberOfRequests++;
}
try
{
var listener = ar.AsyncState as HttpListener;
var context = listener.EndGetContext(ar);
//do some stuff
}
finally //to make sure that bellow code will be executed
{
lock (locker)
{
if (--numberOfRequests == 0)
resetEvent.Set();
}
}
}
Simply calling listener.Stop() should do the trick. This will not terminate any connections that have already been established but will prevent any new connections.
This uses the BlockingCollection typed queue to service requests. It is usable as is. You should derive a class from this one and override Response.
using System;
using System.Collections.Concurrent;
using System.Net;
using System.Text;
using System.Threading;
namespace Service
{
class HttpServer : IDisposable
{
private HttpListener httpListener;
private Thread listenerLoop;
private Thread[] requestProcessors;
private BlockingCollection<HttpListenerContext> messages;
public HttpServer(int threadCount)
{
requestProcessors = new Thread[threadCount];
messages = new BlockingCollection<HttpListenerContext>();
httpListener = new HttpListener();
}
public virtual int Port { get; set; } = 80;
public virtual string[] Prefixes
{
get { return new string[] {string.Format(#"http://+:{0}/", Port )}; }
}
public void Start(int port)
{
listenerLoop = new Thread(HandleRequests);
foreach( string prefix in Prefixes ) httpListener.Prefixes.Add( prefix );
listenerLoop.Start();
for (int i = 0; i < requestProcessors.Length; i++)
{
requestProcessors[i] = StartProcessor(i, messages);
}
}
public void Dispose() { Stop(); }
public void Stop()
{
messages.CompleteAdding();
foreach (Thread worker in requestProcessors) worker.Join();
httpListener.Stop();
listenerLoop.Join();
}
private void HandleRequests()
{
httpListener.Start();
try
{
while (httpListener.IsListening)
{
Console.WriteLine("The Linstener Is Listening!");
HttpListenerContext context = httpListener.GetContext();
messages.Add(context);
Console.WriteLine("The Linstener has added a message!");
}
}
catch(Exception e)
{
Console.WriteLine (e.Message);
}
}
private Thread StartProcessor(int number, BlockingCollection<HttpListenerContext> messages)
{
Thread thread = new Thread(() => Processor(number, messages));
thread.Start();
return thread;
}
private void Processor(int number, BlockingCollection<HttpListenerContext> messages)
{
Console.WriteLine ("Processor {0} started.", number);
try
{
for (;;)
{
Console.WriteLine ("Processor {0} awoken.", number);
HttpListenerContext context = messages.Take();
Console.WriteLine ("Processor {0} dequeued message.", number);
Response (context);
}
} catch { }
Console.WriteLine ("Processor {0} terminated.", number);
}
public virtual void Response(HttpListenerContext context)
{
SendReply(context, new StringBuilder("<html><head><title>NULL</title></head><body>This site not yet implementd.</body></html>") );
}
public static void SendReply(HttpListenerContext context, StringBuilder responseString )
{
byte[] buffer = System.Text.Encoding.UTF8.GetBytes(responseString.ToString());
context.Response.ContentLength64 = buffer.Length;
System.IO.Stream output = context.Response.OutputStream;
output.Write(buffer, 0, buffer.Length);
output.Close();
}
}
}
This is a sample of how to use it. No need to use events or any lock blocks. The BlockingCollection solves all these problems.
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Net;
using System.Text;
using System.Threading;
namespace Service
{
class Server
{
public static void Main (string[] args)
{
HttpServer Service = new QuizzServer (8);
Service.Start (80);
for (bool coninute = true; coninute ;)
{
string input = Console.ReadLine ().ToLower();
switch (input)
{
case "stop":
Console.WriteLine ("Stop command accepted.");
Service.Stop ();
coninute = false;
break;
default:
Console.WriteLine ("Unknown Command: '{0}'.",input);
break;
}
}
}
}
}