I'm currently writing the Networking backend for a game I'm creating in Unity. The problem I'm facing is connecting to my server on another computer (on the same local network). I'm extremely new to uNet and what it entails, so the help is very appreciated.
I've followed a few tutorials, but none have fixed the problem that I've been facing. There's currently no connection at all coming through to the server. I've initialised the server, created sockets, used Network.Listen() to listen to packets coming through the port - but nothing quite yet.
This is a test project that I'm doing before implementing into the game, so sorry for the not-so-clean code.
The IP's are specified in Inspector, but used "127.0.0.1" to preset it. Sorry for the confusion!
Here's the client-side code:
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public class ClientConnector : NetworkManager
{
NetworkClient m_client;
public GameObject screenCanvas;
bool socketConnection = false;
bool serverConnection = false;
bool connectingToSocket = false;
bool connectingToServer = false;
bool connectedToServer = false;
public int socketConnectionPort = 8888;
public int serverConnectionPort = 8899;
public string connectionIP = "127.0.0.1";
public int maxConnectionsNumber = 10;
int reliableChannelID;
int socketID;
int socketConnectionID;
int serverConnectionID;
void Start ()
{
NetworkTransport.Init();
m_client = new NetworkClient();
if (SetupConnection())
{
ConnectSocket();
}
if (socketConnection)
{
ConnectServer();
}
}
void OnApplicationQuit()
{
NetworkTransport.Shutdown();
}
public void ConnectSocket()
{
connectingToSocket = true;
byte error;
socketConnectionID = NetworkTransport.Connect(socketID, connectionIP, socketConnectionPort, 0, out error);
if (socketConnectionID != 0)
{
socketConnection = true;
connectingToSocket = false;
}
}
public void ConnectServer()
{
connectingToServer = true;
byte error;
serverConnectionID = NetworkTransport.Connect(socketID, connectionIP, serverConnectionPort, 0, out error);
if (!ReferenceEquals(socketID, 0))
{
serverConnection = true;
connectedToServer = true;
connectingToServer = false;
SendSocketMessage();
}
}
bool SetupConnection()
{
ConnectionConfig config = new ConnectionConfig();
reliableChannelID = config.AddChannel(QosType.Reliable);
HostTopology topology = new HostTopology(config, maxConnectionsNumber);
socketID = NetworkTransport.AddHost(topology, socketConnectionPort);
if (!ReferenceEquals(client, null))
{
client.RegisterHandler(MsgType.Connect, OnConnected);
}
if (ReferenceEquals(socketID, 0))
{
Debug.Log("Socket open. Socket ID is: " + socketConnectionID);
return true;
}
else
{
return false;
}
}
void Update ()
{
ReceiveAndHandlePacket();
}
void ReceiveAndHandlePacket()
{
int recChannelID;
byte[] recBuffer = new byte[1024];
int bufferSize = 1024;
int dataSize;
byte error;
NetworkEventType networkEvent = NetworkTransport.Receive(out socketID, out socketConnectionID, out recChannelID, recBuffer, bufferSize, out dataSize, out error);
switch (networkEvent)
{
case NetworkEventType.Nothing:
{
Debug.Log("No packets!");
break;
}
case NetworkEventType.ConnectEvent:
{
break;
}
case NetworkEventType.DataEvent:
{
Stream stream = new MemoryStream(recBuffer);
bufferSize = (int)stream.Position;
BinaryFormatter formatter = new BinaryFormatter();
string data = formatter.Deserialize(stream) as string;
Debug.Log("Received Packet from Server saying: " + data);
break;
}
case NetworkEventType.DisconnectEvent:
{
Debug.Log("Disconnected.");
break;
}
}
}
void SendSocketMessage()
{
byte error;
byte[] buffer = new byte[1024];
Stream stream = new MemoryStream(buffer);
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, "Connecting from Client.");
int bufferSize = (int)stream.Position;
NetworkTransport.Send(socketID, socketConnectionID, reliableChannelID, buffer, bufferSize, out error);
}
void OnConnected(NetworkMessage netMsg)
{
Debug.Log("Connected to Socket on " + connectionIP);
}
}
and here is the server-side code:
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.UI;
using UnityEngine.Networking;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
public class ServerControl : NetworkManager
{
public GameObject connectionsScreen;
public int maxConnectables;
public int socketConnectionPort = 8888;
public string socketConnectionIP = "127.0.0.1";
public int serverConnectionPort = 8899;
bool useNat;
List<NetworkClient> connections = new List<NetworkClient>();
bool serverInitialized;
int channelID;
int socketID;
int connectionID;
void Start()
{
useNat = !Network.HavePublicAddress();
NetworkTransport.Init();
if (SetupSocket())
{
InitializePeerServer();
}
}
void Update()
{
ReceiveAndHandlePackets();
}
void OnApplicationQuit()
{
NetworkTransport.Shutdown();
}
public bool SetupSocket()
{
ConnectionConfig config = new ConnectionConfig();
channelID = config.AddChannel(QosType.Reliable);
HostTopology topology = new HostTopology(config, maxConnections);
socketID = NetworkTransport.AddHost(topology);
byte error;
connectionID = NetworkTransport.Connect(socketID, socketConnectionIP, socketConnectionPort, 0, out error);
if (connectionID != 0)
{
Debug.Log("Socket open on: " + socketConnectionPort + " with IP: " + socketConnectionIP);
return true;
}
else
return false;
}
public void InitializePeerServer()
{
NetworkConnectionError serverStatus;
serverStatus = Network.InitializeServer(maxConnectables, serverConnectionPort, useNat);
Network.Listen(serverConnectionPort);
switch (serverStatus)
{
case NetworkConnectionError.NoError:
{
serverInitialized = true;
break;
}
default:
{
serverInitialized = false;
break;
}
}
}
public void ReceiveAndHandlePackets()
{
int recChannelID;
byte[] recBuffer = new byte[1024];
int bufferSize = 1024;
int dataSize;
byte error;
NetworkEventType networkEvent = NetworkTransport.Receive(out socketID, out connectionID, out recChannelID, recBuffer, bufferSize, out dataSize, out error);
switch (networkEvent)
{
case NetworkEventType.Nothing:
connectionsScreen.transform.GetChild(6).GetComponent<Text>().text = "Message: \nNo packets!";
break;
case NetworkEventType.ConnectEvent:
{
Debug.Log("Incoming connection.");
break;
}
case NetworkEventType.DataEvent:
{
Stream stream = new MemoryStream(recBuffer);
bufferSize = (int)stream.Position;
BinaryFormatter formatter = new BinaryFormatter();
string data = formatter.Deserialize(stream) as string;
Debug.Log("Received packet, saying: " + data);
connectionsScreen.transform.GetChild(6).GetComponent<Text>().text = "Message Received: " + data;
break;
}
case NetworkEventType.DisconnectEvent:
{
Debug.Log("Disconnected.");
break;
}
}
}
public override void OnClientConnect(NetworkConnection conn)
{
base.OnClientConnect(conn);
NetworkClient client = new NetworkClient(conn);
connections.Add(client);
Debug.Log("New connection from " + conn.address);
}
}
Any and all help is very greatly appreciated!
Thank you!
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();
}
}
}
N.B. refer to this link.
I wrote this class to be used as a proxy in the server-end and as a client at the client-end.
My current source code is the following:
public class ClientClass : IDisposable
{
private string Host { get; set; }
private int Port { get; set; }
public bool IsConnected { private set; get; }
public TcpClient Tcp { get; private set; }
private System.Net.Sockets.NetworkStream stream;
public ClientClass()
{
IsConnected = false;
}
//constructor for server program.
public ClientClass(TcpListener listener)
{
Tcp = listener.AcceptTcpClient();
Host = ((IPEndPoint)Tcp.Client.RemoteEndPoint).Address.ToString();
Port = ((IPEndPoint)Tcp.Client.LocalEndPoint).Port;
IsConnected = true;
stream = Tcp.GetStream();
}
//constructor for client.
public ClientClass(string host, int port)
{
Host = host;
Port = port;
}
public string Read()
{
if (IsConnected)
{
byte[] buffer = new byte[Tcp.ReceiveBufferSize];//create a byte array
int bytesRead = stream.Read(buffer, 0, Tcp.ReceiveBufferSize);//read count
string str = Encoding.ASCII.GetString(buffer, 0, bytesRead);//convert to string
return str.TrimEnd(new char[] {'\r', '\n'});//remove CR and LF
}
else
{
throw new Exception("Client " + ID + " is not connected!");
}
}
public void Write(string str)
{
if (IsConnected)
{
str = str + Constants.CRLF;// add CR and LF
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(str);
stream.Write(bytesToSend, 0, bytesToSend.Length);
stream.Flush();
}
else
{
throw new Exception("Client " + ID + " is not connected!");
}
}
public bool Connect()
{
if (IsConnected == false)
{
IsConnected = true;
Tcp = new TcpClient(Host, Port);
stream = Tcp.GetStream();
return true;
}
return false;
}
public bool Disconnect()
{
if (IsConnected)
{
if (Tcp != null)
{
//stream.Flush();
stream.Close();
//Tcp.GetStream().Flush();
//Tcp.GetStream().Close();
Tcp.Close();
return true;
}
}
return false;
}
#region dispose pattern
// Flag: Has Dispose already been called?
bool disposed = false;
// Public implementation of Dispose pattern callable by consumers.
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// Protected implementation of Dispose pattern.
protected virtual void Dispose(bool disposing)
{
if (disposed)
return;
if (disposing)
{
// Free any other managed objects here
if (stream != null)
{
stream.Flush();
stream.Close();
stream.Dispose();
stream = null;
}
if (Tcp != null)
{
if (Tcp.Connected)
{
Tcp.Client.Disconnect(false);
Tcp.Client.Close();
Tcp.Client.Dispose();
Tcp.Client = null;
//Tcp.GetStream().Flush();
//Tcp.GetStream().Close();
Tcp.Close();
Tcp = null;
}
}
}
// Free any unmanaged objects here.
// ...
disposed = true;
}
~ClientClass()
{
Dispose(false);
}
#endregion
}
The problem I am facing with this code is:
ClientClass client = new ClientClass(...);
client.Write("1");
client.Write("2");
client.Write("hello");
are going to the other end as only one input:
"12hello"
, rather than three separate inputs {"1", "2", and "hello"}.
How can I fix this?
TCP is a stream protocol, not a packet protocol. All that you are guaranteed is the same bytes in the same order (or a socket failure), not the composition of groups. So: anything beyond that you need to add yourself. With text-based protocols, a common approach might be to put a line feed (\n) after each logical payload, and then look for the same when decoding. With binary protocols, a length prefix is more common.
I am working on a project (server side) where i need to stream data (videos, large files) to clients.
This worked perfect using ByteRangeStreamContent, as i was serving files from disk and could create a seekable stream (FileStream).
if (Request.Headers.Range != null)
{
try
{
HttpResponseMessage partialResponse = Request.CreateResponse(HttpStatusCode.PartialContent);
partialResponse.Content = new ByteRangeStreamContent(fs, Request.Headers.Range, mediaType);
return partialResponse;
}
catch (InvalidByteRangeException invalidByteRangeException)
{
return Request.CreateErrorResponse(invalidByteRangeException);
}
}
else
{
response.Content = new StreamContent(fs);
response.Content.Headers.ContentType = mediaType;
return response;
}
But, i moved the file provider from disk to an external service. The service allows me to get chunks of data (Range{0}-{1}).
Of course, it's not possible to download whole file in memory and then use a MemoryStream for ByteRangeStreamContent because of the obvious reasons (too many concurrent downloads will consume all the available memory at some point).
I found this article https://vikingerik.wordpress.com/2014/09/28/progressive-download-support-in-asp-net-web-api/ where the author says:
A change request I got for my library was to support reading only the
necessary data and sending that out rather than opening a stream for
the full data. I wasn’t sure what this would buy until the user
pointed out they are reading their resource data from a WCF stream
which does not support seeking and would need to read the whole stream
into a MemoryStream in order to allow the library to generate the
output.
That limitation still exists in this specific object but there is a
workaround. Instead of using a ByteRangeStreamContent, you could
instead use a ByteArrayContent object instead. Since the majority of
RANGE requests will be for a single start and end byte, you could pull
the range from the HttpRequestMessage, retrieve only the bytes you
need and send it back out as a byte stream. You’ll also need to add
the CONTENT-RANGE header and set the response code to 206
(PartialContent) but this could be a viable alternative (though I
haven’t tested it) for users who do not want or can’t easily get a
compliant stream object.
So, my question basically is: how can i do that ?
I finally managed to do it.
Here's how:
Custom implementation of a stream:
public class BufferedHTTPStream : Stream
{
private readonly Int64 cacheLength = 4000000;
private const Int32 noDataAvaiable = 0;
private MemoryStream stream = null;
private Int64 currentChunkNumber = -1;
private Int64? length;
private Boolean isDisposed = false;
private Func<long, long, Stream> _getStream;
private Func<long> _getContentLength;
public BufferedHTTPStream(Func<long, long, Stream> streamFunc, Func<long> lengthFunc)
{
_getStream = streamFunc;
_getContentLength = lengthFunc;
}
public override Boolean CanRead
{
get
{
EnsureNotDisposed();
return true;
}
}
public override Boolean CanWrite
{
get
{
EnsureNotDisposed();
return false;
}
}
public override Boolean CanSeek
{
get
{
EnsureNotDisposed();
return true;
}
}
public override Int64 Length
{
get
{
EnsureNotDisposed();
if (length == null)
{
length = _getContentLength();
}
return length.Value;
}
}
public override Int64 Position
{
get
{
EnsureNotDisposed();
Int64 streamPosition = (stream != null) ? stream.Position : 0;
Int64 position = (currentChunkNumber != -1) ? currentChunkNumber * cacheLength : 0;
return position + streamPosition;
}
set
{
EnsureNotDisposed();
EnsurePositiv(value, "Position");
Seek(value);
}
}
public override Int64 Seek(Int64 offset, SeekOrigin origin)
{
EnsureNotDisposed();
switch (origin)
{
case SeekOrigin.Begin:
break;
case SeekOrigin.Current:
offset = Position + offset;
break;
default:
offset = Length + offset;
break;
}
return Seek(offset);
}
private Int64 Seek(Int64 offset)
{
Int64 chunkNumber = offset / cacheLength;
if (currentChunkNumber != chunkNumber)
{
ReadChunk(chunkNumber);
currentChunkNumber = chunkNumber;
}
offset = offset - currentChunkNumber * cacheLength;
stream.Seek(offset, SeekOrigin.Begin);
return Position;
}
private void ReadNextChunk()
{
currentChunkNumber += 1;
ReadChunk(currentChunkNumber);
}
private void ReadChunk(Int64 chunkNumberToRead)
{
Int64 rangeStart = chunkNumberToRead * cacheLength;
if (rangeStart >= Length) { return; }
Int64 rangeEnd = rangeStart + cacheLength - 1;
if (rangeStart + cacheLength > Length)
{
rangeEnd = Length - 1;
}
if (stream != null) { stream.Close(); }
stream = new MemoryStream((int)cacheLength);
var responseStream = _getStream(rangeStart, rangeEnd);
responseStream.Position = 0;
responseStream.CopyTo(stream);
responseStream.Close();
stream.Position = 0;
}
public override void Close()
{
EnsureNotDisposed();
base.Close();
if (stream != null) { stream.Close(); }
isDisposed = true;
}
public override Int32 Read(Byte[] buffer, Int32 offset, Int32 count)
{
EnsureNotDisposed();
EnsureNotNull(buffer, "buffer");
EnsurePositiv(offset, "offset");
EnsurePositiv(count, "count");
if (buffer.Length - offset < count) { throw new ArgumentException("count"); }
if (stream == null) { ReadNextChunk(); }
if (Position >= Length) { return noDataAvaiable; }
if (Position + count > Length)
{
count = (Int32)(Length - Position);
}
Int32 bytesRead = stream.Read(buffer, offset, count);
Int32 totalBytesRead = bytesRead;
count -= bytesRead;
while (count > noDataAvaiable)
{
ReadNextChunk();
offset = offset + bytesRead;
bytesRead = stream.Read(buffer, offset, count);
count -= bytesRead;
totalBytesRead = totalBytesRead + bytesRead;
}
return totalBytesRead;
}
public override void SetLength(Int64 value)
{
EnsureNotDisposed();
throw new NotImplementedException();
}
public override void Write(Byte[] buffer, Int32 offset, Int32 count)
{
EnsureNotDisposed();
throw new NotImplementedException();
}
public override void Flush()
{
EnsureNotDisposed();
}
private void EnsureNotNull(Object obj, String name)
{
if (obj != null) { return; }
throw new ArgumentNullException(name);
}
private void EnsureNotDisposed()
{
if (!isDisposed) { return; }
throw new ObjectDisposedException("BufferedHTTPStream");
}
private void EnsurePositiv(Int32 value, String name)
{
if (value > -1) { return; }
throw new ArgumentOutOfRangeException(name);
}
private void EnsurePositiv(Int64 value, String name)
{
if (value > -1) { return; }
throw new ArgumentOutOfRangeException(name);
}
private void EnsureNegativ(Int64 value, String name)
{
if (value < 0) { return; }
throw new ArgumentOutOfRangeException(name);
}
}
Usage:
var fs = new BufferedHTTPStream((start, end) =>
{
// return stream from external service
}, () =>
{
// return stream length from external service
});
HttpResponseMessage partialResponse = Request.CreateResponse(HttpStatusCode.PartialContent);
partialResponse.Content = new ByteRangeStreamContent(fs, Request.Headers.Range, mediaType);
partialResponse.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = fileName
};
return partialResponse;
I was developing android app on eclipse till now and now I'm using Mono for Android.
I am trying to copy the database from Assets folder, copy it onto the SD Card, then read data from it. I'm using the following code for the same. However I'm getting database corruption error.
After running the following code, empty database is getting created on SD card but the actual table is not getting copied.
Also how to check SD card content in MonoDevelop. I'm currently using eclipse for the same.
As I'm just beginner to android development, any help appreciated.
public class DbManager
{
private Context ctx;
private SQLiteDatabase mDb;
private DatabaseHelper dataHelper;
private String DATABASE_PATH = "/data/data/HelloM4A.HelloM4A/databases/";
private static String DATABASE_NAME = "CompanyMaster.db";
private SQLiteCursor cur;
private String[] b1;
private int x;
private static int DATABASE_VERSION = 1;
public DbManager(Context ctx)
{
this.ctx = ctx;
dataHelper = new DatabaseHelper(ctx);
}
private class DatabaseHelper : Android.Database.Sqlite.SQLiteOpenHelper
{
public DatabaseHelper AnyName
{ get; set; }
Context myContext = null;
public DatabaseHelper(Context context)
: base(context, DATABASE_NAME, null, DATABASE_VERSION)
{
this.myContext = context;
}
public override void OnCreate(Android.Database.Sqlite.SQLiteDatabase db)
{
}
public override void OnUpgrade(Android.Database.Sqlite.SQLiteDatabase db,
int oldVersion, int newVersion)
{
OnCreate(db);
}
}
public bool checkDataBase()
{
String myPath = DATABASE_PATH + DATABASE_NAME;
Java.IO.File f = new Java.IO.File(myPath);
return f.Exists();
}
public void createDataBase()
{
openDB();
try
{
Stream stream;
string destPath = Path.Combine(Environment.GetFolderPath(
Environment.SpecialFolder.Personal), "CompanyMaster.db");
if (new Java.IO.File(destPath).Exists())
using (stream = ctx.Assets.Open("CompanyMaster.db"))
{
OutputStream myOutput = new FileOutputStream(DATABASE_PATH +
DATABASE_NAME);
byte[] buffer = new byte[1024];
int length;
while ((length = stream.Read(buffer, 0, 1024)) > 0)
{
myOutput.Write(buffer, 0, length);
}
if (mDb.IsOpen == true)
mDb.Close();
myOutput.Flush();
myOutput.Close();
stream.Close();
}
}
catch (Exception e)
{ }
}
public DbManager openDB()
{
try
{
mDb = dataHelper.WritableDatabase;
}
catch (Exception e)
{ }
return this;
}
public String[] getSymbol()
{
try
{
cur = (SQLiteCursor)mDb.RawQuery("select symbol, company_name " +
"from Scrip", null);
}
catch (SQLiteException e)
{ }
b1 = new String[2168];
x = 0;
if (cur.MoveToFirst())
{
do`
{
b1[x] = cur.GetString(cur.GetColumnIndex("symbol"));
x++;
}
while (cur.MoveToNext());
}
cur.Close();
return b1;
}
public void close()
{
mDb.Close();
}
}
EDIT
After various suggestions, I tried following code :
public void createDataBase()
{
openDB();
try
{
var dataFile = ctx.ApplicationContext
.GetDatabasePath(DATABASE_NAME)
.AbsolutePath;
Console.WriteLine("path="+
ctx.ApplicationContext
.GetDatabasePath(DATABASE_NAME)
.AbsolutePath);
// I get path=/data/data/HelloM4A.HelloM4A/databases/CompanyMaster.db
// which is correct.
if (!System.IO.File.Exists(dataFile))
{
using (var input = ctx.Assets.Open(DATABASE_NAME))
using (var output = System.IO.File.Create(dataFile))
{
var buffer = new byte[1024];
int len;
while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
}
}
catch
{
}
}
public DbManager openDB()
{
try
{
mDb = dataHelper.WritableDatabase;
}
catch
{
}
return this;
}
Now the issue is if I don't use openDB() within createDataBase() , it gives fileNotFoundException.
and if I use openDB() within createDataBase(), it created empty DB and hence DB doesn't get copied and I get Blank DB as final ouput because of following condition if (!System.IO.File.Exists(dataFile)).
Since already empty DB is created it doesn't traverse inside if.
What can be done here?
I use this code to copy out of the assets folder to where I want it (you should be able to modify it to suit yourself):
const string dbName = "evolution.sqlite";
var dataDirectory = Path.Combine(ApplicationContext.FilesDir.AbsolutePath,
"databases");
var dataFile = Path.Combine(dataDirectory, dbName);
if (!Directory.Exists(dataDirectory))
{
Directory.CreateDirectory(dataDirectory);
}
if (!File.Exists(dataFile))
{
using (var input = ApplicationContext.Assets.Open(dbName))
using (var output = File.Create(dataFile))
{
var buffer = new byte[1024];
int len;
while ((len = input.Read(buffer, 0, buffer.Length)) > 0)
{
output.Write(buffer, 0, len);
}
}
}
Note: I dynamically create the path as I am using an ORM instead of the DB helper classes.
You could use dataDirectory = context.GetDatabasePath("").AbsolutePath(); instead.
You must do this before you use the DatabaseHelper as the database will also try and create the database. I do this in my Activity.OnCreate(...) method and then use the DatabaseHelper afterwards.
Thus, if my code was in a function called CopyDB(...) you use the method:
public DbManager openDB()
{
CopyDB(...);
try
{
mDb = dataHelper.WritableDatabase;
}
catch(Exception e)
{
}
return this;
}