Skip to content
Closed
86 changes: 69 additions & 17 deletions src/BloomExe/Publish/BloomPub/wifi/BloomReaderUDPListener.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.Text; // debug only, for Encoding.ASCII.GetString()
using System.Threading;
using Bloom.Publish.BloomPUB.wifi;

Expand All @@ -18,7 +19,11 @@ class BloomReaderUDPListener
private int _portToListen = 5915;
Thread _listeningThread;
public event EventHandler<AndroidMessageArgs> NewMessageReceived;
UdpClient _listener = null;

// Client by which we receive replies to broadcast book advert. It listens on all network
// interfaces on the expected listening port.
UdpClient _clientForBookRequestReceive = null;

private bool _listening;

//constructor: starts listening.
Expand All @@ -35,35 +40,80 @@ public BloomReaderUDPListener()
/// </summary>
public void ListenForUDPPackages()
{
// UdpClient needs a constructor that specifies more than just the port.
//
// If we specify the port only, this article describes how the system proceeds:
// https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.udpclient.-ctor?view=net-9.0
// "This constructor creates an underlying Socket and binds it to the port number
// from which you intend to communicate. Use this constructor if you are only
// interested in setting the local port number. The underlying service provider
// will assign the local IP address."
// And, similar to what has been observed in Advertiser, the "underlying service
// provider" sometimes assigns the IP address from the wrong network interface.
//
// So, create the endpoint first, setting its port *and* special IP addr 'IPAddress.Any'.
// This address causes the endpoint to listen for client activity on all network interfaces,
// bypassing the possibility of the network stack choosing a wrong address:
// https://learn.microsoft.com/en-us/dotnet/api/system.net.ipaddress.any?view=net-9.0
// Then base the UdpClient on this endpoint.

IPEndPoint epForBookRequestReceive = null;

try
{
_listener = new UdpClient(_portToListen);
epForBookRequestReceive = new IPEndPoint(IPAddress.Any, _portToListen);

if (epForBookRequestReceive == null)
{
EventLog.WriteEntry("Application", "UDPListener, ERROR creating IPEndPoint");
return;
}

_clientForBookRequestReceive = new UdpClient(epForBookRequestReceive);

if (_clientForBookRequestReceive == null)
{
EventLog.WriteEntry("Application", "UDPListener, ERROR creating UdpClient");
return;
}
}
catch (SocketException e)
{
//log then do nothing
Bloom.Utils.MiscUtils.SuppressUnusedExceptionVarWarning(e);
}

if (_listener != null)
{
IPEndPoint groupEP = new IPEndPoint(IPAddress.Any, 0);
// Listener has been created on the port that BloomReader will respond to, and
// will monitor *all* network interfaces for UDP packets sent to that port.

while (_listening)
while (_listening)
{
try
{
try
// Log our local address and port.
if (_clientForBookRequestReceive?.Client?.LocalEndPoint is IPEndPoint localEP)
{
byte[] bytes = _listener.Receive(ref groupEP); // waits for packet from Android.

//raise event
NewMessageReceived?.Invoke(this, new AndroidMessageArgs(bytes));
EventLog.WriteEntry("Application", $"UDP listening will wait for packet on {localEP.Address}, port {localEP.Port}");
}
catch (SocketException se)

byte[] bytes = _clientForBookRequestReceive.Receive(ref epForBookRequestReceive); // waits for packet from Android.

// DEBUG ONLY
//Debug.WriteLine("UDPListener, got {0} bytes (raising \'NewMessageReceived\'):", bytes.Length);
//var bytesToString = Encoding.ASCII.GetString(bytes, 0, bytes.Length);
//Debug.WriteLine(" " + bytesToString.Substring(0, bytes.Length));
// END DEBUG

//raise event
NewMessageReceived?.Invoke(this, new AndroidMessageArgs(bytes));
}
catch (SocketException se)
{
if (!_listening || se.SocketErrorCode == SocketError.Interrupted)
{
if (!_listening || se.SocketErrorCode == SocketError.Interrupted)
return; // no problem, we're just closing up shop
throw se;
return; // no problem, we're just closing up shop
}
throw se;
}
}
}
Expand All @@ -73,12 +123,14 @@ public void StopListener()
if (_listening)
{
_listening = false;
_listener?.Close(); // forcibly end communication
_listener = null;
_clientForBookRequestReceive?.Close(); // forcibly end communication
_clientForBookRequestReceive = null;
}

if (_listeningThread == null)
{
return;
}

// Since we told the listener to close already this shouldn't have to do much (nor be dangerous)
_listeningThread.Abort();
Expand Down
Loading