问题描述
我知道这是一个讨论非常多的话题,但是我的代码遇到了奇怪的行为。
进行一些测试,我发现对于某些客户端来说一切都很好,但是其他客户端根本无法通过基于TCPClient
的自定义客户端设计来接收和发送数据。
尽管许多用户都可以通过此连接类型访问我的服务器,但我无法确认此问题是由VPN
还是NATs
连接引起的。
我一直在阅读.Net中的TCPClient
和TCPListener
并搜索诸如我的问题但没有运气。我得到的一切都是可怜的例子。
我将不胜感激,甚至会为网络高级编程提供参考。
这是我的代码。最新最稳定
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(),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,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);
}
}
}
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,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,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,compressed.Length);
NetStream.Write(NetComTools.EOS,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,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,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,4);
ini.copyTo(4,mac,12);
ini.copyTo(16,netsidfromclient,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);
}
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();
}
}
}
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)