networkComms.net2.3.1开源版本,基于gpl V3协议。因为不能公开3.x版本的源码,所以基于此版本进行学习。3.X版本进行了诸多改进和Bug修复,使用方法上两者相差不大。
namespace NetworkCommsDotNet
{
/// <summary>
/// Connection对象 这个类是TcpConnection和 UDPConnnection连接类的父类
/// Connection由以下五个文件组成 大家注意到每个类前面都有个 partial关键字
/// ConnectionCreate.cs <1>
/// ConnectionDelegatesHandlers.cs <2>
/// ConnectionIncomingData.cs <3>
/// ConnectionSendClose.cs <4>
/// ConnectionStatic.cs <5>
/// </summary>
public abstract partial class Connection
{
static ManualResetEvent workedThreadSignal = new ManualResetEvent(false);
static volatile bool shutdownWorkerThreads = false;
static object staticConnectionLocker = new object();
static Thread connectionKeepAliveWorker;
/// <summary>
/// 一些默认的设置
/// </summary>
static Connection()
{
ConnectionKeepAlivePollIntervalSecs = 30;
MaxNumSendTimes = 100;
MinNumSendsBeforeConnectionSpecificSendTimeout = 4;
MinSendTimeoutMS = 2000;
MinimumMSPerKBSendTimeout = 20;
DefaultMSPerKBSendTimeout = 1000;
NumberOfStDeviationsForWriteTimeout = 3;
}
/// <summary>
/// 每KB数据发送超时的最小时间 默认为10.
/// </summary>
public static double MinimumMSPerKBSendTimeout { get; set; }
/// <summary>
/// 写入间隔的最大时间 默认100
/// </summary>
public static int MaxNumSendTimes { get; set; }
/// <summary>
/// The minimum number of writes before the connection specific write timeouts will be used. Default is 3.
/// </summary>
public static int MinNumSendsBeforeConnectionSpecificSendTimeout { get; set; }
/// <summary>
/// The default milliseconds per KB write timeout before connection specific values become available. Default is 1000. See <see cref="MinNumSendsBeforeConnectionSpecificSendTimeout"/>.
/// </summary>
public static int DefaultMSPerKBSendTimeout { get; set; }
/// <summary>
/// The minimum timeout for any sized send in milliseconds. Prevents timeouts when sending less than 1KB. Default is 500.
/// </summary>
public static int MinSendTimeoutMS { get; set; }
/// <summary>
/// The interval between keep alive polls of all connections. Set to int.MaxValue to disable keep alive poll
/// </summary>
public static int ConnectionKeepAlivePollIntervalSecs { get; set; }
/// <summary>
/// The number of standard deviations from the mean to use for write timeouts. Default is 4.0.
/// </summary>
public static double NumberOfStDeviationsForWriteTimeout { get; set; }
/// 如果 connectionKeepAliveWorker线程没有启动,则启动之 这个线程主要用来进行心跳检测
protected static void TriggerConnectionKeepAliveThread()
{
lock (staticConnectionLocker)
{
if (!shutdownWorkerThreads && (connectionKeepAliveWorker == null || connectionKeepAliveWorker.ThreadState == ThreadState.Stopped))
{
connectionKeepAliveWorker = new Thread(ConnectionKeepAliveWorker);
connectionKeepAliveWorker.Name = "ConnectionKeepAliveWorker";
connectionKeepAliveWorker.Start();
}
}
}
/// 一个单独的静态工作者线程,用来保持连接是活动的
private static void ConnectionKeepAliveWorker()
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Connection keep alive polling thread has started.");
DateTime lastPollCheck = DateTime.Now;
while (!shutdownWorkerThreads)
{
try
{
//We have a short sleep here so that we can exit the thread fairly quickly if we need too
if (ConnectionKeepAlivePollIntervalSecs == int.MaxValue)
workedThreadSignal.WaitOne(5000);
else
workedThreadSignal.WaitOne(100);
//Check for shutdown here
if (shutdownWorkerThreads) break;
//Any connections which we have not seen in the last poll interval get tested using a null packet
if (ConnectionKeepAlivePollIntervalSecs < int.MaxValue && (DateTime.Now - lastPollCheck).TotalSeconds > (double)ConnectionKeepAlivePollIntervalSecs)
{
AllConnectionsSendNullPacketKeepAlive();
lastPollCheck = DateTime.Now;
}
}
catch (Exception ex)
{
NetworkComms.LogError(ex, "ConnectionKeepAlivePollError");
}
}
}
/// <summary>
/// Polls all existing connections based on ConnectionKeepAlivePollIntervalSecs value. Serverside connections are polled slightly earlier than client side to help reduce potential congestion.
/// </summary>
/// <param name="returnImmediately"></param>
private static void AllConnectionsSendNullPacketKeepAlive(bool returnImmediately = false)
{
if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Trace("Starting AllConnectionsSendNullPacketKeepAlive");
//Loop through all connections and test the alive state
List<Connection> allConnections = NetworkComms.GetExistingConnection();
int remainingConnectionCount = allConnections.Count;
#if WINDOWS_PHONE
QueueItemPriority nullSendPriority = QueueItemPriority.High;
#else
QueueItemPriority nullSendPriority = QueueItemPriority.AboveNormal;
#endif
ManualResetEvent allConnectionsComplete = new ManualResetEvent(false);
for (int i = 0; i < allConnections.Count; i++)
{
//We don't send null packets to unconnected udp connections
UDPConnection asUDP = allConnections[i] as UDPConnection;
if (asUDP != null && asUDP.UDPOptions == UDPOptions.None)
{
if (Interlocked.Decrement(ref remainingConnectionCount) == 0)
allConnectionsComplete.Set();
continue;
}
else
{
int innerIndex = i;
NetworkComms.CommsThreadPool.EnqueueItem(nullSendPriority, new WaitCallback((obj) =>
{
try
{
//If the connection is server side we poll preferentially
if (allConnections[innerIndex] != null)
{
if (allConnections[innerIndex].ConnectionInfo.ServerSide)
{
//We check the last incoming traffic time
//In scenarios where the client is sending us lots of data there is no need to poll
if ((DateTime.Now - allConnections[innerIndex].ConnectionInfo.LastTrafficTime).TotalSeconds > ConnectionKeepAlivePollIntervalSecs)
allConnections[innerIndex].SendNullPacket();
}
else
{
//If we are client side we wait upto an additional 3 seconds to do the poll
//This means the server will probably beat us
if ((DateTime.Now - allConnections[innerIndex].ConnectionInfo.LastTrafficTime).TotalSeconds > ConnectionKeepAlivePollIntervalSecs + 1.0 + (NetworkComms.randomGen.NextDouble() * 2.0))
allConnections[innerIndex].SendNullPacket();
}
}
}
catch (Exception) { }
finally
{
if (Interlocked.Decrement(ref remainingConnectionCount) == 0)
allConnectionsComplete.Set();
}
}), null);
}
}
//Max wait is 1 seconds per connection
if (!returnImmediately && allConnections.Count > 0)
{
if (!allConnectionsComplete.WaitOne(allConnections.Count * 2500))
//This timeout should not really happen so we are going to log an error if it does
NetworkComms.LogError(new TimeoutException("Timeout after " + allConnections.Count.ToString() + " seconds waiting for null packet sends to finish. " + remainingConnectionCount.ToString() + " connection waits remain. This error indicates very high send load or a possible send deadlock."), "NullPacketKeepAliveTimeoutError");
}
}
/// <summary>
/// Shutdown any static connection components
/// </summary>
/// <param name="threadShutdownTimeoutMS"></param>
internal static void ShutdownBase(int threadShutdownTimeoutMS = 1000)
{
try
{
shutdownWorkerThreads = true;
if (connectionKeepAliveWorker != null && !connectionKeepAliveWorker.Join(threadShutdownTimeoutMS))
connectionKeepAliveWorker.Abort();
}
catch (Exception ex)
{
NetworkComms.LogError(ex, "CommsShutdownError");
}
finally
{
shutdownWorkerThreads = false;
workedThreadSignal.Reset();
}
}
}
}
【开源下载】基于TCP网络通信的即时聊天系统(IM系统)(c#源码)
[源码下载]Demo2.模拟简单登陆-效果图 基于networkcomms2.3.1
[源码下载]Demo1 客户端从服务器获取信息(基于networkcomms2.3.1)
【开源下载】基于TCP网络通信的自动升级程序c#源码