Learning Delegates and Event Handlers, having issue with message not being shown - visual-studio

I am getting a crash course in delegates and event handlers and I have been following a tutorial on the subject and trying to plug in what I have learned into a socket server program I am creating.
I am trying to decouple my server from knowing about the AlertConnectionOpened class here:
namespace AlertConnectionOpened
{
//This is a subscriber class
public class AlertConnectionOpened
{
public void OnConnectionOpened(string message)
{
Console.WriteLine("Connection is opened");
}
}
}
So I am using a delegate in my server class to accomplish this.
namespace Server
{
public class RunServer
{
// State object for reading client data asynchronously
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousSocketListener
{
// Thread signal.
public ManualResetEvent allDone = new ManualResetEvent(false);
public AsynchronousSocketListener(int port)
{
}
//This defines the delegate
//Agreement between publisher and subscriber
//object, source of event or class publishing or sending data,
//second param is any additional data we need to send.
public delegate void ConnectionOpenEventHandler(string Message);
//Indicates something has happened and finished.
//Event defined here, based on delegate
public event ConnectionOpenEventHandler ConnectionOpened;
//Raise the Event, need a method to do this.
//Responsible for notifying subscribers
protected virtual void OnConnectionOpened()
{
if (ConnectionOpened != null)
ConnectionOpened("Connection Opened");
}
public void StartListening()
{
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
int port = 11000;
// Establish the local endpoint for the socket.
// The DNS name of the computer
// running the listener is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
try
{
listener.Bind(localEndPoint);
//backlog of how many clients to take in
listener.Listen(100);
while (true)
{
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
// Wait until a connection is made before continuing.
allDone.WaitOne();
OnConnectionOpened();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
public void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
public void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer, 0, bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1)
{
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content);
Random rand = new Random();
content = rand.ToString();
// Echo the data back to the client.
Send(handler, content);
}
else {
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
public void Send(Socket handler, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
public void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket handler = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args)
{
AsynchronousSocketListener a = new AsynchronousSocketListener(0);
a.StartListening();
return 0;
}
}
}
}
Here is also my main program:
namespace AppStart
{
class ServerStart
{
static void Main(string[] args)
{
RunServer.AsynchronousSocketListener svr1 = new RunServer.AsynchronousSocketListener(11000);//publisher
//Correct the port number so a second server can open
RunServer.AsynchronousSocketListener svr2 = new RunServer.AsynchronousSocketListener(350);
svr1.StartListening();//publisher
//This creates the subscriber
var alertConnectionOpened = new AlertConnectionOpened.AlertConnectionOpened();//subsciber
//make publisher register the handler for the event.
svr1.ConnectionOpened += alertConnectionOpened.OnConnectionOpened; //POinter to method
svr2.StartListening();
Console.ReadLine();
}
}
}
From the little I understand of this my call to OnConnectionOpened();, should be showing the message that there is now a connection, but it isn't.

Related

How To Fix Indy TIdTCPServer Freezing When Sending Text To TIdTCPClient?

Cannot send text from TIdTCPServer To TIdTCPClient, the server hanging (Not Responding), it just freezes when trying to send text to the client.
I Started Recently using Indy TIdTCPClient and TIdTCPServer, i have the client application working well when sending and receiving response from the server form,
then issue is from the server it doesn't send data, and when i try to send data like Indy creators provided in their Doc, it just freezes and then stops responding (crashes) :(, the weird thing is that the server sends back the response on Execute Event, and not sending data with my send function, so here is my code that i use:
Server Execute Event:
void __fastcall TServerMain::IdTCPServer1Execute(TIdContext *AContext)
{
UnicodeString uMessage;
uMessage = AContext->Connection->IOHandler->ReadLn();
MessageDisplay1->Lines->Add(uMessage);
AContext->Connection->IOHandler->WriteLn("Response OK!"); // i can receive the response from the client
}
Server Send Function:
void TServerMain::itsSendMessage(TIdTCPServer *itsName, UnicodeString uMessage) {
TIdContextList *Clients;
TIdContext *icContext;
if ( uMessage.Length() != 0 && itsName->Active ) {
Clients = itsName->Contexts->LockList();
for (int i = 0; i < Clients->Count; i++) {
icContext = (TIdContext*)Clients->Items[i];
icContext->Connection->IOHandler->WriteLn(uMessage);
}
itsName->Contexts->UnlockList();
}
} // this function doesn't send text to the clients however, it just hangs the application for ever.
Additional Note: The TIdTCPServer stops sending text even from it's OnExecute event when a client is disconnects!
UPDATE:
void __fastcall TMyContext::AddToQueue(TStream *AStream)
{
TStringList *queue = this->FQueue->Lock();
try {
queue->AddObject("", AStream);
this->FMessageInQueue = true;
}
__finally
{
this->FQueue->Unlock();
}
}
void __fastcall TMyContext::CheckQueue()
{
if ( !this->FMessageInQueue )
return;
std::unique_ptr<TStringList> temp(new TStringList);
TStringList *queue = this->FQueue->Lock();
try {
temp->OwnsObjects = true;
temp->Assign(queue);
queue->Clear();
this->FMessageInQueue = false;
}
__finally
{
this->FQueue->Unlock();
}
for (int i = 0; i < temp->Count; i++) {
this->Connection->IOHandler->Write( static_cast<TStream*>(temp->Objects[i]), static_cast<TStream*>(temp->Objects[i])->Size, true );
}
}
Server Send Function:
void __fastcall TServerMain::IdSendMessage(TIdTCPServer *IdTCPServer, TStream *AStream)
{
if ( !IdTCPServer->Active )
return;
TIdContextList *Clients = IdTCPServer->Contexts->LockList();
try {
for (int i = 0; i < Clients->Count; i++) {
static_cast<TMyContext*>(static_cast<TIdContext*>(Clients->Items[i]))->AddToQueue(AStream);
}
}
__finally
{
IdTCPServer->Contexts->UnlockList();
}
}
Client Receive Function:
void __fastcall TReadingThread::Receive() {
TMemoryStream * ms = new TMemoryStream();
this->IdTCPClient1->IOHandler->ReadStream(ms);
ms->Position = 0;
ClientMain->Image1->Picture->Bitmap->LoadFromStream(ms);
delete ms;
}
This function is Synchronized in a TThread.
This is how i send a TBitmap using TMemoryStream:
void __fastcall TServerMain::CaptureDesktop()
{
// Capture Desktop Canvas
HDC hdcDesktop;
TBitmap *bmpCapture = new TBitmap();
TMemoryStream *Stream = new TMemoryStream();
try {
bmpCapture->Width = Screen->Width;
bmpCapture->Height = Screen->Height;
hdcDesktop = GetDC(GetDesktopWindow());
BitBlt(bmpCapture->Canvas->Handle, 0,0,Screen->Width, Screen->Height, hdcDesktop, 0,0, SRCCOPY);
bmpCapture->SaveToStream(Stream);
Stream->Position = 0;
IdSendMessage(IdTCPServer1, Stream);
}
__finally
{
ReleaseDC(GetDesktopWindow(), hdcDesktop);
delete bmpCapture;
delete Stream;
}
}
TIdTCPServer is a multi-threaded component, which you are not accounting for propery.
The server's various events are fired in the context of internal worker threads, not in the context the main UI thread. Your OnExecute code is not syncing with the main UI thread when accessing MessageDisplay1, which can cause all kinds of problems, including but not limited to deadlocks. You MUST sync with the main UI thread, such as with TThread::Synchronize() or TThread::Queue(). For example:
void __fastcall TServerMain::IdTCPServer1Execute(TIdContext *AContext)
{
String Message = AContext->Connection->IOHandler->ReadLn();
// see http://docwiki.embarcadero.com/RADStudio/en/How_to_Handle_Delphi_Anonymous_Methods_in_C%2B%2B
TThread::Queue(nullptr, [](){ MessageDisplay1->Lines->Add(Message); });
AContext->Connection->IOHandler->WriteLn(_D("Response OK!"));
}
Also, you have 2 threads (whichever thread is calling itsSendMessage(), and the OnExecute thread) that are not syncing with each other, so they can potentially write text to the same client at the same time, overlapping each other's text and thus corrupting your communications. When sending unsolicited messages from the server to a client, I usually recommend (depending on circumstances) that you queue the messages and let the client thread's OnExecute code decide when it is safe to send the queue. Another reason to do this is to avoid deadlocks if one client becomes blocked, you don't want to block access to other clients. Do as much per-client work in the client's own OnExecute event as you can. For example:
class TMyContext : public TIdServerContext
{
private:
TIdThreadSafeStringList *FQueue;
bool FMsgInQueue;
public:
__fastcall TMyContext(TIdTCPConnection *AConnection, TIdYarn *AYarn, TIdContextThreadList *AList = nullptr)
: TIdServerContext(AConnection, AYarn, AList)
{
FQueue = new TIdThreadSafeStringList;
}
__fastcall ~TMyContext()
{
delete FQueue;
}
void AddToQueue(const String &Message)
{
TStringList *queue = FQueue->Lock();
try
{
queue->Add(Message);
FMsgInQueue = true;
}
__finally
{
FQueue->Unlock();
}
}
void CheckQueue()
{
if (!FMsgInQueue)
return;
std::unique_ptr<TStringList> temp(new TStringList);
TStringList *queue = FQueue->Lock();
try
{
temp->Assign(queue);
queue->Clear();
FMsgInQueue = false;
}
__finally
{
FQueue->Unlock();
}
Connection->IOHandler->Write(temp.get());
}
bool HasPendingData()
{
TIdIOHandler *io = Connection->IOHandler;
bool empty = io->InputBufferIsEmpty();
if (empty)
{
io->CheckForDataOnSource(100);
io->CheckForDisconnect();
empty = io->InputBufferIsEmpty();
}
return !empty;
}
};
__fastcall TServerMain::TServerMain(...)
{
IdTCPServer1->ContextClass = __classid(TMyContext);
...
}
void __fastcall TServerMain::IdTCPServer1Execute(TIdContext *AContext)
{
TMyContext *ctx = static_cast<TMyContext*>(AContext);
ctx->CheckQueue();
if (!ctx->HasPendingData())
return;
String Message = AContext->Connection->IOHandler->ReadLn();
TThread::Queue(nullptr, [](){ MessageDisplay1->Lines->Add(Message); });
AContext->Connection->IOHandler->WriteLn(_D("Response OK!"));
}
void TServerMain::itsSendMessage(TIdTCPServer *itsName, const String &Message)
{
if ( Message.IsEmpty() || !itsName->Active )
return;
TIdContextList *Clients = itsName->Contexts->LockList();
try
{
for (int i = 0; i < Clients->Count; ++i)
{
static_cast<TMyContext*>(static_cast<TIdContext*>(Clients->Items[i]))->AddToQueue(Message);
}
}
__finally
{
itsName->Contexts->UnlockList();
}
}

Xamarin.android Notification onClick doesn't take to new activity

This is going to be a long post! (grab a cup of coffee/popcorn)
I am using AltBeacon Xamarin sample in my code to show the beacons.
I have come across this example in creating Notifications in Xamarin.
Here there's an Application class where the core logic goes.
public class AltBeaconSampleApplication : Application, IBootstrapNotifier
{
private const string TAG = "AltBeaconSampleApplication";
BeaconManager _beaconManager;
private RegionBootstrap regionBootstrap;
private Region _backgroundRegion;
private BackgroundPowerSaver backgroundPowerSaver;
private bool haveDetectedBeaconsSinceBoot = false;
private string nearbyMessageString = "A beacon is nearby.";
private string nearbyTitleString = "AltBeacon Reference Application";
private MainActivity mainActivity = null;
public MainActivity MainActivity
{
get { return mainActivity; }
set { mainActivity = value; }
}
private NotificationActivity notificationActivity = null;
public NotificationActivity NotificationActivity
{
get { return notificationActivity; }
set { notificationActivity = value; }
}
public AltBeaconSampleApplication() : base() { }
public AltBeaconSampleApplication(IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer) : base(javaReference, transfer) { }
public override void OnCreate()
{
base.OnCreate();
_beaconManager = BeaconManager.GetInstanceForApplication(this);
var iBeaconParser = new BeaconParser();
// Estimote > 2013
iBeaconParser.SetBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24");
_beaconManager.BeaconParsers.Add(iBeaconParser);
Log.Debug(TAG, "setting up background monitoring for beacons and power saving");
// wake up the app when a beacon is seen
_backgroundRegion = new Region("backgroundRegion", null, null, null);
regionBootstrap = new RegionBootstrap(this, _backgroundRegion);
// simply constructing this class and holding a reference to it in your custom Application
// class will automatically cause the BeaconLibrary to save battery whenever the application
// is not visible. This reduces bluetooth power usage by about 60%
backgroundPowerSaver = new BackgroundPowerSaver(this);
PerformHttpRequest();
}
public void DidDetermineStateForRegion(int state, AltBeaconOrg.BoundBeacon.Region region)
{
}
public async void PerformHttpRequest()
{
try
{
using (var client = new HttpClient())
{
var uri = "http://exampleuri";
var result = await client.GetStringAsync(uri);
var response = JsonConvert.DeserializeObject<BeaconURL>(result);
SendNotificationFromBeacon(response);
}
}
catch(Exception ex)
{
throw ex;
}
}
private void SendNotificationFromBeacon(BeaconURL receivedNotification)
{
// Setup an intent for SecondActivity:
Intent notificationIntent = new Intent(this, typeof(NotificationActivity));
// Pass some information to SecondActivity:
notificationIntent.PutExtra("CompaignUrl", receivedNotification.CompaignUrl);
notificationIntent.PutExtra("MediaUrl", receivedNotification.MediaUrl);
notificationIntent.PutExtra("titleText", receivedNotification.Title);
notificationIntent.SetFlags(ActivityFlags.NewTask);
// Create a task stack builder to manage the back stack:
Android.App.TaskStackBuilder stackBuilder = Android.App.TaskStackBuilder.Create(this);
// Add all parents of SecondActivity to the stack:
stackBuilder.AddParentStack(Java.Lang.Class.FromType(typeof(NotificationActivity)));
// Push the intent that starts SecondActivity onto the stack:
stackBuilder.AddNextIntent(notificationIntent);
// Obtain the PendingIntent for launching the task constructed by
// stackbuilder. The pending intent can be used only once (one shot):
const int pendingIntentId = 0;
PendingIntent pendingIntent =
stackBuilder.GetPendingIntent(pendingIntentId, PendingIntentFlags.OneShot);
// Instantiate the builder and set notification elements, including
// the pending intent:
var builder =
new NotificationCompat.Builder(this)
.SetContentTitle(receivedNotification.Title)
.SetContentText(receivedNotification.Text)
.SetSmallIcon(Android.Resource.Drawable.IcDialogInfo);
// Build the notification:
Notification notification = builder.Build();
// Get the notification manager:
NotificationManager notificationManager =
GetSystemService(Context.NotificationService) as NotificationManager;
// Publish the notification:
const int notificationId = 0;
notificationManager.Notify(notificationId, notification);
}
}
BeaconURL is a POCO class
NotificationActivity is a basic Activity class.
I perform the HttpClient request and get data. I create a notification and present it on my screen. It goes like this
Now when I tap on the notification, I dont go to the NotificationActivity. I am trying to invoke an activity from an ApplicationClass. Is this the right way to perform such stuff. Kindly provide details.
Thanks.
Edit: Added NotificationActivity Class
[Activity(Label = "NotificationActivity")]
public class NotificationActivity : MainActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your application here
SetContentView(Resource.Layout.NotificationLayout);
TextView titleTextView = FindViewById<TextView>(Resource.Id.txtTitle);
titleTextView.Text = Intent.Extras.GetString("titleText", "");
ImageView mediaImage = FindViewById<ImageView>(Resource.Id.imgViewMedia);
mediaImage.SetImageBitmap(GetImageBitmapFromUrl(Intent.Extras.GetString("MediaUrl", "")));
}
private Bitmap GetImageBitmapFromUrl(string url)
{
Bitmap imageBitmap = null;
using (var webClient = new WebClient())
{
var imageBytes = webClient.DownloadData(url);
if (imageBytes != null && imageBytes.Length > 0)
{
imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
}
}
return imageBitmap;
}
}
First thing you need to do is to set the your pending intent within the notification builder, it will get your NotificationActivity launching:
var builder =
new NotificationCompat.Builder(this)
.SetContentTitle("receivedNotification.Title")
.SetContentText("receivedNotification.Text")
.SetSmallIcon(Android.Resource.Drawable.IcDialogInfo)
.SetContentIntent(pendingIntent);
The second will be to get your back stack setup, from what you posted I'm not sure what the flow should be as the user will exit the app if they use the back button.
If you want the user to go back to the MainActivity when that press the back button, then you can add a ParentActivity to your NotificationActivity activity attribute, i.e.:
[Activity(Label = "NotificationActivity", ParentActivity = typeof(MainActivity))]
And thus the line:
stackBuilder.AddParentStack(Java.Lang.Class.FromType(typeof(NotificationActivity)));
Would add the MainActivity to the back stack.

TcpListener handle of multiple clients

I created MyListener which will start listening (using TcpListener) on his own thread upon creation. the TcpListener should handle multiple clients so i am running inside infinte while and handle each client in special task.
this is my code:
public class MyListener
{
public event EventHandler<MessageEventArgs> MessageReceived;
public MyListener()
{
var thread = new Thread(Listen);
thread.Start();
}
private void Listen()
{
TcpListener server = null;
try
{
server = new TcpListener(IPAddress.Any, 8977);
server.Start();
while (true)
{
var client = server.AcceptTcpClient();
Task.Run(() =>
{
try
{
var msg = GetMessageFromClient(client);
MessageReceived?.Invoke(this, new MessageEventArgs { Message = msg });
}
catch (Exception)
{
}
finally
{
client.Close();
}
});
}
}
catch (Exception)
{
}
finally
{
if (server != null)
server.Stop();
}
}
private string GetMessageFromClient(TcpClient client)
{
var bytes = new byte[client.ReceiveBufferSize];
var stream = client.GetStream();
var i = stream.Read(bytes, 0, bytes.Length);
var message = Encoding.UTF8.GetString(bytes, 0, i);
return message;
}
}
here are my questions:
how can i ensure that the task handle the client will use the client i pass to it when i start the task and not different client (becuase after the task start we return to the AcceptTcpClient method and may get new client)
in my example and with multiple clients handled by the same method ("GetMessageFromClient") do i need to put some kind of locking on this
method?

How to implement a distributed priority queue without using Zookeeper?

I want to implement a distributed priority queue without using Zookeeper?
If you know how to communicate between client and server (e.g. with TCP sockets) it should be straightforward. The server contains a thread safe implementation of the Priority Queue, hence providing an "interface". Clients connect to the server and uses this "interface".
Server
The server must provide a priority queue interface (i.e. supporting add, peek, poll, ...). Important is that these methods must be thread safe ! So we will use PriorityBlockingQueue (which is synchronized) instead of PriorityQueue.
public class Server {
private static ServerSocket server_skt;
public PriorityBlockingQueue<Integer> pq;
// Constructor
Server(int port, int pq_size) {
server_skt = new ServerSocket(port);
this.pq = new PriorityBlockingQueue<Integer>(pq_size);
}
public static void main(String argv[]) {
Server server = new Server(5555, 20); // Make server instance
while(true) {
// Always wait for new clients to connect
try {
System.out.println("Waiting for a client to connect...");
// Spawn new thread for communication with client
new CommunicationThread(server_skt.accept(), server.pq).start();
} catch(IOException e) {
System.out.println("Exception occured :" + e.getStackTrace());
}
}
}
}
And this is how CommunicationThread class would look like
public class CommunicationThread extends Thread {
private Socket client_socket;
private InputStream client_in;
private OutputStream client_out;
private PriorityBlockingQueue<Integer> pq;
public CommunicationThread(Socket socket, PriorityBlockingQueue<Integer> pq) {
try {
this.client_socket = socket;
this.client_in = client_socket.getInputStream();
this.client_out = client_socket.getOutputStream();
this.pq = pq;
System.out.println("Client connected : " + client_socket.getInetAddress().toString());
} catch(IOException e) {
System.out.println("Could not initialize communication properly. -- CommunicationThread.\n");
}
}
#Override
public void run() {
boolean active = true;
while(active) {
int message_number = client_in.read(); // Listen for next integer --> dispatch to correct method
switch(message_number) {
case -1: case 0:
// Will stop the communication between client and server
active = false;
break;
case 1:
// Add
int element_to_add = client_in.read(); // read element to add to the priority queue
pq.add(element_to_add); // Note that a real implementation would send the answer back to the client
break;
case 2:
// Poll (no extra argument to read)
int res = pq.poll();
// Write result to client
client_out.write(res);
client_out.flush();
break;
/*
* IMPLEMENT REST OF INTERFACE (don't worry about synchronization, PriorityBlockingQueue methods are already thread safe)
*/
}
}
client_in.close();
client_out.close();
}
}
This class is listening to what the client is sending.
According to what the client sent, the server knows what to do, hence there is a mini protocol. That protocol is : when the client wants to invoke a method of the distributed priority queue, he sends an integer (e.g. 2 = poll()). The server reads that integer and knows which method to invoke.
Note that sometimes sending one integer is enough (see poll() example), but not always. Think for example of add() which has to specify an argument. The server will receive 1 from the client (i.e. add()) and will read a second integer (or any other object that has to be stored in the distributed priority queue).
Client
Based on the protocol, the server is offering the client an interface (e.g. 0 = stop communication, 1 = add() , ...). The client only has to connect to the server and send messages (respecting the procotol!) to it.
A client example :
public class PQ_Client {
private static Socket skt;
private InputStream in;
private OutputStream out;
private final int _STOP_ = 0, _ADD_ = 1, _POLL_ = 2; // By convention (protocol)
PQ_Client(String ip, int port) {
try {
this.skt = new Socket(ip, port);
this.in = skt.getInputStream();
this.out = skt.getOutputStream();
System.out.println("Connected to distributed priority queue.");
} catch(IOException e) {
System.out.println("Could not connect with the distributed priority queue : " + e.getStackTrace());
}
}
// Sort of stub functions
public void stop() {
out.write(_STOP_);
out.flush();
out.close();
}
public void add(Integer el) {
out.write(_ADD_); // Send wanted operation
out.write(el); // Send argument element
// Real implementation would listen for result here
out.flush();
}
public int poll() {
out.write(_POLL_);
out.flush();
// Listen for answer
return in.read();
}
/*
* Rest of implementation
*/
}
Note that thanks to these self made "stub functions" we can make a PQ_Client object and use it as if it was a priority queue (the client/server communication is hidden behind the stubs).
String ip = "...";
int port = 5555;
PQ_Client pq = new PQ_Client(ip , port);
pq.add(5);
pq.add(2);
pq.add(4);
int res = pq.poll();
Note that by using RPC (Remote Procedure Call) it could be easier (stub function generated automatically, ...).
In fact what we implemented above is a little RPC-like mechanism, as it does nothing else then sending a message to call a procedure (e.g. add()) on the server, serializing the result (not needed for integers), send it back to the client.

Unity3d server auto discover, LAN only

I need to implement a game with a server in a local network, with no Internet access.
Provided that I can handle the connections and network communication between clients and server if they are connected, I'd like to know if there's a elegant way to make clients discover server's address at startup.
I'm providing my approach as an answer, in order to share the knowledge, but it would be nice to see if there is a more elegant/automatic approach.
Clients will use a specific port to connect to the game server. I implemented a UDP multicast in a different port so clients can get server's IP.
The following code is for both server and client, written in Unity Javascript. On the server side, it will start sending multicast messages every second at port 5100. Clients will listen to the same port, until they detect a new message. Then they identify sender's IP and establish a client-server connection the Unity3d way.
private var server_port : int = 5000;
private var server_ip : String;
// multicast
private var startup_port : int = 5100;
private var group_address : IPAddress = IPAddress.Parse ("224.0.0.224");
private var udp_client : UdpClient;
private var remote_end : IPEndPoint;
function Start ()
{
// loaded elsewhere
if (station_id == "GameServer")
StartGameServer ();
else
StartGameClient ();
}
function StartGameServer ()
{
// the Unity3d way to become a server
init_status = Network.InitializeServer (10, server_port, false);
Debug.Log ("status: " + init_status);
StartBroadcast ();
}
function StartGameClient ()
{
// multicast receive setup
remote_end = IPEndPoint (IPAddress.Any, startup_port);
udp_client = UdpClient (remote_end);
udp_client.JoinMulticastGroup (group_address);
// async callback for multicast
udp_client.BeginReceive (new AsyncCallback (ServerLookup), null);
MakeConnection ();
}
function MakeConnection ()
{
// continues after we get server's address
while (!server_ip)
yield;
while (Network.peerType == NetworkPeerType.Disconnected)
{
Debug.Log ("connecting: " + server_ip +":"+ server_port);
// the Unity3d way to connect to a server
var error : NetworkConnectionError;
error = Network.Connect (server_ip, server_port);
Debug.Log ("status: " + error);
yield WaitForSeconds (1);
}
}
/******* broadcast functions *******/
function ServerLookup (ar : IAsyncResult)
{
// receivers package and identifies IP
var receiveBytes = udp_client.EndReceive (ar, remote_end);
server_ip = remote_end.Address.ToString ();
Debug.Log ("Server: " + server_ip);
}
function StartBroadcast ()
{
// multicast send setup
udp_client = UdpClient ();
udp_client.JoinMulticastGroup (group_address);
remote_end = IPEndPoint (group_address, startup_port);
// sends multicast
while (true)
{
var buffer = Encoding.ASCII.GetBytes ("GameServer");
udp_client.Send (buffer, buffer.Length, remote_end);
yield WaitForSeconds (1);
}
}
Attaching this to your GameObject should do the trick.
Here is a c# version
Thanks
using System.Collections;
using UnityEngine;
using System.Net.Sockets;
using System;
using System.Net;
using System.Text;
public class OwnNetworkManager : MonoBehaviour
{
private int server_port = 5000;
private string server_ip;
// multicast
private int startup_port = 5100;
private IPAddress group_address = IPAddress.Parse("127.0.0.1");
private UdpClient udp_client ;
private IPEndPoint remote_end ;
void Start()
{
// loaded elsewhere
if (Loader.IsPC)
StartGameServer();
else
StartGameClient();
}
void StartGameServer()
{
// the Unity3d way to become a server
NetworkConnectionError init_status = Network.InitializeServer(10,
server_port, false);
Debug.Log("status: " + init_status);
StartCoroutine(StartBroadcast());
}
void StartGameClient()
{
// multicast receive setup
remote_end = new IPEndPoint(IPAddress.Any, startup_port);
udp_client = new UdpClient(remote_end);
udp_client.JoinMulticastGroup(group_address);
// async callback for multicast
udp_client.BeginReceive(new AsyncCallback(ServerLookup), null);
StartCoroutine(MakeConnection());
}
IEnumerator MakeConnection()
{
// continues after we get server's address
while (string.IsNullOrEmpty(server_ip))
yield return null;
while (Network.peerType == NetworkPeerType.Disconnected)
{
Debug.Log("connecting: " + server_ip + ":" + server_port);
// the Unity3d way to connect to a server
NetworkConnectionError error ;
error = Network.Connect(server_ip, server_port);
Debug.Log("status: " + error);
yield return new WaitForSeconds (1);
}
}
/******* broadcast functions *******/
void ServerLookup(IAsyncResult ar)
{
// receivers package and identifies IP
var receiveBytes = udp_client.EndReceive(ar, ref remote_end);
server_ip = remote_end.Address.ToString();
Debug.Log("Server: " + server_ip);
}
IEnumerator StartBroadcast()
{
// multicast send setup
udp_client = new UdpClient();
udp_client.JoinMulticastGroup(group_address);
remote_end = new IPEndPoint(group_address, startup_port);
// sends multicast
while (true)
{
var buffer = Encoding.ASCII.GetBytes("GameServer");
udp_client.Send(buffer, buffer.Length, remote_end);
yield return new WaitForSeconds (1);
}
}
}

Resources