2020-02-11 17:44:06 -05:00
|
|
|
|
using SharedLibraryCore;
|
|
|
|
|
using SharedLibraryCore.Exceptions;
|
2018-04-08 02:44:42 -04:00
|
|
|
|
using SharedLibraryCore.Interfaces;
|
2020-02-11 17:44:06 -05:00
|
|
|
|
using SharedLibraryCore.RCon;
|
2018-04-02 01:25:06 -04:00
|
|
|
|
using System;
|
2018-05-03 01:25:49 -04:00
|
|
|
|
using System.Collections.Concurrent;
|
2020-04-17 16:05:16 -04:00
|
|
|
|
using System.Collections.Generic;
|
2018-04-07 15:49:00 -04:00
|
|
|
|
using System.Linq;
|
2018-04-02 01:25:06 -04:00
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Sockets;
|
2019-02-06 21:12:35 -05:00
|
|
|
|
using System.Text;
|
2018-04-02 01:25:06 -04:00
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
2020-02-11 17:44:06 -05:00
|
|
|
|
namespace IW4MAdmin.Application.RCon
|
2018-04-02 01:25:06 -04:00
|
|
|
|
{
|
2020-02-11 17:44:06 -05:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// implementation of IRConConnection
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class RConConnection : IRConConnection
|
2018-04-02 01:25:06 -04:00
|
|
|
|
{
|
2018-09-29 15:52:22 -04:00
|
|
|
|
static readonly ConcurrentDictionary<EndPoint, ConnectionState> ActiveQueries = new ConcurrentDictionary<EndPoint, ConnectionState>();
|
2018-04-15 21:27:43 -04:00
|
|
|
|
public IPEndPoint Endpoint { get; private set; }
|
|
|
|
|
public string RConPassword { get; private set; }
|
2018-12-03 20:21:13 -05:00
|
|
|
|
|
2020-02-11 17:44:06 -05:00
|
|
|
|
private IRConParserConfiguration config;
|
|
|
|
|
private readonly ILogger _log;
|
|
|
|
|
private readonly Encoding _gameEncoding;
|
2018-04-02 01:25:06 -04:00
|
|
|
|
|
2020-02-11 17:44:06 -05:00
|
|
|
|
public RConConnection(string ipAddress, int port, string password, ILogger log, Encoding gameEncoding)
|
2018-04-02 01:25:06 -04:00
|
|
|
|
{
|
|
|
|
|
Endpoint = new IPEndPoint(IPAddress.Parse(ipAddress), port);
|
2020-02-11 17:44:06 -05:00
|
|
|
|
_gameEncoding = gameEncoding;
|
2018-04-02 01:25:06 -04:00
|
|
|
|
RConPassword = password;
|
2020-02-11 17:44:06 -05:00
|
|
|
|
_log = log;
|
2019-02-01 20:49:25 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void SetConfiguration(IRConParserConfiguration config)
|
|
|
|
|
{
|
2020-02-11 17:44:06 -05:00
|
|
|
|
this.config = config;
|
2018-04-02 01:25:06 -04:00
|
|
|
|
}
|
|
|
|
|
|
2019-02-03 21:47:05 -05:00
|
|
|
|
public async Task<string[]> SendQueryAsync(StaticHelpers.QueryType type, string parameters = "")
|
2018-04-02 01:25:06 -04:00
|
|
|
|
{
|
2018-09-29 15:52:22 -04:00
|
|
|
|
if (!ActiveQueries.ContainsKey(this.Endpoint))
|
2018-04-02 01:25:06 -04:00
|
|
|
|
{
|
2018-09-29 15:52:22 -04:00
|
|
|
|
ActiveQueries.TryAdd(this.Endpoint, new ConnectionState());
|
2018-04-02 01:25:06 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-09-29 15:52:22 -04:00
|
|
|
|
var connectionState = ActiveQueries[this.Endpoint];
|
2018-04-02 01:25:06 -04:00
|
|
|
|
|
2018-10-02 13:39:08 -04:00
|
|
|
|
#if DEBUG == true
|
2020-02-11 17:44:06 -05:00
|
|
|
|
_log.WriteDebug($"Waiting for semaphore to be released [{this.Endpoint}]");
|
2018-10-02 13:39:08 -04:00
|
|
|
|
#endif
|
|
|
|
|
// enter the semaphore so only one query is sent at a time per server.
|
|
|
|
|
await connectionState.OnComplete.WaitAsync();
|
|
|
|
|
|
|
|
|
|
var timeSinceLastQuery = (DateTime.Now - connectionState.LastQuery).TotalMilliseconds;
|
2018-09-29 22:49:12 -04:00
|
|
|
|
|
2018-10-02 13:39:08 -04:00
|
|
|
|
if (timeSinceLastQuery < StaticHelpers.FloodProtectionInterval)
|
2018-09-29 22:49:12 -04:00
|
|
|
|
{
|
2018-10-02 13:39:08 -04:00
|
|
|
|
await Task.Delay(StaticHelpers.FloodProtectionInterval - (int)timeSinceLastQuery);
|
2018-09-29 22:49:12 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connectionState.LastQuery = DateTime.Now;
|
|
|
|
|
|
2018-09-29 15:52:22 -04:00
|
|
|
|
#if DEBUG == true
|
2020-02-11 17:44:06 -05:00
|
|
|
|
_log.WriteDebug($"Semaphore has been released [{this.Endpoint}]");
|
|
|
|
|
_log.WriteDebug($"Query [{this.Endpoint},{type.ToString()},{parameters}]");
|
2018-04-02 01:25:06 -04:00
|
|
|
|
#endif
|
2018-04-09 15:17:10 -04:00
|
|
|
|
|
2018-04-23 01:43:48 -04:00
|
|
|
|
byte[] payload = null;
|
2020-02-11 17:44:06 -05:00
|
|
|
|
bool waitForResponse = config.WaitForResponse;
|
2018-04-02 01:25:06 -04:00
|
|
|
|
|
2019-02-09 16:35:13 -05:00
|
|
|
|
string convertEncoding(string text)
|
2019-09-09 18:37:57 -04:00
|
|
|
|
{
|
2019-02-09 16:35:13 -05:00
|
|
|
|
byte[] convertedBytes = Utilities.EncodingType.GetBytes(text);
|
2020-02-11 17:44:06 -05:00
|
|
|
|
return _gameEncoding.GetString(convertedBytes);
|
2019-02-06 21:12:35 -05:00
|
|
|
|
}
|
|
|
|
|
|
2019-08-30 18:24:44 -04:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
string convertedRConPassword = convertEncoding(RConPassword);
|
|
|
|
|
string convertedParameters = convertEncoding(parameters);
|
|
|
|
|
|
|
|
|
|
switch (type)
|
|
|
|
|
{
|
|
|
|
|
case StaticHelpers.QueryType.GET_DVAR:
|
|
|
|
|
waitForResponse |= true;
|
2020-02-11 17:44:06 -05:00
|
|
|
|
payload = string.Format(config.CommandPrefixes.RConGetDvar, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
|
2019-08-30 18:24:44 -04:00
|
|
|
|
break;
|
|
|
|
|
case StaticHelpers.QueryType.SET_DVAR:
|
2020-02-11 17:44:06 -05:00
|
|
|
|
payload = string.Format(config.CommandPrefixes.RConSetDvar, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
|
2019-08-30 18:24:44 -04:00
|
|
|
|
break;
|
|
|
|
|
case StaticHelpers.QueryType.COMMAND:
|
2020-02-11 17:44:06 -05:00
|
|
|
|
payload = string.Format(config.CommandPrefixes.RConCommand, convertedRConPassword, convertedParameters + '\0').Select(Convert.ToByte).ToArray();
|
2019-08-30 18:24:44 -04:00
|
|
|
|
break;
|
|
|
|
|
case StaticHelpers.QueryType.GET_STATUS:
|
|
|
|
|
waitForResponse |= true;
|
2020-02-11 17:44:06 -05:00
|
|
|
|
payload = (config.CommandPrefixes.RConGetStatus + '\0').Select(Convert.ToByte).ToArray();
|
2019-08-30 18:24:44 -04:00
|
|
|
|
break;
|
|
|
|
|
case StaticHelpers.QueryType.GET_INFO:
|
|
|
|
|
waitForResponse |= true;
|
2020-02-11 17:44:06 -05:00
|
|
|
|
payload = (config.CommandPrefixes.RConGetInfo + '\0').Select(Convert.ToByte).ToArray();
|
2019-08-30 18:24:44 -04:00
|
|
|
|
break;
|
|
|
|
|
case StaticHelpers.QueryType.COMMAND_STATUS:
|
|
|
|
|
waitForResponse |= true;
|
2020-02-11 17:44:06 -05:00
|
|
|
|
payload = string.Format(config.CommandPrefixes.RConCommand, convertedRConPassword, "status\0").Select(Convert.ToByte).ToArray();
|
2019-08-30 18:24:44 -04:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-02-06 21:12:35 -05:00
|
|
|
|
|
2019-08-30 18:24:44 -04:00
|
|
|
|
// this happens when someone tries to send something that can't be converted into a 7 bit character set
|
|
|
|
|
// e.g: emoji -> windows-1252
|
|
|
|
|
catch (OverflowException)
|
2018-04-02 01:25:06 -04:00
|
|
|
|
{
|
2019-08-30 18:24:44 -04:00
|
|
|
|
connectionState.OnComplete.Release(1);
|
2019-09-09 18:37:57 -04:00
|
|
|
|
throw new NetworkException($"Invalid character encountered when converting encodings - {parameters}");
|
2018-04-02 01:25:06 -04:00
|
|
|
|
}
|
|
|
|
|
|
2020-04-17 16:05:16 -04:00
|
|
|
|
byte[][] response = null;
|
2018-10-02 13:39:08 -04:00
|
|
|
|
|
2018-12-03 20:21:13 -05:00
|
|
|
|
retrySend:
|
2018-10-25 09:14:39 -04:00
|
|
|
|
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
|
2018-10-02 13:39:08 -04:00
|
|
|
|
{
|
2019-04-23 18:27:20 -04:00
|
|
|
|
DontFragment = true,
|
2018-10-25 09:14:39 -04:00
|
|
|
|
Ttl = 100,
|
2018-10-02 13:39:08 -04:00
|
|
|
|
ExclusiveAddressUse = true,
|
2018-10-25 09:14:39 -04:00
|
|
|
|
})
|
|
|
|
|
{
|
|
|
|
|
connectionState.SendEventArgs.UserToken = socket;
|
|
|
|
|
connectionState.OnSentData.Reset();
|
|
|
|
|
connectionState.OnReceivedData.Reset();
|
|
|
|
|
connectionState.ConnectionAttempts++;
|
2020-04-17 16:05:16 -04:00
|
|
|
|
connectionState.BytesReadPerSegment.Clear();
|
2018-09-29 15:52:22 -04:00
|
|
|
|
#if DEBUG == true
|
2020-02-11 17:44:06 -05:00
|
|
|
|
_log.WriteDebug($"Sending {payload.Length} bytes to [{this.Endpoint}] ({connectionState.ConnectionAttempts}/{StaticHelpers.AllowedConnectionFails})");
|
2018-09-29 15:52:22 -04:00
|
|
|
|
#endif
|
2018-10-25 09:14:39 -04:00
|
|
|
|
try
|
2018-04-02 23:11:19 -04:00
|
|
|
|
{
|
2018-10-25 09:14:39 -04:00
|
|
|
|
response = await SendPayloadAsync(payload, waitForResponse);
|
2018-11-07 21:30:11 -05:00
|
|
|
|
|
2020-04-17 16:05:16 -04:00
|
|
|
|
if ((response.Length == 0 || response[0].Length == 0) && waitForResponse)
|
2018-11-07 21:30:11 -05:00
|
|
|
|
{
|
2019-04-23 18:27:20 -04:00
|
|
|
|
throw new NetworkException("Expected response but got 0 bytes back");
|
2018-11-07 21:30:11 -05:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-25 09:14:39 -04:00
|
|
|
|
connectionState.ConnectionAttempts = 0;
|
2018-04-02 23:11:19 -04:00
|
|
|
|
}
|
2018-09-29 22:49:12 -04:00
|
|
|
|
|
2018-11-07 21:30:11 -05:00
|
|
|
|
catch
|
2018-10-25 09:14:39 -04:00
|
|
|
|
{
|
|
|
|
|
if (connectionState.ConnectionAttempts < StaticHelpers.AllowedConnectionFails)
|
|
|
|
|
{
|
|
|
|
|
await Task.Delay(StaticHelpers.FloodProtectionInterval);
|
|
|
|
|
goto retrySend;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-21 17:28:13 -04:00
|
|
|
|
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_COMMUNICATION"].FormatExt(Endpoint));
|
2018-10-25 09:14:39 -04:00
|
|
|
|
}
|
2019-09-09 18:37:57 -04:00
|
|
|
|
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (connectionState.OnComplete.CurrentCount == 0)
|
|
|
|
|
{
|
|
|
|
|
connectionState.OnComplete.Release(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-29 15:52:22 -04:00
|
|
|
|
}
|
2018-04-02 23:11:19 -04:00
|
|
|
|
|
2020-04-18 18:48:49 -04:00
|
|
|
|
if (response.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
_log.WriteWarning($"Received empty response for request [{type.ToString()}, {parameters}, {Endpoint.ToString()}]");
|
|
|
|
|
return new string[0];
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-17 16:05:16 -04:00
|
|
|
|
string responseString = type == StaticHelpers.QueryType.COMMAND_STATUS ?
|
|
|
|
|
ReassembleSegmentedStatus(response) :
|
|
|
|
|
_gameEncoding.GetString(response[0]) + '\n';
|
2018-04-11 18:24:21 -04:00
|
|
|
|
|
2020-01-13 17:51:16 -05:00
|
|
|
|
// note: not all games respond if the pasword is wrong or not set
|
2019-06-13 20:10:08 -04:00
|
|
|
|
if (responseString.Contains("Invalid password") || responseString.Contains("rconpassword"))
|
2018-09-29 15:52:22 -04:00
|
|
|
|
{
|
2018-09-29 22:49:12 -04:00
|
|
|
|
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_INVALID"]);
|
2018-09-29 15:52:22 -04:00
|
|
|
|
}
|
2018-04-02 01:25:06 -04:00
|
|
|
|
|
2020-01-13 17:51:16 -05:00
|
|
|
|
if (responseString.Contains("rcon_password"))
|
2018-09-29 15:52:22 -04:00
|
|
|
|
{
|
2018-09-29 22:49:12 -04:00
|
|
|
|
throw new NetworkException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_RCON_NOTSET"]);
|
2018-09-29 15:52:22 -04:00
|
|
|
|
}
|
2018-04-02 01:25:06 -04:00
|
|
|
|
|
2020-02-11 17:44:06 -05:00
|
|
|
|
if (responseString.Contains(config.ServerNotRunningResponse))
|
2020-01-13 17:51:16 -05:00
|
|
|
|
{
|
|
|
|
|
throw new ServerException(Utilities.CurrentLocalization.LocalizationIndex["SERVER_ERROR_NOT_RUNNING"].FormatExt(Endpoint.ToString()));
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-18 18:48:49 -04:00
|
|
|
|
string[] headerSplit = responseString.Split(type == StaticHelpers.QueryType.GET_INFO ? config.CommandPrefixes.RconGetInfoResponseHeader : config.CommandPrefixes.RConResponse);
|
2020-04-17 16:05:16 -04:00
|
|
|
|
|
2020-04-18 18:48:49 -04:00
|
|
|
|
if (headerSplit.Length != 2)
|
2020-04-17 16:05:16 -04:00
|
|
|
|
{
|
|
|
|
|
throw new NetworkException("Unexpected response header from server");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string[] splitResponse = headerSplit.Last().Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
|
2018-09-29 15:52:22 -04:00
|
|
|
|
return splitResponse;
|
|
|
|
|
}
|
2018-04-10 20:25:44 -04:00
|
|
|
|
|
2020-04-17 16:05:16 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// reassembles broken status segments into the 'correct' ordering
|
|
|
|
|
/// <remarks>this is primarily for T7, and is really only reliable for 2 segments</remarks>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="segments">array of segmented byte arrays</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public string ReassembleSegmentedStatus(byte[][] segments)
|
|
|
|
|
{
|
|
|
|
|
var splitStatusStrings = new List<string>();
|
|
|
|
|
|
|
|
|
|
foreach (byte[] segment in segments)
|
|
|
|
|
{
|
|
|
|
|
string responseString = _gameEncoding.GetString(segment, 0, segment.Length);
|
|
|
|
|
var statusHeaderMatch = config.StatusHeader.PatternMatcher.Match(responseString);
|
|
|
|
|
if (statusHeaderMatch.Success)
|
|
|
|
|
{
|
2020-04-20 11:45:58 -04:00
|
|
|
|
splitStatusStrings.Insert(0, responseString.TrimEnd('\0'));
|
2020-04-17 16:05:16 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-04-18 11:46:55 -04:00
|
|
|
|
splitStatusStrings.Add(responseString.Replace(config.CommandPrefixes.RConResponse, "").TrimEnd('\0'));
|
2020-04-17 16:05:16 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return string.Join("", splitStatusStrings);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async Task<byte[][]> SendPayloadAsync(byte[] payload, bool waitForResponse)
|
2018-09-29 15:52:22 -04:00
|
|
|
|
{
|
|
|
|
|
var connectionState = ActiveQueries[this.Endpoint];
|
2018-10-02 13:39:08 -04:00
|
|
|
|
var rconSocket = (Socket)connectionState.SendEventArgs.UserToken;
|
2018-05-03 01:25:49 -04:00
|
|
|
|
|
2018-10-02 13:39:08 -04:00
|
|
|
|
if (connectionState.ReceiveEventArgs.RemoteEndPoint == null &&
|
|
|
|
|
connectionState.SendEventArgs.RemoteEndPoint == null)
|
2018-09-29 15:52:22 -04:00
|
|
|
|
{
|
2018-10-02 13:39:08 -04:00
|
|
|
|
// setup the event handlers only once because we're reusing the event args
|
|
|
|
|
connectionState.SendEventArgs.Completed += OnDataSent;
|
|
|
|
|
connectionState.ReceiveEventArgs.Completed += OnDataReceived;
|
|
|
|
|
connectionState.SendEventArgs.RemoteEndPoint = this.Endpoint;
|
|
|
|
|
connectionState.ReceiveEventArgs.RemoteEndPoint = this.Endpoint;
|
|
|
|
|
connectionState.ReceiveEventArgs.DisconnectReuseSocket = true;
|
|
|
|
|
connectionState.SendEventArgs.DisconnectReuseSocket = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
connectionState.SendEventArgs.SetBuffer(payload);
|
2018-04-04 15:38:34 -04:00
|
|
|
|
|
2018-09-29 15:52:22 -04:00
|
|
|
|
// send the data to the server
|
2018-10-02 13:39:08 -04:00
|
|
|
|
bool sendDataPending = rconSocket.SendToAsync(connectionState.SendEventArgs);
|
2018-04-04 15:38:34 -04:00
|
|
|
|
|
2018-10-02 13:39:08 -04:00
|
|
|
|
if (sendDataPending)
|
2018-09-29 15:52:22 -04:00
|
|
|
|
{
|
2018-10-02 13:39:08 -04:00
|
|
|
|
// the send has not been completed asyncronously
|
|
|
|
|
if (!await Task.Run(() => connectionState.OnSentData.Wait(StaticHelpers.SocketTimeout)))
|
|
|
|
|
{
|
|
|
|
|
rconSocket.Close();
|
|
|
|
|
throw new NetworkException("Timed out sending data", rconSocket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-08 22:15:59 -04:00
|
|
|
|
if (!waitForResponse)
|
|
|
|
|
{
|
2020-04-17 16:05:16 -04:00
|
|
|
|
return new byte[0][];
|
2018-10-08 22:15:59 -04:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-02 13:39:08 -04:00
|
|
|
|
connectionState.ReceiveEventArgs.SetBuffer(connectionState.ReceiveBuffer);
|
2018-04-02 23:11:19 -04:00
|
|
|
|
|
2018-09-29 15:52:22 -04:00
|
|
|
|
// get our response back
|
2018-10-02 13:39:08 -04:00
|
|
|
|
bool receiveDataPending = rconSocket.ReceiveFromAsync(connectionState.ReceiveEventArgs);
|
2018-04-04 15:38:34 -04:00
|
|
|
|
|
2018-10-02 13:39:08 -04:00
|
|
|
|
if (receiveDataPending)
|
2018-09-29 15:52:22 -04:00
|
|
|
|
{
|
2020-04-17 16:05:16 -04:00
|
|
|
|
if (!await Task.Run(() => connectionState.OnReceivedData.Wait(10000)))
|
2018-10-02 13:39:08 -04:00
|
|
|
|
{
|
|
|
|
|
rconSocket.Close();
|
|
|
|
|
throw new NetworkException("Timed out waiting for response", rconSocket);
|
|
|
|
|
}
|
2018-09-29 15:52:22 -04:00
|
|
|
|
}
|
2018-04-02 01:25:06 -04:00
|
|
|
|
|
2019-04-23 18:27:20 -04:00
|
|
|
|
rconSocket.Close();
|
|
|
|
|
|
2020-04-17 16:05:16 -04:00
|
|
|
|
var responseList = new List<byte[]>();
|
|
|
|
|
int totalBytesRead = 0;
|
2018-10-02 13:39:08 -04:00
|
|
|
|
|
2020-04-17 16:05:16 -04:00
|
|
|
|
foreach (int bytesRead in connectionState.BytesReadPerSegment)
|
|
|
|
|
{
|
|
|
|
|
responseList.Add(connectionState.ReceiveBuffer
|
|
|
|
|
.Skip(totalBytesRead)
|
|
|
|
|
.Take(bytesRead)
|
|
|
|
|
.ToArray());
|
|
|
|
|
|
|
|
|
|
totalBytesRead += bytesRead;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return responseList.ToArray();
|
2018-09-29 15:52:22 -04:00
|
|
|
|
}
|
2018-04-02 01:25:06 -04:00
|
|
|
|
|
2018-09-29 15:52:22 -04:00
|
|
|
|
private void OnDataReceived(object sender, SocketAsyncEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
#if DEBUG == true
|
2020-02-11 17:44:06 -05:00
|
|
|
|
_log.WriteDebug($"Read {e.BytesTransferred} bytes from {e.RemoteEndPoint.ToString()}");
|
2018-09-29 15:52:22 -04:00
|
|
|
|
#endif
|
2020-04-17 16:05:16 -04:00
|
|
|
|
|
|
|
|
|
// this occurs when we close the socket
|
|
|
|
|
if (e.BytesTransferred == 0)
|
|
|
|
|
{
|
|
|
|
|
ActiveQueries[this.Endpoint].OnReceivedData.Set();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (sender is Socket sock)
|
|
|
|
|
{
|
|
|
|
|
var state = ActiveQueries[this.Endpoint];
|
|
|
|
|
state.BytesReadPerSegment.Add(e.BytesTransferred);
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
// we still have available data so the payload was segmented
|
|
|
|
|
if (sock.Available > 0)
|
|
|
|
|
{
|
|
|
|
|
state.ReceiveEventArgs.SetBuffer(state.ReceiveBuffer, e.BytesTransferred, state.ReceiveBuffer.Length - e.BytesTransferred);
|
|
|
|
|
|
|
|
|
|
if (!sock.ReceiveAsync(state.ReceiveEventArgs))
|
|
|
|
|
{
|
|
|
|
|
#if DEBUG == true
|
|
|
|
|
_log.WriteDebug($"Read {state.ReceiveEventArgs.BytesTransferred} synchronous bytes from {e.RemoteEndPoint.ToString()}");
|
|
|
|
|
#endif
|
|
|
|
|
// we need to increment this here because the callback isn't executed if there's no pending IO
|
|
|
|
|
state.BytesReadPerSegment.Add(state.ReceiveEventArgs.BytesTransferred);
|
|
|
|
|
ActiveQueries[this.Endpoint].OnReceivedData.Set();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ActiveQueries[this.Endpoint].OnReceivedData.Set();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
catch (ObjectDisposedException)
|
|
|
|
|
{
|
|
|
|
|
ActiveQueries[this.Endpoint].OnReceivedData.Set();
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-29 15:52:22 -04:00
|
|
|
|
}
|
2018-04-02 01:25:06 -04:00
|
|
|
|
|
2018-09-29 15:52:22 -04:00
|
|
|
|
private void OnDataSent(object sender, SocketAsyncEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
#if DEBUG == true
|
2020-02-11 17:44:06 -05:00
|
|
|
|
_log.WriteDebug($"Sent {e.Buffer?.Length} bytes to {e.ConnectSocket?.RemoteEndPoint?.ToString()}");
|
2018-09-29 15:52:22 -04:00
|
|
|
|
#endif
|
2018-10-02 13:39:08 -04:00
|
|
|
|
ActiveQueries[this.Endpoint].OnSentData.Set();
|
2018-04-02 01:25:06 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|