I use this packet class (.dll) for my multithreaded client-server application.
BUT I don't know how to send strings with my code - pls help:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using System.Runtime.Serialization.Formatters.Binary;
using System.Net.NetworkInformation;
namespace ServerData
{
[Serializable]
public class Packet
{
public List<string> Gdata;
public int packetInt;
public bool packetBool;
public string senderID;
public PacketType packetType;
public Packet(PacketType type, string senderID)
{
Gdata = new List<string>();
this.senderID = senderID;
this.packetType = type;
}
public Packet(byte[] packetbytes)
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream(packetbytes);
Packet p = (Packet)bf.Deserialize(ms);
ms.Close();
this.Gdata = p.Gdata;
this.packetInt = p.packetInt;
this.packetType = p.packetType;
this.senderID = p.senderID;
this.packetBool = p.packetBool;
}
public byte[] ToBytes()
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, this);
byte[] bytes = ms.ToArray();
ms.Close();
return bytes;
}
public static string GetIP4Address()
{
IPAddress[] ips = Dns.GetHostAddresses(Dns.GetHostName());
foreach (IPAddress i in ips)
{
if (i.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
return i.ToString();
}
}
return "127.0.0.1";
}
public static string GetIP6Address()
{
string macAddresses = string.Empty;
foreach (NetworkInterface nic in NetworkInterface.GetAllNetworkInterfaces())
{
if (nic.OperationalStatus == OperationalStatus.Up)
{
macAddresses += nic.GetPhysicalAddress().ToString();
break;
}
}
return macAddresses;
}
}
public enum PacketType
{
Registration,
Command,
Download,
Inquiry,
Reponse,
Confirmation
}
}
the send function in the server/client:
public void SendRegistrationPacket() // for example this send an packet from the type registration but in this moment i can "know" whats in the packet by "asking" what type it is...
{
Packet p = new Packet(PacketType.Registration, id);
clientSocket.Send(p.ToBytes()); //clientSocket is the socket where the client is connected^^
}
the manager for the incoming data (not reading only handling the readed data):
void DataManager(Packet p)
{
switch (p.packetType)
{
case PacketType.Registration:
listBox1.Items.Add("Got Registration Packet");
listBox1.Items.Add("Sending Registration...");
break;
}
}
how can i send strings and "encoding" them with these packets?
This class provides a public List<string> Gdata;. You can add any number of strings to this object, and the serializer will take care of it. So e.g. you could write
Packet p = new Packet(PacketType.Command, 123);
p.Gdata.Add("SomeString which I want to execute");
clientSocket.Send(p.ToBytes());
On the receiving end, you will deserialize the Packet object and use the Gdata field as usual.
byte[] received = new byte[1024];
int num_received = someSocket.Receive(received);
Packet decoded = new Packet(received.Take(num_received).ToArray());
Console.WriteLine("The first string in the list: " + decoded.Gdata[0]);
Related
I am creating a multithread server and I am using mutex to control the sending data, the problem is when a client disconnect all the others clients also disconnect.
This is the server code
using Newtonsoft.Json;
using Pastel;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.AccessControl;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace DeepestSwordServer
{
public class Program
{
public static TcpListener server;
public static TcpClient client = new TcpClient();
public static Thread t;
public static Mutex mutex = new Mutex(false, "poligamerMod");
public static List<Connection> list = new List<Connection>();
public static Connection con;
static void Main(string[] args)
{
string ConfigPath = Path.Combine(Environment.CurrentDirectory, "config.json");
if (File.Exists(ConfigPath))
{
Console.WriteLine("Starting Server...".Pastel(Color.FromArgb(165, 229, 250)));
try
{
string json = File.ReadAllText(ConfigPath);
Config config = JsonConvert.DeserializeObject<Config>(json);
Start(config.Ip, config.Port);
}
catch (Exception e)
{
Console.WriteLine($"Failed To Start Server Error: {e.Message}".Pastel(Color.FromArgb(255, 0, 0)));
}
}
else
{
Console.WriteLine("Creating Config File...".Pastel(Color.FromArgb(165, 229, 250)));
Config config = new Config();
config.Ip = "0.0.0.0";
config.Port = 3878;
string json = JsonConvert.SerializeObject(config);
File.Create(ConfigPath).Dispose();
File.WriteAllText(ConfigPath, json);
Console.WriteLine("File Created!".Pastel(Color.FromArgb(58, 255, 0)));
Console.WriteLine("Please see the config.json where is the ip, port and password if needed of the server, you need to restart the server!".Pastel(Color.FromArgb(165, 229, 250)));
Console.ReadLine();
}
}
public static void Start(string ip, int port)
{
Console.WriteLine("Server Ready!".Pastel(Color.FromArgb(58, 255, 0)));
server = new TcpListener(IPAddress.Parse(ip), port);
server.Start();
while (true)
{
client = server.AcceptTcpClient();
con = new Connection();
con.stream = client.GetStream();
con.streamr = new StreamReader(con.stream);
con.streamw = new StreamWriter(con.stream);
con.username = con.streamr.ReadLine();
con.id = con.streamr.ReadLine();
Event #event = new Event();
#event.EventType = EventType.Join;
#event.Username = con.username;
#event.Id = con.id;
string join = JsonConvert.SerializeObject(#event);
foreach (Connection c in list)
{
c.streamw.WriteLine(join);
#event = new Event();
#event.EventType = EventType.Join;
#event.Username = c.username;
#event.Id = c.id;
string newJoin = JsonConvert.SerializeObject(#event);
con.streamw.WriteLine(newJoin);
}
list.Add(con);
#event = new Event();
#event.EventType = EventType.List;
foreach (Connection c in list.ToList())
{
#event.List.Add(c.username, c.id);
}
string playerList = JsonConvert.SerializeObject(#event);
con.streamw.WriteLine(playerList);
Console.WriteLine($"{con.username} with id {con.id} has connected!".Pastel(Color.FromArgb(58, 255, 0)));
t = new Thread(HearConnection);
t.Start();
}
}
public static void HearConnection()
{
Connection hcon = con;
do
{
try
{
foreach (Connection c in list.ToList())
{
mutex.WaitOne();
c.streamw.WriteLine(hcon.streamr.ReadLine());
mutex.ReleaseMutex();
}
}
catch (Exception e)
{
list.Remove(hcon);
Event #event = new Event();
#event.EventType = EventType.Leave;
#event.Username = con.username;
#event.Id = con.id;
string leave = JsonConvert.SerializeObject(#event);
foreach (Connection c in list)
{
c.streamw.WriteLine(leave);
}
Console.WriteLine($"{hcon.username} with id {hcon.id} has disconnected!".Pastel(Color.FromArgb(255, 0, 0)));
Console.WriteLine(e.ToString());
break;
}
} while (true);
}
public struct Connection
{
public NetworkStream stream;
public StreamWriter streamw;
public StreamReader streamr;
public string username;
public string id;
}
}
}
Here is the important parts of the client code
public void Connect()
{
MultiPlayerSubMenu.transform.GetChild(2).gameObject.GetComponent<Button>().enabled = false;
MultiPlayerSubMenu.transform.GetChild(2).GetChild(0).gameObject.GetComponent<TextMeshProUGUI>().text = "Connecting...";
try
{
client.Connect(IPAddress.Parse(Ip.GetComponent<InputField>().text), Convert.ToInt32(Port.GetComponent<InputField>().text));
if (client.Connected)
{
t = new Thread(Listen);
stream = client.GetStream();
streamw = new StreamWriter(stream);
streamr = new StreamReader(stream);
streamw.AutoFlush = true;
streamw.WriteLine(Username);
streamw.WriteLine(Id);
t.IsBackground = true;
t.Start();
StartCoroutine(Yes());
}
else
{
MultiPlayerSubMenu.transform.GetChild(2).GetChild(0).gameObject.GetComponent<TextMeshProUGUI>().text = "Cant Connect To Server!";
MultiPlayerSubMenu.transform.GetChild(2).gameObject.GetComponent<Image>().color = Color.red;
StartCoroutine(No());
}
}
catch (Exception e)
{
Logger.LogInfo(e.Message);
MultiPlayerSubMenu.transform.GetChild(2).GetChild(0).gameObject.GetComponent<TextMeshProUGUI>().text = "Cant Connect To Server!";
MultiPlayerSubMenu.transform.GetChild(2).gameObject.GetComponent<Image>().color = Color.red;
StartCoroutine(No());
}
}
public void Listen()
{
while (client.Connected)
{
try
{
string message = streamr.ReadLine();
DoEvent(message);
Logger.LogInfo(message);
}
catch(Exception e)
{
if (!client.Connected)
{
LostConnection = true;
}
else
{
Logger.LogError(e.ToString());
}
}
}
}
What I want is that the others clients dont disconnect if one client disconnect.
I know that this is a very discussed topic, but i'm facing a strange behavior from my code.
Making some tests I found that everything goes perfectly for some clients but others simply can't receive and send data through my custom Client design TCPClient based.
I can't confirm if this issue is caused by VPN or NATs connections although many Users can access my server from this connection types.
I' ve been reading about TCPClient and TCPListener in .Net and searching issues like mine but no luck. Everything I got are poor examples.
I will appreciate any help, even book references for network advanced programming.
Here is my code. Most stable up to date
Cliente.cs
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.Serialization.Formatters.Binary;
using System.Linq;
using System.IO.Compression;
namespace netComunicator
{
public class Client:IDisposable
{
int BufferSize { get { return 16384; } }
/// <summary>
/// .NET TcpClient for receive and send methods
/// </summary>
public TcpClient NetClient = new TcpClient();
/// <summary>
/// Externl buffer to store incoming data
/// This buffers get clear when EOS secuence is present on it
/// </summary>
List<byte> ExternBuffer = new List<byte>();
/// <summary>
/// Remote port
/// </summary>
public string Port { get; set; }
/// <summary>
/// MAC address from client
/// </summary>
public string ClientMac { get; set; }
/// <summary>
/// Client IP
/// </summary>
public string ClientIp { get; set; }
/// <summary>
/// Cifrado
/// Use NETSID as public key and his first 128 bits as IV
/// </summary>
private NetCipher Cipher { get; set; }
/// <summary>
/// Shared network session id
/// </summary>
public string NETSID { get; set; }
#region delegados y eventos
public delegate void NoConnection(string noConnectedClient);
public delegate void ObjectDataReceived(object data, Client client);
public delegate void ClientDisconnected(Client client);
public event ClientDisconnected OnClientDisconnected;
public event ObjectDataReceived OnObjectDataReceived;
public event NoConnection OnFailConnectTo;
#endregion
public NetworkStream NetStream;
CancellationTokenSource CTS;
CancellationToken Token;
CancellationTokenSource CancelKAS;
CancellationToken CancelKASToken;
/// <summary>
/// Max lost count of KEEP_ALIVE
/// </summary>
const int MAX_KEEP_ALIVE_COUNT = 30;
/// <summary>
/// Current lost KEEP_ALIVE_SIGNAL
/// </summary>
int CURRENT_KEEP_ALIVE_SIGNAL_COUNT = 0;
public Client(byte[] NETSID)
{
CTS = new CancellationTokenSource();
Token = CTS.Token;
CancelKAS = new CancellationTokenSource();
CancelKASToken = CancelKAS.Token;
ClientMac = GetMac();
if (NETSID != null)
{
Cipher = new NetCipher(NETSID);
this.NETSID = new Guid(NETSID).ToString();
}
}
public void StartClient()
{
try
{
Task.Factory.StartNew(() => Receive(), Token);
}
catch { }
}
public void StopClient()
{
CTS.Cancel();
}
public void StartKeepAliveSignal()
{
Task.Factory.StartNew(() =>
{
while (!CancelKASToken.IsCancellationRequested)
{
if (CURRENT_KEEP_ALIVE_SIGNAL_COUNT == MAX_KEEP_ALIVE_COUNT)
{
OnClientDisconnected?.BeginInvoke(this, null, null);
Dispose();
}
Send("KEEP_ALIVE");
CURRENT_KEEP_ALIVE_SIGNAL_COUNT++;
Thread.Sleep(1000);
}
}, CancelKASToken);
}
public void StoptKeepAliveSignal()
{
CancelKAS.Cancel();
}
static string GetLocalIPAddress()
{
var host = Dns.GetHostEntry(Dns.GetHostName());
foreach (var ip in host.AddressList)
if (ip.AddressFamily == AddressFamily.InterNetwork)
return ip.ToString();
throw new Exception("No network adapters with an IPv4 address in the system!");
}
string GetMac()
{
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
var mac = nics.Where(n => n.OperationalStatus == OperationalStatus.Up).FirstOrDefault();
if (mac != null)
return mac.GetPhysicalAddress().ToString();
return "";
}
void CloseConnection()
{
NetClient.Close();
}
public void Dispose()
{
try
{
StoptKeepAliveSignal();
StopClient();
CloseConnection();
}
catch{ }
}
async public Task<bool> Connect(string ip, int port, bool initKAS = true)
{
return await Task.Factory.StartNew(() =>
{
try
{
NetClient.Connect(ip, port);
Guid guid = new Guid(NETSID);
if (NetStream == null)
{
NetStream = NetClient.GetStream();
Cipher = new NetCipher(guid.ToByteArray());
}
List<byte> initdata = new List<byte>();
initdata.AddRange(NetComTools.wellcomeMsg);
initdata.AddRange(System.Text.Encoding.ASCII.GetBytes(ClientMac));
initdata.AddRange(guid.ToByteArray());
NetStream.Write(initdata.ToArray(), 0, initdata.Count);
StartClient();
// if required KEEP_ALIVE_SIGNAL on
if (initKAS)
StartKeepAliveSignal();
ClientIp = ip;
return true;
}
catch
{
// Notificar que no se pudo establecer conexion
OnFailConnectTo?.Invoke(ip);
}
return false;
});
}
public void Receive()
{
while (!Token.IsCancellationRequested)
{
try
{
byte[] buff = new byte[BufferSize];
int bytesReaded = NetStream.Read(buff, 0, buff.Length);
byte[] bytesFromBuffer = buff.Take(bytesReaded).ToArray();
CURRENT_KEEP_ALIVE_SIGNAL_COUNT = 0;
ExternBuffer.AddRange(bytesFromBuffer);
// Determine if current received bytes have End Of Secuence byte array
bool isEOS = IsEndOfSecuence(ExternBuffer);
if (isEOS)
{
// Secuence separator takes inside EOS and passing ExternBuffer
// split current buffer in N subsecuences.
// This because in one buffer read, can be more than one message delimited by EOS
List<byte[]> secuences = SecuenceSeparator(ExternBuffer);
ExternBuffer.Clear();
foreach (byte[] secuence in secuences)
{
byte[] decompress = Decompress(secuence);
byte[] decipher = Cipher.Decrypt(decompress);
object data = Serializer.Deserialize(decipher);
OnObjectDataReceived?.BeginInvoke(data, this, null, null);
}
}
}
catch
{
Dispose();
OnClientDisconnected?.Invoke(this);
return;
}
Thread.Sleep(10);
}
}
/// <summary>
/// If bytes[] have an EOS secuence return true
/// </summary>
bool IsEndOfSecuence(byte[] bytes)
{
int secuencelen = bytes.Length - 1;
int checkSum = NetComTools.EOS.Length - 1;
int EOSLen = checkSum;
int trys = 0;
for (int i = secuencelen; i >= 0; i--)
{
if (trys <= EOSLen)
{
if (checkSum == 0)
return true;
if (bytes[i] == NetComTools.EOS[checkSum])
checkSum--;
else
checkSum = NetComTools.EOS.Length - 1;
}
else
return false;
trys++;
}
return false;
}
bool IsEndOfSecuence(List<byte> bytes)
{
int secuencelen = bytes.Count - 1;
int checkSum = NetComTools.EOS.Length - 1;
int EOSLen = checkSum;
int trys = 0;
for (int i = secuencelen; i >= 0; i--)
{
if (trys <= EOSLen)
{
if (checkSum == 0)
return true;
if (bytes[i] == NetComTools.EOS[checkSum])
checkSum--;
else
checkSum = NetComTools.EOS.Length - 1;
}
else
return false;
trys++;
}
return false;
}
List<byte[]> SecuenceSeparator(byte[] data)
{
List<byte[]> subsecuences = new List<byte[]>();
List<byte> externBuff = new List<byte>(data);
int inc = 0;
int secuencelen = externBuff.Count;
int lastpos = 0;
//40 = 15 + datos, 5 + eos, 15 + datos, 5 + eos
for (int i = 0; i < secuencelen; i++)
{
if (externBuff[i] == NetComTools.EOS[inc])
{
inc++;
if (inc == NetComTools.EOS.Length)
{
List<byte> sub_nsecuence = new List<byte>();
int elements = i + 1 - lastpos - NetComTools.EOS.Length;
byte[] matchsecuence = new byte[elements];
externBuff.CopyTo(lastpos, matchsecuence, 0, elements);
sub_nsecuence.AddRange(matchsecuence);
subsecuences.Add(sub_nsecuence.ToArray());
inc = 0;
lastpos = i + 1;
}
}
else
inc = 0;
}
return subsecuences;
}
List<byte[]> SecuenceSeparator(List<byte> externBuff)
{
List<byte[]> subsecuences = new List<byte[]>();
int inc = 0;
int secuencelen = externBuff.Count;
int lastpos = 0;
for (int i = 0; i < secuencelen; i++)
{
if (externBuff[i] == NetComTools.EOS[inc])
{
inc++;
if (inc == NetComTools.EOS.Length)
{
List<byte> sub_nsecuence = new List<byte>();
int elements = i + 1 - lastpos - NetComTools.EOS.Length;
byte[] matchsecuence = new byte[elements];
externBuff.CopyTo(lastpos, matchsecuence, 0, elements);
sub_nsecuence.AddRange(matchsecuence);
subsecuences.Add(sub_nsecuence.ToArray());
inc = 0;
lastpos = i + 1;
}
}
else
inc = 0;
}
return subsecuences;
}
public void Send(object data)
{
try
{
byte[] objDatabyte = Serializer.Serialize(data);
byte[] cipher = Cipher.Encrypt(objDatabyte);
byte[] compressed = Compress(cipher);
NetStream.Write(compressed, 0, compressed.Length);
NetStream.Write(NetComTools.EOS, 0, NetComTools.EOS.Length);
}
catch
{
OnClientDisconnected?.Invoke(this);
Dispose();
}
}
public static byte[] Compress(byte[] raw)
{
using (MemoryStream msw = new MemoryStream())
{
using (GZipStream gzip = new GZipStream(msw, CompressionMode.Compress))
gzip.Write(raw, 0, raw.Length);
return msw.ToArray();
}
}
public static byte[] Decompress(byte[] gzipraw)
{
using (GZipStream msw = new GZipStream(new MemoryStream(gzipraw), CompressionMode.Decompress))
{
using (MemoryStream ms = new MemoryStream())
{
int readed;
while ((readed = msw.ReadByte()) != -1)
ms.WriteByte((byte)readed);
return ms.ToArray();
}
}
}
}
static class Serializer
{
public static byte[] Serialize(object anySerializableObject)
{
using (var memoryStream = new MemoryStream())
{
(new BinaryFormatter()).Serialize(memoryStream, anySerializableObject);
return memoryStream.ToArray();
}
}
public static object Deserialize(byte[] message)
{
using (var memoryStream = new MemoryStream(message))
{
BinaryFormatter b = new BinaryFormatter();
memoryStream.Position = 0;
return b.Deserialize(memoryStream);
}
}
}
}
Server.cs
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System.Threading;
using System.Linq;
namespace netComunicator
{
public class Server
{
/// <summary>
/// Connected Clients
/// </summary>
public ConcurrentBag<Client> Clients = new ConcurrentBag<Client>();
/// <summary>
/// .NET TcpListenet
/// </summary>
public TcpListener Host;
CancellationTokenSource CTS;
CancellationToken Token;
#region Delegados y Eventos
public delegate void ClientConnected(Client client);
public delegate void ClientDisconnected(Client client);
public event ClientConnected OnClientConnected;
public event ClientDisconnected OnClientDisconnected;
#endregion
public Server(int port)
{
CTS = new CancellationTokenSource();
Token = CTS.Token;
try
{
Host = new TcpListener(IPAddress.Parse(NetComTools.thisClientIP), port);
Host.Start();
try { Task.Factory.StartNew(() => AcceptIncoming(), Token); } catch (Exception ex) {
throw ex;
}
}
catch (Exception ec)
{
throw ec;
}
}
public void SendTo(Client client, object data)
{
Client cl = Clients.Where(c => c.Equals(client)).FirstOrDefault();
cl?.Send(data);
}
public void SendAll(object data)
{
Task.Factory.StartNew(() =>
{
foreach (Client client in Clients)
client.Send(data);
});
}
void AcceptIncoming()
{
while (!Token.IsCancellationRequested)
{
try
{
AsyncCallback acb = new AsyncCallback(PerformConnection);
Host.BeginAcceptSocket(acb, null);
Thread.Sleep(10);
}
catch (Exception ex)
{
throw ex;
}
}
}
void PerformConnection(IAsyncResult res)
{
TcpClient tempClient = null;
try
{
tempClient = Host.EndAcceptTcpClient(res);
NetworkStream ns;
ns = tempClient.GetStream();
byte[] initData = new byte[NetComTools.wellcomeMsg.Length + 28];
ns.Read(initData, 0, initData.Length);
byte[] wellcome = new byte[NetComTools.wellcomeMsg.Length];
byte[] mac = new byte[12];
byte[] netsidfromclient = new byte[16];
List<byte> ini = new List<byte>(initData);
ini.CopyTo(0, wellcome, 0, 4);
ini.CopyTo(4, mac, 0, 12);
ini.CopyTo(16, netsidfromclient, 0, 16);
if (WellcomeOk(wellcome))
{
Client newClient = new Client(netsidfromclient)
{
ClientIp = tempClient.Client.RemoteEndPoint.ToString().Split(':')[0],
Port = tempClient.Client.RemoteEndPoint.ToString().Split(':')[1],
NetStream = ns,
ClientMac = System.Text.Encoding.ASCII.GetString(mac),
};
Clients.Add(newClient);
newClient.NetClient = tempClient;
newClient.OnClientDisconnected += newClient_OnClientDisconnected;
newClient.StartClient();
OnClientConnected?.BeginInvoke(newClient, null, null);
}
else
tempClient.Close();
}
catch {if( tempClient != null) tempClient.Close(); return; }
}
bool WellcomeOk(byte[] wellcomeFromNet)
{
if (wellcomeFromNet.Length != NetComTools.wellcomeMsg.Length)
return false;
else
{
for (int i = 0; i < wellcomeFromNet.Length; i++)
if (wellcomeFromNet[i] != NetComTools.wellcomeMsg[i])
return false;
}
return true;
}
void newClient_OnClientDisconnected(Client client)
{
client.OnClientDisconnected -= newClient_OnClientDisconnected;
Clients = RemoveClientFromList(client);
OnClientDisconnected?.Invoke(client);
}
ConcurrentBag<Client> RemoveClientFromList(Client client)
{
ConcurrentBag<Client> Acl = new ConcurrentBag<Client>();
return new ConcurrentBag<Client>(Clients.Where(c => c != client));
}
public void StopListener()
{
Host.Stop();
CTS.Cancel();
}
}
}
I'm making a simple program that receive UDP Packets from another program and I want to cast this packet to a class.
I have a class :
public class Packet
{
public string MyFirstProperty {get; set;}
public string MySecondProperty {get; set;}
public string MyThirdProperty {get; set;}
public OtherObject[] ObjectArray {get; set}
}
The packet that I receive are bytes array. How can I transform the packet to a class. I've heard of marshalling but I'm not experienced enough to fully understand it.
What should I do.
Thank you.
To send an object from client to server (utilizing Json.Net) ; assuming you have already done your own research and have a working UDP client/server
Client site:
c1) Packet p = new Packet();
c2) //fill the properties
c3) string json = JsonConvert.SerializeObject(p);
c4) byte[] buf = Encoding.UTF8.GetBytes(json);
c5) Send *buf* to server
Server site:
s1) Receive data from client( call it *buf*)
s2) string json = Encoding.UTF8.GetString(buf); (must be equal to json at client site)
s3) Packet p = JsonConvert.DeserializeObject<Packet>(json);
s4) Tada........
Now you can use the same algorithm to send object from server to client
PS: As long as you can send and then receive the same byte array using UDP(c5 => s1), you can get the original object back.
You need to use Serialization and De-Serialization to convert back class objects from and to Byte Array.
Here is a sample example.
public class MyClass {
public int Id { get; set; }
public string Name { get; set; }
public byte[] Serialize() {
using (MemoryStream m = new MemoryStream()) {
using (BinaryWriter writer = new BinaryWriter(m)) {
writer.Write(Id);
writer.Write(Name);
}
return m.ToArray();
}
}
public static MyClass Desserialize(byte[] data) {
MyClass result = new MyClass();
using (MemoryStream m = new MemoryStream(data)) {
using (BinaryReader reader = new BinaryReader(m)) {
result.Id = reader.ReadInt32();
result.Name = reader.ReadString();
}
}
return result;
}
}
Link to MSDN for more on serialization
i want to send specific objects thru a simple named pipe. I think the receiver does not recognize the cut between two sent objects. In the result, i get an xml serialization error. Here is my code:
Server process:
using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;
using System.Xml.Serialization;
namespace NamedPipe
{
class ProgramServer
{
static void Main(string[] args)
{
NamedPipeServerStream server;
server = new NamedPipeServerStream("PipesOfPiece");
server.WaitForConnection();
StreamReader reader = new StreamReader(server);
StreamWriter writer = new StreamWriter(server);
Console.WriteLine("Start loop. ");
Thread.Sleep(1000);
while (true)
{
XmlSerializer receiver = new XmlSerializer(typeof(PayLoad));
object testobj = receiver.Deserialize(reader);
Console.WriteLine("Testobject: " + ((PayLoad)(testobj)).name + ((PayLoad)(testobj)).count);
}
}
}
}
Client process:
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
namespace NamedPipe
{
class ProgramClient
{
static void Main(string[] args)
{
//Client
var client = new NamedPipeClientStream("PipesOfPiece");
client.Connect();
StreamReader reader = new StreamReader(client);
while (true)
{
StreamWriter writer = new StreamWriter(client);
PayLoad test = new PayLoad
()
{
name = "Test",
count = 42
};
XmlSerializer sendSerializer = new XmlSerializer(typeof(PayLoad));
sendSerializer.Serialize(writer, test);
}
}
}
}
I have tried recently using NamedPipes without WCF. You can look into this code which might give you some idea. Here, Client is enabled with Callback functionality so server can callback the client.
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
const string PIPE_NAME = "testPipeName33";
const string OBJECT_NAME = "test";
const string CALLBACK_PIPE_NAME = "testPipeName34";
const string CALLBACK_OBJECT_NAME = "testclient";
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
if ((args.Length == 0 || args[0] == "s"))
{
try
{
IPCRegistration.RegisterServer(PIPE_NAME,OBJECT_NAME);
}
catch (RemotingException)
{
remoteObject = IPCRegistration.RegisterClient(typeof(RemoteObject),PIPE_NAME,OBJECT_NAME);
remoteObject.OnNewProcessStarted("test");
Application.Exit();
return;
}
MessageBox.Show("Server:" + Process.GetCurrentProcess().Id);
Process.Start(Application.ExecutablePath, "c");
Application.Run(new Form1("Server"));
}
else
{
IsClient = true;
remoteObject = IPCRegistration.RegisterClient(typeof(RemoteObject), PIPE_NAME, OBJECT_NAME);
IPCRegistration.RegisterServer(CALLBACK_PIPE_NAME, CALLBACK_OBJECT_NAME); // Here Client will listen on this channel.
remoteObject.SetOnNewProcessStarted(OnNewProcessStarted,Process.GetCurrentProcess().Id.ToString());
MessageBox.Show("Client:" + Process.GetCurrentProcess().Id);
Application.Run(new Form1("Client"));
}
}
static RemoteObject remoteObject;
static bool IsClient = false;
static bool OnNewProcessStarted(string commandLine)
{
MessageBox.Show("saved:"+commandLine+" Currrent:"+Process.GetCurrentProcess().Id);
MessageBox.Show("Is Client : " + IsClient);//problem here, IsClient should be true
return true;
}
}
public delegate bool OnNewProcessStartedDelegate(string text);
internal class RemoteObject : MarshalByRefObject
{
public OnNewProcessStartedDelegate OnNewProcessStartedHandler;
public string value;
public bool isCallBack = false;
const string PIPE_NAME = "testPipeName33";
const string OBJECT_NAME = "test";
const string CALLBACK_PIPE_NAME = "testPipeName34";
const string CALLBACK_OBJECT_NAME = "testclient";
RemoteObject remoteObject;
public bool OnNewProcessStarted(string commandLine)
{
if (!isCallBack)
{
remoteObject.isCallBack = true;
return remoteObject.OnNewProcessStarted(commandLine);
}
if (OnNewProcessStartedHandler != null)
return OnNewProcessStartedHandler(value);
return false;
}
public void SetOnNewProcessStarted(OnNewProcessStartedDelegate onNewProcessStarted,string value)
{
this.value = value;
OnNewProcessStartedHandler = onNewProcessStarted;
if (!isCallBack)
{
remoteObject = IPCRegistration.RegisterClient(typeof(RemoteObject), CALLBACK_PIPE_NAME, CALLBACK_OBJECT_NAME);
remoteObject.isCallBack = true;
remoteObject.SetOnNewProcessStarted(onNewProcessStarted, Process.GetCurrentProcess().Id.ToString());
}
}
public override object InitializeLifetimeService()
{
return null;
}
}
internal class IPCRegistration
{
public static RemoteObject RegisterClient(Type remoteObject,string PIPE_NAME,string OBJECT_NAME)
{
IpcClientChannel chan = new IpcClientChannel();
ChannelServices.RegisterChannel(chan, false);
RemoteObject remoteObjectInstance = (RemoteObject)Activator.GetObject(remoteObject,
string.Format("ipc://{0}/{1}", PIPE_NAME, OBJECT_NAME));
return remoteObjectInstance;
}
public static void RegisterServer(string pipeName, string objectName)
{
BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
serverProvider.TypeFilterLevel = TypeFilterLevel.Full;
IpcServerChannel chan = new IpcServerChannel("", pipeName, serverProvider);
ChannelServices.RegisterChannel(chan, false);
RemotingServices.Marshal(new RemoteObject(), objectName);
}
}
I've got a server and client set up using TcpListener and TcpClient.
I want to send an object to my server application for processing.
I've discovered the using System.Runtime.Serialization and the following documentation, but I didn't want to faff around to find that I'm doing it in long winded way.
The question: What is the best way to process and send an object over the TCP stream?
Sending and receiving.
Here's an example of my object:
// Create a new house to send
house newHouse = new house();
// Set variables
newHouse.street = "Mill Lane";
newHouse.postcode = "LO1 BT5";
newHouse.house_number = 11;
newHouse.house_id = 1;
newHouse.house_town = "London";
Assuming you have a class House (available on both sides of your connection) looking like this:
[Serializable]
public class House
{
public string Street { get; set; }
public string ZipCode { get; set; }
public int Number { get; set; }
public int Id { get; set; }
public string Town { get; set; }
}
You can serialize the class into a MemoryStream. You can then use in your TcpClient connection like this:
// Create a new house to send house and set values.
var newHouse = new House
{
Street = "Mill Lane",
ZipCode = "LO1 BT5",
Number = 11,
Id = 1,
Town = "London"
};
var xmlSerializer = new XmlSerializer(typeof(House));
var networkStream = tcpClient.GetStream();
if (networkStream.CanWrite)
{
xmlSerializer.Serialize(networkStream, newHouse);
}
Of course you have to do a little more investigation to make the program running without exception. (e.g. Check memoryStream.Length not to be greater than an int, a.s.o.), but I hope I gave you the right suggestions to help you on your way ;-)
First create a empty ServerApplication and ClientApplication as Console Application to simplify the example.
Then, put the definition for the serializable object into a separate assembly and then add a reference to the shared assembly to each project (server and client). Is necesary share the same object, not just an identical class copy.
To Generate DLL >
Right clic in Solution 'ServerApplication' in the Solution Explorer > Add New Project... -> select Class Library
(e.g. name this project MySharedHouse)
Rename the default Class1 to House and complete it
[Serializable]
public class House
{
public string Street { get; set; }
public string ZipCode { get; set; }
public int Number { get; set; }
public int Id { get; set; }
public string Town { get; set; }
}
Right clic in MySharedHouse and Build.
Now the dll is build and we need to add it in Server Project and Client Project.
Right clic in ServerApplication > Add Reference > Browse and find the dll, for this example
Projects\ServerApplication\MySharedHouse\bin\Debug\MySharedHouse.dll
Repeat the process in ClientApplication using the same dll (same path).
Now you can use instances of House class in ServerApplication and ClientApplication as a single object, simply adding the sentence "using MySharedHouse" at the top.
SERVER CODE
using System;
using System.Net;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using MySharedHouse;
namespace ServerApplication
{
class Program
{
static void Main(string[] args)
{
MessageServer s = new MessageServer(515);
s.Start();
}
}
public class MessageServer
{
private int _port;
private TcpListener _tcpListener;
private bool _running;
private TcpClient connectedTcpClient;
private BinaryFormatter _bFormatter;
private Thread _connectionThread;
public MessageServer(int port)
{
this._port = port;
this._tcpListener = new TcpListener(IPAddress.Loopback, port);
this._bFormatter = new BinaryFormatter();
}
public void Start()
{
if (!_running)
{
this._tcpListener.Start();
Console.WriteLine("Waiting for a connection... ");
this._running = true;
this._connectionThread = new Thread
(new ThreadStart(ListenForClientConnections));
this._connectionThread.Start();
}
}
public void Stop()
{
if (this._running)
{
this._tcpListener.Stop();
this._running = false;
}
}
private void ListenForClientConnections()
{
while (this._running)
{
this.connectedTcpClient = this._tcpListener.AcceptTcpClient();
Console.WriteLine("Connected!");
House house = new House();
house.Street = "Evergreen Terrace";
house.ZipCode = "71474";
house.Number = 742;
house.Id = 34527;
house.Town = "Springfield";
_bFormatter.Serialize(this.connectedTcpClient.GetStream(), house);
Console.WriteLine("send House!");
}
}
}
}
CLIENT CODE
using System;
using System.Net.Sockets;
using System.Runtime.Serialization.Formatters.Binary;
using System.Threading;
using MySharedHouse;
namespace ClientApplication
{
class Program
{
static void Main(string[] args)
{
MessageClient client = new MessageClient(515);
client.StartListening();
}
}
public class MessageClient
{
private int _port;
private TcpClient _tcpClient;
private BinaryFormatter _bFormatter;
private Thread _listenThread;
private bool _running;
private House house;
public MessageClient(int port)
{
this._port = port;
this._tcpClient = new TcpClient("127.0.0.1", port);
this._bFormatter = new BinaryFormatter();
this._running = false;
}
public void StartListening()
{
lock (this)
{
if (!_running)
{
this._running = true;
this._listenThread = new Thread
(new ThreadStart(ListenForMessage));
this._listenThread.Start();
}
else
{
this._running = true;
this._listenThread = new Thread
(new ThreadStart(ListenForMessage));
this._listenThread.Start();
}
}
}
private void ListenForMessage()
{
Console.WriteLine("Reading...");
try
{
while (this._running)
{
this.house = (House)this._bFormatter.Deserialize(this._tcpClient.GetStream());
Console.WriteLine(this.house.Street);
Console.WriteLine(this.house.ZipCode);
Console.WriteLine(this.house.Number);
Console.WriteLine(this.house.Id);
Console.WriteLine(this.house.Town);
}
}
catch (Exception e)
{
Console.WriteLine(e);
Console.ReadLine();
}
}
}
}
Wooala! the first house to be sent over TCP/IP
You can simply decorate your House class with the [Serializable] attribute. (You do not need to define all the other stuff as posted in the other answer)
You can then send this object on the wire by serializing it using the BinaryFormatter class.
Have you considered setting up a WCF service instead of using TcpListener and TcpClient ? Makes life a lot easier.
For instance you could define a service that returned a house
[ServiceContract]
public interface IService
{
[OperationContract]
House GetHouse(int houseId);
}
See this real world example.
Your answer implies the following object (it is common practice to name classes using PascalCase):
[Serializable]
class House:ISerializable
{
public string Street {get; set;}
public string PostalCode {get; set;}
public int HouseNumber {get; set;}
public int HouseID {get; set;}
public string City {get; set;}
public House() { }
protected House(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new System.ArgumentNullException("info");
Street = (string)info.GetValue("Street ", typeof(string));
PostalCode = (string)info.GetValue("PostalCode", typeof(string));
HouseNumber = (int)info.GetValue("HouseNumber", typeof(int));
HouseID = (int)info.GetValue("HouseID", typeof(int));
City = (string)info.GetValue("City", typeof(string));
}
[SecurityPermissionAttribute(SecurityAction.LinkDemand,
Flags=SecurityPermissionFlag.SerializationFormatter)]
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new System.ArgumentNullException("info");
info.AddValue("Street ", Street);
info.AddValue("PostalCode", PostalCode);
info.AddValue("HouseNumber", HouseNumber);
info.AddValue("HouseID", HouseID );
info.AddValue("City", City);
}
}
Now you can serialize your objects:
void Send(Stream stream)
{
BinaryFormatter binaryFmt = new BinaryFormatter();
House h = new House()
{
Street = "Mill Lane",
PostalCode = "LO1 BT5",
HouseNumber = 11,
HouseID = 1,
City = "London"
};
binaryFmt.Serialize(stream, h);
}
How would you deserialize the xml House stream back to a House object on the receiving end?
I'm refering to the solution given in Fischermaen's answer.
On my recieving end I can see a string representation in my Output window by using the following:
ASCIIEncoding encoder = new ASCIIEncoding();
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
Thank you in advance.
EDIT *
Ok well this solution has worked for me. Might need some tidying up.
Here's a method to deserialize a string:
public static T DeserializeFromXml<T>(string xml)
{
T result;
XmlSerializer ser = new XmlSerializer(typeof(T));
using (TextReader tr = new StringReader(xml))
{
result = (T)ser.Deserialize(tr);
}
return result;
}
Then from my TPC/IP Recieving end I call the method like so:
TcpClient tcpClient = (TcpClient)client;
NetworkStream clientStream = tcpClient.GetStream();
byte[] message = new byte[4096];
int bytesRead;
while (true)
{
bytesRead = 0;
try
{
//blocks until a client sends a message
bytesRead = clientStream.Read(message, 0, 4096);
}
catch
{
//a socket error has occured
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
//message has successfully been received
ASCIIEncoding encoder = new ASCIIEncoding();
System.Diagnostics.Debug.WriteLine(encoder.GetString(message, 0, bytesRead));
House house = DeserializeFromXml<House>(encoder.GetString(message, 0, bytesRead));
//Send Message Back
byte[] buffer = encoder.GetBytes("Hello Client - " + DateTime.Now.ToLongTimeString());
clientStream.Write(buffer, 0, buffer.Length);
clientStream.Flush();
}
tcpClient.Close();
}