备份CatanBuilding瘦身独立工程
This commit is contained in:
@@ -0,0 +1,335 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using com.fpnn.proto;
|
||||
|
||||
/*
|
||||
* TCPConnection 需要在连接事件完成后,抛弃连接事件代理,避免长时间持有client的引用,导致无法释放资源
|
||||
* TCPClient 需要在状态转换后,判断是否放弃当前的TCPConnection对象,避免资源持续相互引用,无法释放
|
||||
*/
|
||||
|
||||
namespace com.fpnn
|
||||
{
|
||||
/*
|
||||
* Connection events.
|
||||
*/
|
||||
public delegate void ConnectionConnectedDelegate(Int64 connectionId, string endpoint, bool connected);
|
||||
public delegate void ConnectionCloseDelegate(Int64 connectionId, string endpoint, bool causedByError);
|
||||
|
||||
/*
|
||||
* Process server pushed quests and return answers if necessary.
|
||||
*/
|
||||
public delegate Answer QuestProcessDelegate(Int64 connectionId, string endpoint, Quest quest);
|
||||
|
||||
public interface IQuestProcessor
|
||||
{
|
||||
QuestProcessDelegate GetQuestProcessDelegate(string method);
|
||||
}
|
||||
|
||||
/*
|
||||
* Recommend that call Close() method when client instance is no longer used.
|
||||
* If this method haven't been called, the client instance will be existed until which connection is broken.
|
||||
*/
|
||||
public class TCPClient
|
||||
{
|
||||
public enum ClientStatus
|
||||
{
|
||||
Closed,
|
||||
Connecting,
|
||||
Connected
|
||||
}
|
||||
|
||||
//----------------[ fields ]-----------------------//
|
||||
|
||||
private object interLocker;
|
||||
private readonly DnsEndPoint dnsEndpoint;
|
||||
public volatile int ConnectTimeout;
|
||||
public volatile int QuestTimeout;
|
||||
public volatile bool AutoConnect;
|
||||
|
||||
private ClientStatus status;
|
||||
private ManualResetEvent syncConnectingEvent;
|
||||
private TCPConnection connection;
|
||||
private ConnectionConnectedDelegate connectConnectedDelegate;
|
||||
private ConnectionCloseDelegate connectionCloseDelegate;
|
||||
private IQuestProcessor questProcessor;
|
||||
|
||||
private common.ErrorRecorder errorRecorder;
|
||||
|
||||
//----------------[ Constructor ]-----------------------//
|
||||
public TCPClient(string host, int port, bool autoConnect = true)
|
||||
{
|
||||
interLocker = new object();
|
||||
dnsEndpoint = new DnsEndPoint(host, port);
|
||||
ConnectTimeout = 0;
|
||||
QuestTimeout = 0;
|
||||
AutoConnect = autoConnect;
|
||||
status = ClientStatus.Closed;
|
||||
syncConnectingEvent = new ManualResetEvent(false);
|
||||
|
||||
errorRecorder = ClientEngine.errorRecorder;
|
||||
}
|
||||
|
||||
public static TCPClient Create(string host, int port, bool autoConnect = true)
|
||||
{
|
||||
return new TCPClient(host, port, autoConnect);
|
||||
}
|
||||
|
||||
public static TCPClient Create(string endpoint, bool autoConnect = true)
|
||||
{
|
||||
int idx = endpoint.LastIndexOf(':');
|
||||
if (idx == -1)
|
||||
throw new ArgumentException("Invalid endpoint: " + endpoint);
|
||||
|
||||
string host = endpoint.Substring(0, idx);
|
||||
string portString = endpoint.Substring(idx + 1);
|
||||
int port = Convert.ToInt32(portString, 10);
|
||||
|
||||
return new TCPClient(host, port, autoConnect);
|
||||
}
|
||||
|
||||
//----------------[ Properties methods ]-----------------------//
|
||||
|
||||
public string Endpoint()
|
||||
{
|
||||
return dnsEndpoint.ToString();
|
||||
}
|
||||
|
||||
public ClientStatus Status()
|
||||
{
|
||||
lock (interLocker)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsConnected()
|
||||
{
|
||||
lock (interLocker)
|
||||
{
|
||||
return status == ClientStatus.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------[ Configure Operations ]-----------------------//
|
||||
public void SetConnectionConnectedDelegate(ConnectionConnectedDelegate ccd)
|
||||
{
|
||||
lock (interLocker)
|
||||
{
|
||||
connectConnectedDelegate = ccd;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetConnectionCloseDelegate(ConnectionCloseDelegate cwcd)
|
||||
{
|
||||
lock (interLocker)
|
||||
{
|
||||
connectionCloseDelegate = cwcd;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetErrorRecorder(common.ErrorRecorder recorder)
|
||||
{
|
||||
lock (interLocker)
|
||||
{
|
||||
errorRecorder = recorder;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetQuestProcessor(IQuestProcessor processor)
|
||||
{
|
||||
lock (interLocker)
|
||||
{
|
||||
questProcessor = processor;
|
||||
}
|
||||
}
|
||||
|
||||
//----------------[ Internal Configure Operations ]-----------------------//
|
||||
private void SetClientStatus(TCPConnection conn, ClientStatus newStatus)
|
||||
{
|
||||
lock (interLocker)
|
||||
{
|
||||
if (connection == conn)
|
||||
{
|
||||
status = newStatus;
|
||||
if (status == ClientStatus.Closed)
|
||||
connection = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ConfigConnectedDelegate(TCPConnection conn, ConnectionConnectedDelegate cb, ManualResetEvent finishEvent)
|
||||
{
|
||||
conn.SetConnectedDelegate((Int64 connectionId, string endpoint, bool connected) =>
|
||||
{
|
||||
if (!connected)
|
||||
SetClientStatus(conn, ClientStatus.Closed);
|
||||
|
||||
if (cb != null)
|
||||
try
|
||||
{
|
||||
cb(connectionId, endpoint, connected);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (errorRecorder != null)
|
||||
errorRecorder.RecordError("Connected event exception. Remote endpoint: " + endpoint + ".", ex);
|
||||
}
|
||||
|
||||
if (connected)
|
||||
SetClientStatus(conn, ClientStatus.Connected);
|
||||
|
||||
finishEvent.Set();
|
||||
});
|
||||
}
|
||||
|
||||
private void ConfigWillCloseDelegate(TCPConnection conn, ConnectionCloseDelegate cb)
|
||||
{
|
||||
conn.SetCloseDelegate((Int64 connectionId, string endpoint, bool causedByError) =>
|
||||
{
|
||||
SetClientStatus(conn, ClientStatus.Closed);
|
||||
|
||||
cb?.Invoke(connectionId, endpoint, causedByError);
|
||||
});
|
||||
}
|
||||
|
||||
//----------------[ Connect Operations ]-----------------------//
|
||||
private void RealConnect()
|
||||
{
|
||||
TCPConnection conn;
|
||||
lock (interLocker)
|
||||
{
|
||||
if (status != ClientStatus.Closed)
|
||||
return;
|
||||
|
||||
connection = new TCPConnection(dnsEndpoint);
|
||||
|
||||
ConfigConnectedDelegate(connection, connectConnectedDelegate, syncConnectingEvent);
|
||||
|
||||
ConfigWillCloseDelegate(connection, connectionCloseDelegate);
|
||||
|
||||
if (questProcessor != null)
|
||||
connection.SetQuestProcessor(questProcessor);
|
||||
|
||||
if (errorRecorder != null)
|
||||
connection.SetErrorRecorder(errorRecorder);
|
||||
|
||||
status = ClientStatus.Connecting;
|
||||
syncConnectingEvent.Reset();
|
||||
|
||||
conn = connection;
|
||||
}
|
||||
|
||||
conn.AsyncConnect(ConnectTimeout);
|
||||
}
|
||||
|
||||
public void AsyncConnect()
|
||||
{
|
||||
RealConnect();
|
||||
}
|
||||
|
||||
public bool SyncConnect()
|
||||
{
|
||||
RealConnect();
|
||||
syncConnectingEvent.WaitOne();
|
||||
|
||||
lock (interLocker)
|
||||
{
|
||||
return (status == ClientStatus.Connected);
|
||||
}
|
||||
}
|
||||
|
||||
public void AsyncReconnect()
|
||||
{
|
||||
Close();
|
||||
AsyncConnect();
|
||||
}
|
||||
|
||||
public bool SyncReconnect()
|
||||
{
|
||||
Close();
|
||||
return SyncConnect();
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
TCPConnection conn;
|
||||
lock (interLocker)
|
||||
{
|
||||
conn = connection;
|
||||
connection = null;
|
||||
status = ClientStatus.Closed;
|
||||
syncConnectingEvent.Set(); //-- If some threads are waiting for sync connecting finished.
|
||||
}
|
||||
|
||||
if (conn != null)
|
||||
conn.Close();
|
||||
}
|
||||
|
||||
//----------------[ Operations ]-----------------------//
|
||||
public bool SendQuest(Quest quest, IAnswerCallback callback, int timeout = 0)
|
||||
{
|
||||
if (AutoConnect)
|
||||
AsyncConnect(); //-- Auto check and reconnect if necessary.
|
||||
|
||||
TCPConnection conn = null;
|
||||
|
||||
lock (interLocker)
|
||||
{
|
||||
conn = connection;
|
||||
}
|
||||
|
||||
if (conn != null)
|
||||
{
|
||||
if (timeout == 0)
|
||||
timeout = QuestTimeout;
|
||||
|
||||
if (timeout == 0)
|
||||
timeout = ClientEngine.globalQuestTimeoutSeconds;
|
||||
|
||||
conn.SendQuest(quest, callback, timeout);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool SendQuest(Quest quest, AnswerDelegate callback, int timeout = 0)
|
||||
{
|
||||
AnswerDelegateCallback cb = new AnswerDelegateCallback(callback);
|
||||
return SendQuest(quest, cb, timeout);
|
||||
}
|
||||
|
||||
public Answer SendQuest(Quest quest, int timeout = 0)
|
||||
{
|
||||
if (quest.IsOneWay())
|
||||
{
|
||||
SendQuest(quest, (IAnswerCallback)null, timeout);
|
||||
return null;
|
||||
}
|
||||
|
||||
SyncAnswerCallback callback = new SyncAnswerCallback(quest);
|
||||
if (SendQuest(quest, callback, timeout))
|
||||
{
|
||||
return callback.GetAnswer();
|
||||
}
|
||||
|
||||
Answer answer = new Answer(quest);
|
||||
answer.FillErrorCode(ErrorCode.FPNN_EC_CORE_INVALID_CONNECTION);
|
||||
return answer;
|
||||
}
|
||||
|
||||
public void SendAnswer(Answer answer)
|
||||
{
|
||||
TCPConnection conn = null;
|
||||
|
||||
lock (interLocker)
|
||||
{
|
||||
conn = connection;
|
||||
}
|
||||
|
||||
if (conn != null)
|
||||
conn.SendAnswer(answer);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user