Overview
Examples
Screenshots
Comparisons
Applications
Download
Documentation
Tutorials
Bazaar
Status & Roadmap
FAQ
Authors & License
Forums
Funding Ultimate++
Search on this site
Search in forums












SourceForge.net Logo
Home » U++ Library support » U++ Core » Convert struct to string and reconstruct a struct from string
Convert struct to string and reconstruct a struct from string [message #55321] Fri, 30 October 2020 02:35 Go to next message
sinpeople is currently offline  sinpeople
Messages: 29
Registered: October 2020
Location: Singapore
Promising Member

Hi folks,

I have a client and server which communicates via UDP. Ideally, the client converts one struct to strings and sent it to server with its client id and a command id. The message would be like "ClientID, MessageID, Strings converted from a struct". The server side picks up the message and from the messageID, it knows which struct to be used to recover its content from the remaining portion of the message, at the server side.

In case that I have roughly about 100+ such structs, how do I construct this portion to avoid a huge switch case to make the program lean with current available resources in U++?

Please point me to the right direction. I am very new to U++.

Thank you very much!


Best Regards
David
Re: Convert struct to string and reconstruct a struct from string [message #55322 is a reply to message #55321] Fri, 30 October 2020 09:33 Go to previous messageGo to next message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
sinpeople wrote on Fri, 30 October 2020 02:35
Hi folks,

I have a client and server which communicates via UDP. Ideally, the client converts one struct to strings and sent it to server with its client id and a command id. The message would be like "ClientID, MessageID, Strings converted from a struct". The server side picks up the message and from the messageID, it knows which struct to be used to recover its content from the remaining portion of the message, at the server side.

In case that I have roughly about 100+ such structs, how do I construct this portion to avoid a huge switch case to make the program lean with current available resources in U++?

Please point me to the right direction. I am very new to U++.

Thank you very much!


Best Regards
David


As this sounds like you have both server and clinet under your control, I would say that binary serialization here makes the sense.

One question that remains is about what you are going to do with that struct then...

Either way, I think where this leads is that you will have Serialize method in all of your structs. While it is probably not only way how to do things, I think that in this case it will be reasonable to have some base class for your structs and Serialize will then be virtual.

struct AMessage {
   virtual void Serialize(Stream& s) = 0;
};

struct TemperatureMessage {
   double altitude, temperature;

   virtual void Serialize(Stream& s) {
       s % altitude % temperature;
   }
}


Then I can imagine you will have a map somewhere to create the specific struct on demand:

VectorMap<int, void (*make)(One<AMessage>& m)> message_maker;

template <class T>
void RegisterMessage(int messageid)
{
    message_maker.Add(messageid, [](One<AMessage>& m) { m.Create<T>(); });
}

INITBLOCK {
   RegisterMessage<TemperatureMessage>(); // do that for all of your messages
};


then when processing the intput

void ProcessRequest(const String& data)
{
   StringStream ss(data); // error handling for now omitted
   int client_id = ss.GetInt32();
   int message_id = ss.GetInt32();
   One<AMessage> m;
   int q = message_maker.Find(message_id);
   if(q < 0)
      return;
   (*message_maker[q])(m); // create the required concrete message
   ss % *m; // load data to struct
}


Of course, this all is based on very little info that you have provided...

Mirek
Re: Convert struct to string and reconstruct a struct from string [message #55325 is a reply to message #55322] Fri, 30 October 2020 12:51 Go to previous messageGo to next message
Didier is currently online  Didier
Messages: 680
Registered: November 2008
Location: France
Contributor
I don't know if this simple example is in tutorial, but I think it has it's place
The use of One<> (for creation and ownership) makes code very small.

I'm not sure so many Upp users would rapidly think about using One<> (At least i woudn't since I rarely use it and tend to forget about it Very Happy )
Re: Convert struct to string and reconstruct a struct from string [message #55326 is a reply to message #55325] Fri, 30 October 2020 13:14 Go to previous messageGo to next message
Didier is currently online  Didier
Messages: 680
Registered: November 2008
Location: France
Contributor
After trying it out,

here is the same exmaple with small compilation corrections (compiles on Clang linux)

#include <Core/Core.h>



namespace Upp {
	
struct AMessage {
   virtual void Serialize(Stream& s) = 0;
   virtual ~AMessage() {}
};

typedef Function< void (One<AMessage>&) > MessageMake;

VectorMap<int, MessageMake> message_maker;

template <class T>
void RegisterMessage(int messageid)
{
    message_maker.Add(messageid, [](One<AMessage>& m) { m.Create<T>(); });
}


// =============================
//     Messages definition
// =============================
struct TemperatureMessage : AMessage {
   double altitude, temperature;

   virtual void Serialize(Stream& s) {
       s % altitude % temperature;
   }
};


struct WarningMessage : AMessage {
   String text;

   virtual void Serialize(Stream& s) {
       s % text;
   }
};



// =============================
//     Message registeration
// =============================


INITBLOCK {
   RegisterMessage<TemperatureMessage>(1); // do that for all of your messages
   RegisterMessage<WarningMessage>(2); // do that for all of your messages
};




void ProcessRequest(const String& data)
{
   StringStream ss(data); // error handling for now omitted
   int client_id = ss.Get32();
   int message_id = ss.Get32();
   One<AMessage> m;
   int q = message_maker.Find(message_id);
   if(q < 0)
      return;
   (message_maker[q])(m); // create the required concrete message
   ss % *m; // load data to struct
}

}
using namespace Upp;

CONSOLE_APP_MAIN
{
}
Re: Convert struct to string and reconstruct a struct from string [message #55335 is a reply to message #55326] Sat, 31 October 2020 16:43 Go to previous messageGo to next message
sinpeople is currently offline  sinpeople
Messages: 29
Registered: October 2020
Location: Singapore
Promising Member

+Mirek

Thank you very much for this great example. It did really broaden my horizon in terms of C++ knowledge as a newbie.

Now I am having difficulties in handing the binary message for sending and receiving; The sending/receiving has been verified. Only the data format seems not very correct.

void LocalCtrl::RpcRequest()
{
	TrafficMessage m;
	m.traffic = "Lots of Traffic";
	String data = StoreAsString(m);
	SendCmd(Traffic, data);
}

void LocalCtrl::SendCmd(enum MessageIDs msgID, String data)
{
	ClientUDPHead udpHead(local_cfg.nID, regional_cfg.strIP, regional_cfg.nPort);

	UdpRpcCmd(udpHead, msgID, data);
}



The "UdpRpcCmd" will eventually calls the following function to send message to server side
	void UdpCmd(ClientUDPHead head, enum MessageIDs msgID, String data)
	{
		UrrClient urr;
		urr.SetServer(head.strDestIP, head.nDestPort);

		int tm = GetTickCount();
		String strCmd = Format("%d%d%s", head.clientID, msgID, data);
		
		strCmd = urr.Call(strCmd);
		int tm2 = GetTickCount();
		
		String strMsg;
		if(strCmd.GetCount())
		{
			strMsg = Format("Request: %s, Response: %s in %d ms", data, strCmd, tm2-tm);
		}
		else
		{
			strMsg = Format("Request: %s, Time out!", data);
		}

		//Do something account to RpcCmd request;
		notify->OnReplyUdpRpcCmd(strMsg, (int) Random(500), Format(GetSysTime())); //notify result
	}



I need to combine three things together before sending it out. Initially I used this one to combine the strings together.
String strCmd = Format("%d%d%s", head.clientID, msgID, data);


The server side can pick up the messages correctly. But I failed to extract them properly with the sample code below
	for(;;) 
	{
		UrrRequest r;
		if(urr.Accept(r)) 
		{
			StringStream ss(~r); // error handling for now omitted
   			int client_id = ss.Get32();
   			int message_id = ss.Get32();
   			One<AMessage> m;
   			int q = message_maker.Find(message_id);
   			if(q < 0)
      			return;
   			(message_maker[q])(m); // create the required concrete message
   			ss % *m; // load data to struct
/*
			Vector<String> tokens = Split(~r, [](int c) { return c == ':' || c == '\t' || c == ' ' || c == ',' || c == '.' ? 1 : 0; });
			
			if(tokens.GetCount()>=2) // local_ctrl.nID + Command ID;
			{
				int nFind = ctrl.Find(tokens[0]);
				if(nFind != -1)
				{
					ctrl[nFind]->UdpRpcCmd(r);
				}
			}
			*/
		}
	}

Both the client_id and message_id are very big numbers. In fact, the clicen_id is an int and message_id is an enum which starts from 1;

How to combine 2 or more strings and separate them properly after network transmission in this case?

Thank you so much!


David WANG

[Updated on: Sat, 31 October 2020 16:46]

Report message to a moderator

Re: Convert struct to string and reconstruct a struct from string [message #55342 is a reply to message #55335] Sun, 01 November 2020 15:39 Go to previous message
mirek is currently offline  mirek
Messages: 13975
Registered: November 2005
Ultimate Member
sinpeople wrote on Sat, 31 October 2020 16:43
+Mirek

Thank you very much for this great example. It did really broaden my horizon in terms of C++ knowledge as a newbie.

Now I am having difficulties in handing the binary message for sending and receiving; The sending/receiving has been verified. Only the data format seems not very correct.

void LocalCtrl::RpcRequest()
{
	TrafficMessage m;
	m.traffic = "Lots of Traffic";
	String data = StoreAsString(m);
	SendCmd(Traffic, data);
}

void LocalCtrl::SendCmd(enum MessageIDs msgID, String data)
{
	ClientUDPHead udpHead(local_cfg.nID, regional_cfg.strIP, regional_cfg.nPort);

	UdpRpcCmd(udpHead, msgID, data);
}



The "UdpRpcCmd" will eventually calls the following function to send message to server side
	void UdpCmd(ClientUDPHead head, enum MessageIDs msgID, String data)
	{
		UrrClient urr;
		urr.SetServer(head.strDestIP, head.nDestPort);

		int tm = GetTickCount();
		String strCmd = Format("%d%d%s", head.clientID, msgID, data);
		
		strCmd = urr.Call(strCmd);
		int tm2 = GetTickCount();
		
		String strMsg;
		if(strCmd.GetCount())
		{
			strMsg = Format("Request: %s, Response: %s in %d ms", data, strCmd, tm2-tm);
		}
		else
		{
			strMsg = Format("Request: %s, Time out!", data);
		}

		//Do something account to RpcCmd request;
		notify->OnReplyUdpRpcCmd(strMsg, (int) Random(500), Format(GetSysTime())); //notify result
	}



I need to combine three things together before sending it out. Initially I used this one to combine the strings together.
String strCmd = Format("%d%d%s", head.clientID, msgID, data);


The server side can pick up the messages correctly. But I failed to extract them properly with the sample code below
	for(;;) 
	{
		UrrRequest r;
		if(urr.Accept(r)) 
		{
			StringStream ss(~r); // error handling for now omitted
   			int client_id = ss.Get32();
   			int message_id = ss.Get32();
   			One<AMessage> m;
   			int q = message_maker.Find(message_id);
   			if(q < 0)
      			return;
   			(message_maker[q])(m); // create the required concrete message
   			ss % *m; // load data to struct
/*
			Vector<String> tokens = Split(~r, [](int c) { return c == ':' || c == '\t' || c == ' ' || c == ',' || c == '.' ? 1 : 0; });
			
			if(tokens.GetCount()>=2) // local_ctrl.nID + Command ID;
			{
				int nFind = ctrl.Find(tokens[0]);
				if(nFind != -1)
				{
					ctrl[nFind]->UdpRpcCmd(r);
				}
			}
			*/
		}
	}

Both the client_id and message_id are very big numbers. In fact, the clicen_id is an int and message_id is an enum which starts from 1;

How to combine 2 or more strings and separate them properly after network transmission in this case?

Thank you so much!


David WANG


I think you are mixing text interpretation (Format) and binary one (ss.Get32). Use StringStream on both sides..

Mirek
Previous Topic: Looking for examples to split string into container
Next Topic: What does this compiling error mean exactly?
Goto Forum:
  


Current Time: Thu Mar 28 21:14:48 CET 2024

Total time taken to generate the page: 0.01800 seconds