You've already forked lidgren-network-gen3
mirror of
https://github.com/lidgren/lidgren-network-gen3.git
synced 2026-05-17 07:36:32 +09:00
major update; gen 3.5
This commit is contained in:
@@ -1,85 +1,56 @@
|
||||
/* Copyright (c) 2010 Michael Lidgren
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom
|
||||
the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or
|
||||
substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace Lidgren.Network
|
||||
{
|
||||
//
|
||||
// This partial file holds public netpeer methods accessible to the application
|
||||
//
|
||||
[DebuggerDisplay("Status={m_status}")]
|
||||
/// <summary>
|
||||
/// Represents a local peer capable of holding zero, one or more connections to remote peers
|
||||
/// </summary>
|
||||
public partial class NetPeer
|
||||
{
|
||||
private static int s_peerCount = 0;
|
||||
private static int s_initializedPeersCount;
|
||||
|
||||
internal const int kMinPacketHeaderSize = 2;
|
||||
internal const int kMaxPacketHeaderSize = 5;
|
||||
|
||||
private NetPeerStatus m_status;
|
||||
private readonly object m_initializeLock = new object();
|
||||
internal long m_uniqueIdentifier;
|
||||
|
||||
internal NetPeerConfiguration m_configuration;
|
||||
internal readonly NetPeerStatistics m_statistics;
|
||||
private Thread m_networkThread;
|
||||
private string m_shutdownReason;
|
||||
private string m_networkThreadName;
|
||||
private int m_listenPort;
|
||||
|
||||
internal readonly List<NetConnection> m_connections;
|
||||
private readonly Dictionary<IPEndPoint, NetConnection> m_connectionLookup;
|
||||
|
||||
private string m_shutdownReason;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the status of the NetPeer
|
||||
/// </summary>
|
||||
public NetPeerStatus Status { get { return m_status; } }
|
||||
|
||||
/// <summary>
|
||||
/// Name of the network thread for this NetPeer
|
||||
/// Signalling event which can be waited on to determine when a message is queued for reading.
|
||||
/// Note that there is no guarantee that after the event is signaled the blocked thread will
|
||||
/// find the message in the queue. Other user created threads could be preempted and dequeue
|
||||
/// the message before the waiting thread wakes up.
|
||||
/// </summary>
|
||||
public string NetworkThreadName
|
||||
{
|
||||
get { return m_networkThreadName; }
|
||||
set
|
||||
{
|
||||
if (m_networkThreadName != value)
|
||||
{
|
||||
m_networkThreadName = value;
|
||||
if (m_networkThread != null)
|
||||
m_networkThread.Name = m_networkThreadName;
|
||||
}
|
||||
}
|
||||
}
|
||||
public AutoResetEvent MessageReceivedEvent { get { return m_messageReceivedEvent; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a unique identifier for this NetPeer based on Mac address and ip/port. Note! Not available until Start has been called!
|
||||
/// </summary>
|
||||
public long UniqueIdentifier { get { return m_uniqueIdentifier; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the port number this NetPeer is listening and sending on
|
||||
/// </summary>
|
||||
public int Port { get { return m_listenPort; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a copy of the list of connections
|
||||
/// </summary>
|
||||
public NetConnection[] Connections
|
||||
public List<NetConnection> Connections
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (m_connections)
|
||||
return m_connections.ToArray();
|
||||
return new List<NetConnection>(m_connections);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,48 +75,22 @@ namespace Lidgren.Network
|
||||
/// </summary>
|
||||
public NetPeerConfiguration Configuration { get { return m_configuration; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the port number this NetPeer is listening and sending on
|
||||
/// </summary>
|
||||
public int Port { get { return m_listenPort; } }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a semi-unique identifier based on Mac address and ip/port. Note! Not available until Start has been called!
|
||||
/// </summary>
|
||||
public long UniqueIdentifier { get { return m_uniqueIdentifier; } }
|
||||
|
||||
public NetPeer(NetPeerConfiguration configuration)
|
||||
public NetPeer(NetPeerConfiguration config)
|
||||
{
|
||||
if (configuration == null)
|
||||
throw new ArgumentNullException("configuration");
|
||||
|
||||
m_status = NetPeerStatus.NotRunning;
|
||||
m_configuration = configuration;
|
||||
m_connections = new List<NetConnection>(m_configuration.MaximumConnections);
|
||||
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>(m_configuration.MaximumConnections);
|
||||
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
|
||||
m_configuration = config;
|
||||
m_statistics = new NetPeerStatistics(this);
|
||||
|
||||
int pc = Interlocked.Increment(ref s_peerCount);
|
||||
m_networkThreadName = "Lidgren network thread " + pc.ToString();
|
||||
m_releasedIncomingMessages = new NetQueue<NetIncomingMessage>(4);
|
||||
m_unsentUnconnectedMessages = new NetQueue<NetTuple<IPEndPoint, NetOutgoingMessage>>(2);
|
||||
m_connections = new List<NetConnection>();
|
||||
m_connectionLookup = new Dictionary<IPEndPoint, NetConnection>();
|
||||
m_handshakes = new Dictionary<IPEndPoint, NetConnection>();
|
||||
m_senderRemote = (EndPoint)new IPEndPoint(IPAddress.Any, 0);
|
||||
m_status = NetPeerStatus.NotRunning;
|
||||
m_receivedFragmentGroups = new Dictionary<int, ReceivedFragmentGroup>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the connection to a remote endpoint; if it exists
|
||||
/// </summary>
|
||||
public NetConnection GetConnection(IPEndPoint remoteEndPoint)
|
||||
{
|
||||
if (remoteEndPoint == null)
|
||||
throw new ArgumentNullException("remoteEndPoint");
|
||||
|
||||
NetConnection retval;
|
||||
if (m_connectionLookup.TryGetValue(remoteEndPoint, out retval))
|
||||
return retval;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds to socket
|
||||
/// Binds to socket and spawns networking thread
|
||||
/// </summary>
|
||||
public void Start()
|
||||
{
|
||||
@@ -158,16 +103,18 @@ namespace Lidgren.Network
|
||||
|
||||
m_status = NetPeerStatus.Starting;
|
||||
|
||||
m_releasedIncomingMessages.Clear();
|
||||
m_unsentUnconnectedMessage.Clear();
|
||||
|
||||
m_configuration.VerifyAndLock();
|
||||
// fix network thread name
|
||||
if (m_configuration.NetworkThreadName == "Lidgren network thread")
|
||||
{
|
||||
int pc = Interlocked.Increment(ref s_initializedPeersCount);
|
||||
m_configuration.NetworkThreadName = "Lidgren network thread " + pc.ToString();
|
||||
}
|
||||
|
||||
InitializeNetwork();
|
||||
|
||||
|
||||
// start network thread
|
||||
m_networkThread = new Thread(new ThreadStart(NetworkLoop));
|
||||
m_networkThread.Name = m_networkThreadName;
|
||||
m_networkThread.Name = m_configuration.NetworkThreadName;
|
||||
m_networkThread.IsBackground = true;
|
||||
m_networkThread.Start();
|
||||
|
||||
@@ -175,17 +122,11 @@ namespace Lidgren.Network
|
||||
Thread.Sleep(10);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if there is a queued message available to read using ReadMessage()
|
||||
/// </summary>
|
||||
public bool MessageAvailable
|
||||
internal NetConnection GetConnection(IPEndPoint ep)
|
||||
{
|
||||
get
|
||||
{
|
||||
if (m_status == NetPeerStatus.NotRunning)
|
||||
return false;
|
||||
return (m_releasedIncomingMessages.Count > 0);
|
||||
}
|
||||
NetConnection retval;
|
||||
m_connectionLookup.TryGetValue(ep, out retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -193,9 +134,6 @@ namespace Lidgren.Network
|
||||
/// </summary>
|
||||
public NetIncomingMessage ReadMessage()
|
||||
{
|
||||
if (m_status == NetPeerStatus.NotRunning)
|
||||
return null;
|
||||
|
||||
NetIncomingMessage retval;
|
||||
if (m_releasedIncomingMessages.TryDequeue(out retval))
|
||||
{
|
||||
@@ -207,14 +145,16 @@ namespace Lidgren.Network
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
public NetIncomingMessage WaitMessage(int maxMillis)
|
||||
|
||||
// send message immediately
|
||||
internal void SendLibrary(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (m_messageReceivedEvent != null)
|
||||
m_messageReceivedEvent.WaitOne(maxMillis);
|
||||
NetIncomingMessage retval;
|
||||
m_releasedIncomingMessages.TryDequeue(out retval);
|
||||
return retval;
|
||||
VerifyNetworkThread();
|
||||
NetException.Assert(msg.m_isSent == false);
|
||||
|
||||
bool connReset;
|
||||
int len = msg.Encode(m_sendBuffer, 0, 0);
|
||||
SendPacket(len, recipient, 1, out connReset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -225,6 +165,14 @@ namespace Lidgren.Network
|
||||
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
public NetConnection Connect(string host, int port, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), hailMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
@@ -236,34 +184,7 @@ namespace Lidgren.Network
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
public NetConnection Connect(string host, int port, NetOutgoingMessage approvalMessage)
|
||||
{
|
||||
return Connect(new IPEndPoint(NetUtility.Resolve(host), port), approvalMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to create a connection to a remote endpoint; returns true on success
|
||||
/// Note that the connection attempt may still fail; the returning value only indicates that the connection procedure initiated successfully
|
||||
/// </summary>
|
||||
public bool TryConnect(IPEndPoint remoteEndpoint, NetOutgoingMessage approvalMessage, out NetConnection connection)
|
||||
{
|
||||
lock (m_connections)
|
||||
{
|
||||
if (m_status == NetPeerStatus.NotRunning || m_connectionLookup.ContainsKey(remoteEndpoint))
|
||||
{
|
||||
connection = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
connection = Connect(remoteEndpoint, approvalMessage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a connection to a remote endpoint
|
||||
/// </summary>
|
||||
public virtual NetConnection Connect(IPEndPoint remoteEndpoint, NetOutgoingMessage approvalMessage)
|
||||
public virtual NetConnection Connect(IPEndPoint remoteEndpoint, NetOutgoingMessage hailMessage)
|
||||
{
|
||||
if (remoteEndpoint == null)
|
||||
throw new ArgumentNullException("remoteEndpoint");
|
||||
@@ -276,158 +197,49 @@ namespace Lidgren.Network
|
||||
if (m_connectionLookup.ContainsKey(remoteEndpoint))
|
||||
throw new NetException("Already connected to that endpoint!");
|
||||
|
||||
NetConnection hs;
|
||||
if (m_handshakes.TryGetValue(remoteEndpoint, out hs))
|
||||
{
|
||||
// already trying to connect to that endpoint; make another try
|
||||
switch (hs.Status)
|
||||
{
|
||||
case NetConnectionStatus.InitiatedConnect:
|
||||
// send another connect
|
||||
hs.m_connectRequested = true;
|
||||
break;
|
||||
case NetConnectionStatus.RespondedConnect:
|
||||
// send another response
|
||||
hs.SendConnectResponse(false);
|
||||
break;
|
||||
default:
|
||||
// weird
|
||||
LogWarning("Weird situation; Connect() already in progress to remote endpoint; but hs status is " + hs.Status);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NetConnection conn = new NetConnection(this, remoteEndpoint);
|
||||
conn.m_approvalMessage = approvalMessage;
|
||||
conn.m_localHailMessage = hailMessage;
|
||||
|
||||
// handle on network thread
|
||||
conn.m_connectRequested = true;
|
||||
conn.m_connectionInitiator = true;
|
||||
|
||||
m_connections.Add(conn);
|
||||
m_connectionLookup[remoteEndpoint] = conn;
|
||||
m_handshakes.Add(remoteEndpoint, conn);
|
||||
|
||||
return conn;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an existing connection
|
||||
/// </summary>
|
||||
public bool SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod deliveryMethod)
|
||||
#if DEBUG
|
||||
public void RawSend(byte[] arr, int offset, int length, IPEndPoint destination)
|
||||
{
|
||||
return SendMessage(msg, recipient, deliveryMethod, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an existing connection
|
||||
/// </summary>
|
||||
/// <param name="msg">The NetOutgoingMessage to send</param>
|
||||
/// <param name="recipient">The recipient connection</param>
|
||||
/// <param name="deliveryMethod">How to deliver the message</param>
|
||||
/// <param name="channel">Delivery channel (0-31)</param>
|
||||
/// <returns>True if the message was queued for delivery, else false</returns>
|
||||
public bool SendMessage(NetOutgoingMessage msg, NetConnection recipient, NetDeliveryMethod deliveryMethod, int channel)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent! To send to multiple recipients use SendMessage(... IEnumerable<NetConnection...)");
|
||||
if (channel < 0 || channel > 63)
|
||||
throw new NetException("Channel must be between 0 and 63");
|
||||
if (channel != 0 && (deliveryMethod == NetDeliveryMethod.Unreliable || deliveryMethod == NetDeliveryMethod.ReliableUnordered))
|
||||
throw new NetException("Channel must be 0 for Unreliable and ReliableUnordered");
|
||||
|
||||
if (m_status != NetPeerStatus.Running)
|
||||
return false;
|
||||
|
||||
return recipient.SendMessage(msg, deliveryMethod, channel);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to a number of existing connections; returns true if all recipients were sent the message
|
||||
/// </summary>
|
||||
/// <param name="channel">Delivery channel (0-31)</param>
|
||||
public bool SendMessage(NetOutgoingMessage msg, IEnumerable<NetConnection> recipients, NetDeliveryMethod deliveryMethod, int sequenceChannel)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipients == null)
|
||||
throw new ArgumentNullException("recipients");
|
||||
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
if (sequenceChannel < 0 || sequenceChannel > NetConstants.NetChannelsPerDeliveryMethod)
|
||||
throw new NetException("Channel must be between 0 and " + (NetConstants.NetChannelsPerDeliveryMethod - 1));
|
||||
if (sequenceChannel != 0 && (deliveryMethod == NetDeliveryMethod.Unreliable || deliveryMethod == NetDeliveryMethod.ReliableUnordered))
|
||||
throw new NetException("Channel must be 0 for Unreliable and ReliableUnordered");
|
||||
|
||||
if (m_status != NetPeerStatus.Running)
|
||||
return false;
|
||||
|
||||
msg.m_wasSent = true;
|
||||
|
||||
NetMessageType tp = (NetMessageType)((int)deliveryMethod + sequenceChannel);
|
||||
|
||||
bool all = true;
|
||||
foreach (NetConnection conn in recipients)
|
||||
{
|
||||
if (!conn.EnqueueSendMessage(msg, tp))
|
||||
all = false;
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, string host, int port)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (host == null)
|
||||
throw new ArgumentNullException("host");
|
||||
|
||||
IPAddress adr = NetUtility.Resolve(host);
|
||||
if (adr == null)
|
||||
throw new NetException("Failed to resolve " + host);
|
||||
|
||||
SendUnconnectedMessage(msg, new IPEndPoint(adr, port));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to an unconnected host
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
msg.m_wasSent = true;
|
||||
|
||||
EnqueueUnconnectedMessage(msg, recipient);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to a number of unconnected hosts
|
||||
/// </summary>
|
||||
public void SendUnconnectedMessage(NetOutgoingMessage msg, IEnumerable<IPEndPoint> recipients)
|
||||
{
|
||||
if (msg == null)
|
||||
throw new ArgumentNullException("msg");
|
||||
if (recipients == null)
|
||||
throw new ArgumentNullException("recipients");
|
||||
if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
msg.m_wasSent = true;
|
||||
|
||||
foreach (IPEndPoint rec in recipients)
|
||||
EnqueueUnconnectedMessage(msg, rec);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a discovery response message
|
||||
/// </summary>
|
||||
public void SendDiscoveryResponse(NetOutgoingMessage msg, IPEndPoint recipient)
|
||||
{
|
||||
if (recipient == null)
|
||||
throw new ArgumentNullException("recipient");
|
||||
|
||||
if (msg == null)
|
||||
msg = CreateMessage(0);
|
||||
else if (msg.IsSent)
|
||||
throw new NetException("Message has already been sent!");
|
||||
|
||||
msg.m_libType = NetMessageLibraryType.DiscoveryResponse;
|
||||
SendUnconnectedLibrary(msg, recipient);
|
||||
// wrong thread - this miiiight crash with network thread... but what's a boy to do.
|
||||
Array.Copy(arr, offset, m_sendBuffer, 0, length);
|
||||
bool unused;
|
||||
SendPacket(length, destination, 1, out unused);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Disconnects all active connections and closes the socket
|
||||
@@ -435,7 +247,6 @@ namespace Lidgren.Network
|
||||
public void Shutdown(string bye)
|
||||
{
|
||||
// called on user thread
|
||||
|
||||
if (m_socket == null)
|
||||
return; // already shut down
|
||||
|
||||
@@ -443,12 +254,5 @@ namespace Lidgren.Network
|
||||
m_shutdownReason = bye;
|
||||
m_status = NetPeerStatus.ShutdownRequested;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (m_socket == null)
|
||||
return "[NetPeer unbound]";
|
||||
return "[NetPeer bound to " + m_socket.LocalEndPoint + " " + ConnectionsCount + " connections]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user