├── .github
├── dependabot.yml
└── workflows
│ └── dotnet.yml
├── .gitignore
├── EasyTcp4.Actions
├── ActionsCore
│ ├── Delegates.cs
│ ├── LoadedAction.cs
│ └── ReflectionCore.cs
├── EasyAction.cs
├── EasyTcp4.Actions.csproj
├── EasyTcpActionClient.cs
├── EasyTcpActionServer.cs
└── Utils
│ ├── ActionCodeUtil.cs
│ ├── ActionMessageUtil.cs
│ ├── Async
│ └── SendActionAndGetReplyAsyncUtil.cs
│ ├── EasyActionFilter.cs
│ ├── SendActionAndGetReplyUtil.cs
│ ├── SendActionUtil.cs
│ └── SendAllActionUtil.cs
├── EasyTcp4.Encryption
├── EasyTcp4.Encryption.csproj
├── EncryptedProtocolUtil.cs
└── Ssl
│ ├── PlainSslProtocol.cs
│ ├── PrefixLengthSslProtocol.cs
│ └── SslProtocol.cs
├── EasyTcp4.Test
├── Actions
│ ├── Detection.cs
│ ├── EchoAction.cs
│ ├── Events
│ │ ├── Interceptor.cs
│ │ └── OnUnknownAction.cs
│ └── Filters.cs
├── EasyTcp
│ ├── Connect
│ │ ├── Connect.cs
│ │ ├── ConnectAsync.cs
│ │ └── OnConnect.cs
│ ├── DataTransfer
│ │ ├── Compression.cs
│ │ ├── Protocols
│ │ │ ├── Delimiter.cs
│ │ │ ├── PlainTcp.cs
│ │ │ └── PrefixLength.cs
│ │ ├── SendData.cs
│ │ └── Serialization.cs
│ └── Utils
│ │ ├── ArrayUtil.cs
│ │ ├── SendAndGetReply.cs
│ │ └── StreamUtil.cs
├── EasyTcp4.Test.csproj
├── Encryption
│ └── Ssl
│ │ ├── ArrayUtil.cs
│ │ ├── Protocols
│ │ ├── PlainTcp.cs
│ │ └── PrefixLengthSsl.cs
│ │ └── StreamUtil.cs
├── TestHelper.cs
└── certificate.pfx
├── EasyTcp4.sln
├── EasyTcp4
├── ClientUtils
│ ├── ArrayUtil.cs
│ ├── Async
│ │ ├── ArrayAsyncUtil.cs
│ │ ├── ConnectAsyncUtil.cs
│ │ ├── SendAndGetReplyAsyncUtil.cs
│ │ └── StreamAsyncUtil.cs
│ ├── ConnectUtil.cs
│ ├── InformationUtil.cs
│ ├── KeepAliveUtil.cs
│ ├── SendAndGetReplyUtil.cs
│ ├── SendUtil.cs
│ └── StreamUtil.cs
├── EasyTcp4.csproj
├── EasyTcpClient.cs
├── EasyTcpServer.cs
├── LogUtils
│ └── LogUtil.cs
├── Message.cs
├── PacketUtils
│ ├── CompressionUtil.cs
│ └── IEasyPacket.cs
├── Protocols
│ ├── IEasyProtocol.cs
│ ├── ProtocolUtil.cs
│ └── Tcp
│ │ ├── DelimiterProtocol.cs
│ │ ├── PlainTcpProtocol.cs
│ │ ├── PrefixLengthProtocol.cs
│ │ └── TcpProtocol.cs
└── ServerUtils
│ ├── KeepAliveUtil.cs
│ ├── SendAllUtil.cs
│ └── StartUtil.cs
├── LICENSE
├── README.md
└── icon.png
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: nuget
4 | directory: /
5 | schedule:
6 | interval: weekly
7 | open-pull-requests-limit: 5
8 |
--------------------------------------------------------------------------------
/.github/workflows/dotnet.yml:
--------------------------------------------------------------------------------
1 | name: dotnet
2 |
3 | on: [push]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 |
9 | strategy:
10 | matrix:
11 | dotnet-version: ["6.0.x"]
12 |
13 | steps:
14 | - uses: actions/checkout@v3
15 |
16 | - name: Setup dotnet
17 | uses: actions/setup-dotnet@v2
18 | with:
19 | dotnet-version: ${{ matrix.dotnet-version }}
20 |
21 | - name: Install dependencies
22 | run: dotnet restore
23 |
24 | - name: Build
25 | run: dotnet build --configuration Release --no-restore
26 |
27 | - name: Test
28 | run: dotnet test --no-restore --verbosity normal
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.swp
2 | *.*~
3 | project.lock.json
4 | .DS_Store
5 | *.pyc
6 | nupkg/
7 |
8 | # Visual Studio Code
9 | .vscode
10 |
11 | # Rider
12 | .idea
13 |
14 | # User-specific files
15 | *.suo
16 | *.user
17 | *.userosscache
18 | *.sln.docstates
19 |
20 | # Build results
21 | [Dd]ebug/
22 | [Dd]ebugPublic/
23 | [Rr]elease/
24 | [Rr]eleases/
25 | x64/
26 | x86/
27 | build/
28 | bld/
29 | [Bb]in/
30 | [Oo]bj/
31 | [Oo]ut/
32 | msbuild.log
33 | msbuild.err
34 | msbuild.wrn
35 |
36 | # Visual Studio 2015
37 | .vs/
38 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/ActionsCore/Delegates.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using System.Threading.Tasks;
4 |
5 | namespace EasyTcp4.Actions.ActionsCore
6 | {
7 | public static class Delegates
8 | {
9 | /* All accepted delegate types */
10 | private delegate void Delegate1(object sender, Message message);
11 | private delegate void Delegate2(Message message);
12 | private delegate void Delegate3();
13 | private delegate Task Delegate4(object sender, Message message);
14 | private delegate Task Delegate5(Message message);
15 | private delegate Task Delegate6();
16 |
17 | ///
18 | /// Get delegate type for a specific action method
19 | /// Return null when method isn't a valid action method.
20 | ///
21 | internal static Type GetActionDelegateType(this MethodInfo m)
22 | {
23 | bool isVoid = m.ReturnType == typeof(void), isTask = m.ReturnType == typeof(Task);
24 | if (!isVoid && !isTask) return null;
25 |
26 | var p = m.GetParameters();
27 | if (p.Length == 1 && p[0].ParameterType == typeof(Message))
28 | return isVoid ? typeof(Delegate2) : typeof(Delegate5);
29 | if (p.Length == 2 && p[0].ParameterType == typeof(object) && p[1].ParameterType == typeof(Message))
30 | return isVoid ? typeof(Delegate1) : typeof(Delegate4);
31 | if (p.Length == 0)
32 | return isVoid ? typeof(Delegate3) : typeof(Delegate6);
33 | return null;
34 | }
35 |
36 | ///
37 | /// Execute an action mehod
38 | ///
39 | /// valid action delegate
40 | /// instance of an EasyTcpClient or EasyTcpServer
41 | /// received message
42 | internal static async Task ExecuteActionDelegate(Delegate d, object sender, Message message)
43 | {
44 | var type = d.GetType();
45 | if (type == typeof(Delegate1)) ((Delegate1)d)(sender, message);
46 | else if (type == typeof(Delegate2)) ((Delegate2)d)(message);
47 | else if (type == typeof(Delegate3)) ((Delegate3)d)();
48 | else if (type == typeof(Delegate4)) await ((Delegate4)d)(sender, message);
49 | else if (type == typeof(Delegate5)) await ((Delegate5)d)(message);
50 | else if (type == typeof(Delegate6)) await ((Delegate6)d)();
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/ActionsCore/LoadedAction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.Actions.Utils;
6 |
7 | namespace EasyTcp4.Actions.ActionsCore
8 | {
9 | public class LoadedAction
10 | {
11 | ///
12 | /// Valid action delegate
13 | ///
14 | private readonly Delegate ActionMethod;
15 |
16 | ///
17 | /// Array with action filters
18 | ///
19 | public readonly EasyActionFilter[] Filters;
20 |
21 | ///
22 | /// Create new action
23 | ///
24 | /// valid action method
25 | /// instance of the declaring class of the actionMethod, null when actionMethod is static
26 | public LoadedAction(MethodInfo actionMethod, object classInstance)
27 | {
28 | var methodType = actionMethod.GetActionDelegateType();
29 | ActionMethod = Delegate.CreateDelegate(methodType, classInstance, actionMethod);
30 | Filters = actionMethod.GetCustomAttributes().OfType().ToArray();
31 | }
32 |
33 | ///
34 | /// Trigger actionMethod
35 | ///
36 | /// instance of an EasyTcpClient or EasyTcpServer
37 | /// received message
38 | /// function that determines whether action should be executed
39 | public async Task TryExecute(object sender = null, Message message = null, Func interceptor = null)
40 | {
41 | if (interceptor?.Invoke(message) != false && HasAccess(sender, message))
42 | await Delegates.ExecuteActionDelegate(ActionMethod, sender, message);
43 | }
44 |
45 | ///
46 | /// Determines whether the remote host has access to this action based on the filter attributes
47 | ///
48 | private bool HasAccess(object sender, Message message)
49 | {
50 | if (Filters == null) return true;
51 | foreach (var filter in Filters)
52 | if (!filter.HasAccess(sender, message))
53 | return false;
54 | return true;
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/ActionsCore/ReflectionCore.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 |
6 | namespace EasyTcp4.Actions.ActionsCore
7 | {
8 | internal static class ReflectionCore
9 | {
10 | ///
11 | /// Get all methods with the action attribute from an assembly
12 | ///
13 | /// assembly with actions
14 | /// only load actions from a specific namespace
15 | /// all valid actions within an assembly
16 | internal static Dictionary GetActionsFromAssembly(Assembly assembly, string nameSpace = null)
17 | {
18 | try
19 | {
20 | var classInstances = new Dictionary();
21 | return assembly.GetTypes()
22 | .Where(t => string.IsNullOrEmpty(nameSpace) || (t.Namespace ?? "").StartsWith(nameSpace))
23 | .SelectMany(t => t.GetMethods())
24 | .Where(IsValidAction)
25 | .ToDictionary(k => k.GetCustomAttributes().OfType().First().ActionCode,
26 | v => new LoadedAction(v, GetClassInstance(v, classInstances)));
27 | }
28 | catch (MissingMethodException ex)
29 | {
30 | throw new Exception("Could not load actions, make sure that the classes with (non-static) actions do hava a parameterless constructor\n", ex);
31 | }
32 | catch (ArgumentException ex)
33 | {
34 | throw new Exception("Could not load actions, make sure that there aren't multiple actions with the same action code\n", ex);
35 | }
36 | }
37 |
38 | ///
39 | /// Determines whether method is a valid action
40 | ///
41 | private static bool IsValidAction(this MethodInfo m) =>
42 | m.GetCustomAttributes(typeof(EasyAction), false).Any() && m.GetActionDelegateType() != null;
43 |
44 | ///
45 | /// Get a class instance for an action
46 | /// New class instance is created when not available inside classInstances.
47 | ///
48 | /// valid action method
49 | /// list with already initialized classes
50 | /// null if method is static, else instance of declaring class
51 | private static object GetClassInstance(MethodInfo method, Dictionary classInstances)
52 | {
53 | if (method.IsStatic) return null; // Static actions don't need a class instance
54 |
55 | var classType = method.DeclaringType ?? throw new InvalidOperationException("Declaring class is null");
56 | if (!classInstances.TryGetValue(classType, out object instance))
57 | {
58 | instance = Activator.CreateInstance(classType);
59 | classInstances.Add(classType, instance);
60 | }
61 |
62 | return instance;
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/EasyAction.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using EasyTcp4.Actions.Utils;
3 |
4 | namespace EasyTcp4.Actions
5 | {
6 | ///
7 | /// Attribute for the actions, methods with this attribute will get detected as actions
8 | ///
9 | [AttributeUsage(AttributeTargets.Method)]
10 | public sealed class EasyAction : Attribute
11 | {
12 | ///
13 | /// ActionCode of action, used to determine action method when receiving data
14 | /// ActionCode must be unique
15 | ///
16 | public int ActionCode { get; }
17 |
18 | ///
19 | /// action code
20 | public EasyAction(int actionCode) => ActionCode = actionCode;
21 |
22 | ///
23 | /// Create action with a string as actionCode, string gets converted to an int with djb2a
24 | ///
25 | /// action code as string
26 | public EasyAction(string actionCode) => ActionCode = actionCode.ToActionCode();
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/EasyTcp4.Actions.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | EasyTcp.Actions
4 | EasyTcp.Actions
5 | Support for EasyTcp to triggering specific functions with an attribute based on received data. See github for examples.
6 | 4.0.2
7 | Job79
8 | Job79
9 | MIT
10 | https://github.com/Job79/EasyTcp
11 | https://github.com/Job79/EasyTcp
12 | Socket Networking Tcp TcpClient TcpServer Server SimpleTcp EasyTcp AsyncTcp Reflection Actions
13 | https://raw.githubusercontent.com/Job79/EasyTcp/master/icon.png
14 | netstandard2.0;net5.0;net6.0
15 |
16 | true
17 | 1591
18 | 8
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/EasyTcpActionClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.Actions.ActionsCore;
6 | using EasyTcp4.Actions.Utils;
7 | using EasyTcp4.Protocols;
8 |
9 | namespace EasyTcp4.Actions
10 | {
11 | public class EasyTcpActionClient : EasyTcpClient
12 | {
13 | ///
14 | /// Dictionary with all loaded actions of client [action code, action method]
15 | ///
16 | public Dictionary Actions = new Dictionary();
17 |
18 | ///
19 | /// Function that determines whether an action should be executed
20 | ///
21 | public Func Interceptor;
22 |
23 | ///
24 | /// Event that is fired when an unknown action is received
25 | ///
26 | public event EventHandler OnUnknownAction;
27 |
28 | ///
29 | /// Fire the OnUnknownAction event
30 | ///
31 | public void FireOnUnknownAction(Message e) => OnUnknownAction?.Invoke(this, e);
32 |
33 | ///
34 | /// Load new actions from an assembly
35 | ///
36 | /// assembly with actions
37 | /// only load actions from a specific namespace
38 | public void LoadActionsFromAssembly(Assembly assembly, string nameSpace = null)
39 | {
40 | foreach (var action in ReflectionCore.GetActionsFromAssembly(assembly, nameSpace))
41 | Actions.Add(action.Key, action.Value);
42 | }
43 |
44 | ///
45 | /// Create new EasyTcpActionClient and load actions from (calling) assembly
46 | ///
47 | ///
48 | /// assembly with actions, calling assembly if null
49 | /// only load actions from a specific namespace
50 | public EasyTcpActionClient(IEasyProtocol protocol = null, Assembly assembly = null, string nameSpace = null) : base(protocol)
51 | {
52 | LoadActionsFromAssembly(assembly ?? Assembly.GetCallingAssembly(), nameSpace);
53 | OnDataReceiveAsync += async (sender, message) =>
54 | {
55 | try { await ExecuteAction(message.ConvertToActionMessage()); }
56 | catch (Exception ex) { FireOnError(ex); }
57 | };
58 | }
59 |
60 | ///
61 | /// Execute action
62 | ///
63 | /// message with an action code
64 | public async Task ExecuteAction(Message message)
65 | {
66 | if (Actions.TryGetValue(message.GetActionCode(), out var action)) await action.TryExecute(this, message, Interceptor);
67 | else OnUnknownAction?.Invoke(this, message);
68 | }
69 |
70 | ///
71 | /// Execute action
72 | ///
73 | ///
74 | /// message without action code
75 | public Task ExecuteAction(int actionCode, Message message = null)
76 | => ExecuteAction((message ?? new Message()).SetActionCode(actionCode));
77 |
78 | ///
79 | /// Execute action
80 | ///
81 | ///
82 | /// message without action code
83 | public Task ExecuteAction(string actionCode, Message message = null)
84 | => ExecuteAction(actionCode.ToActionCode(), message);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/EasyTcpActionServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reflection;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.Actions.ActionsCore;
6 | using EasyTcp4.Actions.Utils;
7 | using EasyTcp4.Protocols;
8 |
9 | namespace EasyTcp4.Actions
10 | {
11 | public class EasyTcpActionServer : EasyTcpServer
12 | {
13 | ///
14 | /// Dictionary with all loaded actions of server [action code, action method]
15 | ///
16 | public Dictionary Actions = new Dictionary();
17 |
18 | ///
19 | /// Function that determines whether an action should be executed
20 | ///
21 | public Func Interceptor;
22 |
23 | ///
24 | /// Event that is fired when an unknown action is received
25 | ///
26 | public event EventHandler OnUnknownAction;
27 |
28 | ///
29 | /// Fire the OnUnknownAction event
30 | ///
31 | public void FireOnUnknownAction(Message e) => OnUnknownAction?.Invoke(this, e);
32 |
33 | ///
34 | /// Load new actions from an assembly
35 | ///
36 | /// assembly with actions
37 | /// only load actions from a specific namespace
38 | public void LoadActionsFromAssembly(Assembly assembly, string nameSpace = null)
39 | {
40 | foreach (var action in ReflectionCore.GetActionsFromAssembly(assembly, nameSpace))
41 | Actions.Add(action.Key, action.Value);
42 | }
43 |
44 | ///
45 | /// Create new EasyTcpActionServer and load actions from (calling) assembly
46 | ///
47 | ///
48 | /// assembly with actions, calling assembly if null
49 | /// only load actions from a specific namespace
50 | public EasyTcpActionServer(IEasyProtocol protocol = null, Assembly assembly = null, string nameSpace = null) : base(protocol)
51 | {
52 | LoadActionsFromAssembly(assembly ?? Assembly.GetCallingAssembly(), nameSpace);
53 | OnDataReceiveAsync += async (sender, message) =>
54 | {
55 | try { await ExecuteAction(message.ConvertToActionMessage()); }
56 | catch (Exception ex) { FireOnError(ex); }
57 | };
58 | }
59 |
60 | ///
61 | /// Execute action
62 | ///
63 | /// message with an action code
64 | public async Task ExecuteAction(Message message)
65 | {
66 | if (Actions.TryGetValue(message.GetActionCode(), out var action)) await action.TryExecute(this, message, Interceptor);
67 | else OnUnknownAction?.Invoke(this, message);
68 | }
69 |
70 | ///
71 | /// Execute action
72 | ///
73 | ///
74 | /// message without action code
75 | public Task ExecuteAction(int actionCode, Message message = null)
76 | => ExecuteAction((message ?? new Message()).SetActionCode(actionCode));
77 |
78 | ///
79 | /// Execute action
80 | ///
81 | ///
82 | /// message without action code
83 | public Task ExecuteAction(string actionCode, Message message = null)
84 | => ExecuteAction(actionCode.ToActionCode(), message);
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/Utils/ActionCodeUtil.cs:
--------------------------------------------------------------------------------
1 | namespace EasyTcp4.Actions.Utils
2 | {
3 | public static class ActionCodeUtil
4 | {
5 | ///
6 | /// Convert string to actionCode with the djb2a algorithm
7 | /// The djb2a algorithm can be found here: http://www.cse.yorku.ca/~oz/hash.html
8 | ///
9 | /// action string
10 | /// hash of action string (action code)
11 | public static int ToActionCode(this string str)
12 | {
13 | int hash = 5381;
14 | foreach (var t in str) hash = ((hash << 5) + hash) ^ (byte)t;
15 | return hash;
16 | }
17 |
18 | ///
19 | /// Determines whether specified action string is equal to an action code
20 | ///
21 | public static bool IsEqualToAction(this int actionCode, string str)
22 | => actionCode == str.ToActionCode();
23 |
24 | ///
25 | /// Determines whether a message holds a specic action code
26 | ///
27 | /// message with actionCode
28 | /// action string
29 | public static bool IsAction(this Message message, string str)
30 | => message.GetActionCode().IsEqualToAction(str);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/Utils/ActionMessageUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EasyTcp4.Actions.Utils
4 | {
5 | public static class ActionMessageUtil
6 | {
7 | ///
8 | /// Store the value of an action code inside a message
9 | ///
10 | public static Message SetActionCode(this Message message, int actionCode)
11 | {
12 | message.MetaData["ActionCode"] = actionCode;
13 | return message;
14 | }
15 |
16 | ///
17 | /// Get action code from message
18 | ///
19 | public static int GetActionCode(this Message message)
20 | => message.MetaData.TryGetValue("ActionCode", out object actionCode) ?
21 | actionCode as int? ?? 0 : throw new Exception("Message doesn't have an action code");
22 |
23 | ///
24 | /// Get action code from received message and set message attribute + remove action code from data
25 | ///
26 | internal static Message ConvertToActionMessage(this Message message)
27 | {
28 | message.SetActionCode(BitConverter.ToInt32(message.Data, 0));
29 |
30 | if (message.Data.Length <= 4) message.Data = null;
31 | else
32 | {
33 | #if (NETCOREAPP3_1 || NET5_0 || NET6_0)
34 | message.Data = message.Data[4..]; // More optimized solution
35 | #else
36 | var data = new byte[message.Data.Length - 4];
37 | Buffer.BlockCopy(message.Data, 4, data, 0, data.Length);
38 | message.Data = data;
39 | #endif
40 | }
41 |
42 | return message;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/Utils/Async/SendActionAndGetReplyAsyncUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.Threading.Tasks;
4 | using EasyTcp4.ClientUtils.Async;
5 | using EasyTcp4.PacketUtils;
6 |
7 | namespace EasyTcp4.Actions.Utils.Async
8 | {
9 | public static class SendActionAndGetReplyAsyncUtil
10 | {
11 | ///
12 | /// Send action to remote host. Then wait and return the reply
13 | ///
14 | ///
15 | /// action code
16 | /// data to send to remote host
17 | /// maximum time to wait for a reply, return null when time expires
18 | /// compress data using deflate if set to true
19 | /// received reply
20 | public static async Task SendActionAndGetReplyAsync(this EasyTcpClient client, int action, byte[] data = null,
21 | TimeSpan? timeout = null, bool compression = false)
22 | {
23 | if (compression && data != null) data = CompressionUtil.Compress(data);
24 | return await client.SendAndGetReplyAsync(timeout, BitConverter.GetBytes(action), data);
25 | }
26 |
27 | ///
28 | /// Send action to remote host. Then wait and return the reply
29 | ///
30 | ///
31 | /// action code as string
32 | /// data to send to remote host
33 | /// maximum time to wait for a reply, return null when time expires
34 | /// compress data using deflate if set to true
35 | /// received reply
36 | public static async Task SendActionAndGetReplyAsync(this EasyTcpClient client, string action, byte[] data = null,
37 | TimeSpan? timeout = null, bool compression = false) =>
38 | await client.SendActionAndGetReplyAsync(action.ToActionCode(), data, timeout, compression);
39 |
40 | ///
41 | /// Send action to remote host. Then wait and return the reply
42 | ///
43 | ///
44 | /// action code
45 | /// data to send to remote host
46 | /// maximum time to wait for a reply, return null when time expires
47 | /// encoding type (default: UTF8)
48 | /// compress data using deflate if set to true
49 | /// received reply
50 | public static async Task SendActionAndGetReplyAsync(this EasyTcpClient client, int action, string data,
51 | TimeSpan? timeout = null, Encoding encoding = null, bool compression = false)
52 | => await client.SendActionAndGetReplyAsync(action, (encoding ?? Encoding.UTF8).GetBytes(data), timeout, compression);
53 |
54 | ///
55 | /// Send action to remote host. Then wait and return the reply
56 | ///
57 | ///
58 | /// action code as string
59 | /// data to send to remote host
60 | /// maximum time to wait for a reply, return null when time expires
61 | /// encoding type (default: UTF8)
62 | /// compress data using deflate if set to true
63 | /// received reply
64 | public static async Task SendActionAndGetReplyAsync(this EasyTcpClient client, string action, string data,
65 | TimeSpan? timeout = null, Encoding encoding = null, bool compression = false)
66 | => await client.SendActionAndGetReplyAsync(action.ToActionCode(), (encoding ?? Encoding.UTF8).GetBytes(data), timeout, compression);
67 |
68 | ///
69 | /// Send action to remote host. Then wait and return the reply
70 | ///
71 | ///
72 | /// action code
73 | /// data to send to remote host
74 | /// maximum time to wait for a reply, return null when time expires
75 | /// compress data using deflate if set to true
76 | /// received reply
77 | public static async Task SendActionAndGetReplyAsync(this EasyTcpClient client, int action, IEasyPacket data,
78 | TimeSpan? timeout = null, bool compression = false)
79 | => await client.SendActionAndGetReplyAsync(action, data.Data, timeout, compression);
80 |
81 | ///
82 | /// Send action to remote host. Then wait and return the reply
83 | ///
84 | ///
85 | /// action code as string
86 | /// data to send to remote host
87 | /// maximum time to wait for a reply, return null when time expires
88 | /// compress data using deflate if set to true
89 | /// received reply
90 | public static async Task SendActionAndGetReplyAsync(this EasyTcpClient client, string action, IEasyPacket data,
91 | TimeSpan? timeout = null, bool compression = false)
92 | => await client.SendActionAndGetReplyAsync(action.ToActionCode(), data.Data, timeout, compression);
93 |
94 | ///
95 | /// Send action to remote host. Then wait and return the reply
96 | ///
97 | ///
98 | /// action code
99 | /// data to send to remote host
100 | /// maximum time to wait for a reply, return null when time expires
101 | /// compress data using deflate if set to true
102 | /// received reply
103 | public static async Task SendActionAndGetReplyAsync(this EasyTcpClient client, int action, object data,
104 | TimeSpan? timeout = null, bool compression = false)
105 | => await client.SendActionAndGetReplyAsync(action, client?.Serialize(data), timeout, compression);
106 |
107 | ///
108 | /// Send action to remote host. Then wait and return the reply
109 | ///
110 | ///
111 | /// action code as string
112 | /// data to send to remote host
113 | /// maximum time to wait for a reply, return null when time expires
114 | /// compress data using deflate if set to true
115 | /// received reply
116 | public static async Task SendActionAndGetReplyAsync(this EasyTcpClient client, string action, object data,
117 | TimeSpan? timeout = null, bool compression = false)
118 | => await client.SendActionAndGetReplyAsync(action.ToActionCode(), client?.Serialize(data), timeout, compression);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/Utils/EasyActionFilter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace EasyTcp4.Actions.Utils
4 | {
5 | public abstract class EasyActionFilter : Attribute
6 | {
7 | ///
8 | /// Determines whether client has access to an action
9 | /// Action call is aborted when function returns false.
10 | ///
11 | /// EasyTcpActionServer/EasyTcpActionClient as sender
12 | /// received action message
13 | public abstract bool HasAccess(object sender, Message message);
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/Utils/SendActionAndGetReplyUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using EasyTcp4.ClientUtils;
4 | using EasyTcp4.PacketUtils;
5 |
6 | namespace EasyTcp4.Actions.Utils
7 | {
8 | public static class SendActionAndGetReplyUtil
9 | {
10 | ///
11 | /// Send action to remote host. Then wait and return the reply
12 | ///
13 | ///
14 | /// action code
15 | /// data to send to remote host
16 | /// maximum time to wait for a reply, return null when time expires
17 | /// compress data using deflate if set to true
18 | /// received reply
19 | public static Message SendActionAndGetReply(this EasyTcpClient client, int action, byte[] data = null,
20 | TimeSpan? timeout = null, bool compression = false)
21 | {
22 | if (compression && data != null) data = CompressionUtil.Compress(data);
23 | return client.SendAndGetReply(timeout, BitConverter.GetBytes(action), data);
24 | }
25 |
26 | ///
27 | /// Send action to remote host. Then wait and return the reply
28 | ///
29 | ///
30 | /// action code as string
31 | /// data to send to remote host
32 | /// maximum time to wait for a reply, return null when time expires
33 | /// compress data using deflate if set to true
34 | /// received reply
35 | public static Message SendActionAndGetReply(this EasyTcpClient client, string action, byte[] data = null,
36 | TimeSpan? timeout = null, bool compression = false) =>
37 | client.SendActionAndGetReply(action.ToActionCode(), data, timeout, compression);
38 |
39 | ///
40 | /// Send action to remote host. Then wait and return the reply
41 | ///
42 | ///
43 | /// action code
44 | /// data to send to remote host
45 | /// maximum time to wait for a reply, return null when time expires
46 | /// encoding type (default: UTF8)
47 | /// compress data using deflate if set to true
48 | /// received reply
49 | public static Message SendActionAndGetReply(this EasyTcpClient client, int action, string data,
50 | TimeSpan? timeout = null, Encoding encoding = null, bool compression = false) =>
51 | client.SendActionAndGetReply(action, (encoding ?? Encoding.UTF8).GetBytes(data), timeout, compression);
52 |
53 | ///
54 | /// Send action to remote host. Then wait and return the reply
55 | ///
56 | ///
57 | /// action code as string
58 | /// data to send to remote host
59 | /// maximum time to wait for a reply, return null when time expires
60 | /// encoding type (default: UTF8)
61 | /// compress data using deflate if set to true
62 | /// received reply
63 | public static Message SendActionAndGetReply(this EasyTcpClient client, string action, string data,
64 | TimeSpan? timeout = null, Encoding encoding = null, bool compression = false) =>
65 | client.SendActionAndGetReply(action.ToActionCode(), (encoding ?? Encoding.UTF8).GetBytes(data), timeout,
66 | compression);
67 |
68 | ///
69 | /// Send action to remote host. Then wait and return the reply
70 | ///
71 | ///
72 | /// action code
73 | /// data to send to remote host
74 | /// maximum time to wait for a reply, return null when time expires
75 | /// compress data using deflate if set to true
76 | /// received reply
77 | public static Message SendActionAndGetReply(this EasyTcpClient client, int action, IEasyPacket data,
78 | TimeSpan? timeout = null, bool compression = false) =>
79 | client.SendActionAndGetReply(action, data.Data, timeout, compression);
80 |
81 | ///
82 | /// Send action to remote host. Then wait and return the reply
83 | ///
84 | ///
85 | /// action code as string
86 | /// data to send to remote host
87 | /// maximum time to wait for a reply, return null when time expires
88 | /// compress data using deflate if set to true
89 | /// received reply
90 | public static Message SendActionAndGetReply(this EasyTcpClient client, string action, IEasyPacket data,
91 | TimeSpan? timeout = null, bool compression = false) =>
92 | client.SendActionAndGetReply(action.ToActionCode(), data.Data, timeout, compression);
93 |
94 | ///
95 | /// Send action to remote host. Then wait and return the reply
96 | ///
97 | ///
98 | /// action code
99 | /// data to send to remote host
100 | /// maximum time to wait for a reply, return null when time expires
101 | /// compress data using deflate if set to true
102 | /// received reply
103 | public static Message SendActionAndGetReply(this EasyTcpClient client, int action, object data,
104 | TimeSpan? timeout = null, bool compression = false) =>
105 | client.SendActionAndGetReply(action, client?.Serialize(data), timeout, compression);
106 |
107 | ///
108 | /// Send action to remote host. Then wait and return the reply
109 | ///
110 | ///
111 | /// action code as string
112 | /// data to send to remote host
113 | /// maximum time to wait for a reply, return null when time expires
114 | /// compress data using deflate if set to true
115 | /// received reply
116 | public static Message SendActionAndGetReply(this EasyTcpClient client, string action, object data,
117 | TimeSpan? timeout = null, bool compression = false) =>
118 | client.SendActionAndGetReply(action.ToActionCode(), client?.Serialize(data), timeout, compression);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/Utils/SendActionUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using EasyTcp4.ClientUtils;
4 | using EasyTcp4.PacketUtils;
5 |
6 | namespace EasyTcp4.Actions.Utils
7 | {
8 | public static class SendActionUtil
9 | {
10 | ///
11 | /// Send action to remote host
12 | ///
13 | ///
14 | /// action code
15 | /// data to send to remote host
16 | /// compress data using deflate if set to true
17 | public static void SendAction(this EasyTcpClient client, int action, byte[] data = null, bool compression = false)
18 | {
19 | if (compression && data != null) data = CompressionUtil.Compress(data);
20 | client.Send(BitConverter.GetBytes(action), data);
21 | }
22 |
23 | ///
24 | /// Send action to remote host
25 | ///
26 | ///
27 | /// action code as string
28 | /// data to send to remote host
29 | /// compress data using deflate if set to true
30 | public static void SendAction(this EasyTcpClient client, string action, byte[] data = null, bool compression = false) =>
31 | client.SendAction(action.ToActionCode(), data, compression);
32 |
33 | ///
34 | /// Send action to remote host
35 | ///
36 | ///
37 | /// action code
38 | /// data to send to remote host
39 | /// encoding type (default: UTF8)
40 | /// compress data using deflate if set to true
41 | public static void SendAction(this EasyTcpClient client, int action, string data, Encoding encoding = null, bool compression = false)
42 | => client.SendAction(action, (encoding ?? Encoding.UTF8).GetBytes(data), compression);
43 |
44 | ///
45 | /// Send action to remote host
46 | ///
47 | ///
48 | /// action code as string
49 | /// data to send to remote host
50 | /// encoding type (default: UTF8)
51 | /// compress data using deflate if set to true
52 | public static void SendAction(this EasyTcpClient client, string action, string data, Encoding encoding = null, bool compression = false)
53 | => client.SendAction(action.ToActionCode(), (encoding ?? Encoding.UTF8).GetBytes(data), compression);
54 |
55 | ///
56 | /// Send action to remote host
57 | ///
58 | ///
59 | /// action code
60 | /// data to send to remote host
61 | /// compress data using deflate if set to true
62 | public static void SendAction(this EasyTcpClient client, int action, IEasyPacket data, bool compression = false)
63 | => client.SendAction(action, data.Data, compression);
64 |
65 | ///
66 | /// Send action to remote host
67 | ///
68 | ///
69 | /// action code as string
70 | /// data to send to remote host
71 | /// compress data using deflate if set to true
72 | public static void SendAction(this EasyTcpClient client, string action, IEasyPacket data, bool compression = false)
73 | => client.SendAction(action.ToActionCode(), data.Data, compression);
74 |
75 | ///
76 | /// Send action to remote host
77 | ///
78 | ///
79 | /// action code
80 | /// data to send to remote host
81 | /// compress data using deflate if set to true
82 | public static void SendAction(this EasyTcpClient client, int action, object data, bool compression = false)
83 | => client.SendAction(action, client?.Serialize(data), compression);
84 |
85 | ///
86 | /// Send action to remote host
87 | ///
88 | ///
89 | /// action code as string
90 | /// data to send to remote host
91 | /// compress data using deflate if set to true
92 | public static void SendAction(this EasyTcpClient client, string action, object data, bool compression = false)
93 | => client.SendAction(action.ToActionCode(), client?.Serialize(data), compression);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/EasyTcp4.Actions/Utils/SendAllActionUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using EasyTcp4.PacketUtils;
4 | using EasyTcp4.ServerUtils;
5 |
6 | namespace EasyTcp4.Actions.Utils
7 | {
8 | public static class SendAllActionUtil
9 | {
10 | ///
11 | /// Send action to connected clients
12 | ///
13 | ///
14 | /// action code
15 | /// data to send to connected clients
16 | /// compress data using Deflate if set to true
17 | public static void SendAllAction(this EasyTcpServer server, int action, byte[] data = null, bool compression = false)
18 | {
19 | if (compression && data != null) data = CompressionUtil.Compress(data);
20 | server.SendAll(BitConverter.GetBytes(action), data);
21 | }
22 |
23 | ///
24 | /// Send action to connected clients
25 | ///
26 | ///
27 | /// action code as string
28 | /// data to send to connected clients
29 | /// compress data using Deflate if set to true
30 | public static void SendAllAction(this EasyTcpServer server, string action, byte[] data = null, bool compression = false)
31 | => server.SendAllAction(action.ToActionCode(), data, compression);
32 |
33 | ///
34 | /// Send action to connected clients
35 | ///
36 | ///
37 | /// action code
38 | /// data to send to connected clients
39 | /// encoding type (default: UTF8)
40 | /// compress data using deflate if set to true
41 | public static void SendAllAction(this EasyTcpServer server, int action, string data, Encoding encoding = null, bool compression = false)
42 | => server.SendAllAction(action, (encoding ?? Encoding.UTF8).GetBytes(data), compression);
43 |
44 | ///
45 | /// Send action to connected clients
46 | ///
47 | ///
48 | /// action code as string
49 | /// data to send to connected clients
50 | /// encoding type (default: UTF8)
51 | /// compress data using deflate if set to true
52 | public static void SendAllAction(this EasyTcpServer server, string action, string data,
53 | Encoding encoding = null, bool compression = false)
54 | => server.SendAllAction(action.ToActionCode(), (encoding ?? Encoding.UTF8).GetBytes(data), compression);
55 |
56 | ///
57 | /// Send action to connected clients
58 | ///
59 | ///
60 | /// action code
61 | /// data to send to connected clients
62 | /// compress data using deflate if set to true
63 | public static void SendAllAction(this EasyTcpServer server, int action, IEasyPacket data, bool compression = false)
64 | => server.SendAllAction(action, data.Data, compression);
65 |
66 | ///
67 | /// Send action to connected clients
68 | ///
69 | ///
70 | /// action code as string
71 | /// data to send to connected clients
72 | /// compress data using deflate if set to true
73 | public static void SendAllAction(this EasyTcpServer server, string action, IEasyPacket data, bool compression = false)
74 | => server.SendAllAction(action.ToActionCode(), data.Data, compression);
75 |
76 | ///
77 | /// Send action to connected clients
78 | ///
79 | ///
80 | /// action code
81 | /// data to send to connected clients
82 | /// compress data using deflate if set to true
83 | public static void SendAllAction(this EasyTcpServer server, int action, object data, bool compression = false)
84 | => server.SendAllAction(action, server?.Serialize(data), compression);
85 |
86 | ///
87 | /// Send action to connected clients
88 | ///
89 | ///
90 | /// action code as string
91 | /// data to send to connected clients
92 | /// compress data using deflate if set to true
93 | public static void SendAllAction(this EasyTcpServer server, string action, object data, bool compression = false)
94 | => server.SendAllAction(action.ToActionCode(), server?.Serialize(data), compression);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/EasyTcp4.Encryption/EasyTcp4.Encryption.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | EasyTcp.Encryption
4 | EasyTcp.Encryption
5 | Ssl support for EasyTcp and EasyTcp.Actions
6 | 4.0.1
7 | Job79
8 | Job79
9 | MIT
10 | https://github.com/Job79/EasyTcp
11 | https://github.com/Job79/EasyTcp
12 | Socket Networking Tcp TcpClient TcpServer Server SimpleTcp EasyTcp AsyncTcp Ssl Encryption
13 | https://raw.githubusercontent.com/Job79/EasyTcp/master/icon.png
14 | netstandard2.0;net5.0;net6.0
15 |
16 | true
17 | 1591
18 | 8
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/EasyTcp4.Encryption/EncryptedProtocolUtil.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Cryptography.X509Certificates;
2 | using System.Text;
3 | using EasyTcp4.Encryption.Ssl;
4 | using EasyTcp4.Protocols.Tcp;
5 |
6 | namespace EasyTcp4.Encryption
7 | {
8 | public static class EncryptedProtocolUtil
9 | {
10 | ///
11 | /// Use the ssl prefix length protocol
12 | ///
13 | ///
14 | /// domain name of server, must be the same as in server certificate
15 | /// determines whether the client can connect to servers that use an invalid certificate
16 | /// maximimum amount of bytes for one message
17 | public static T UseSsl(this T client, string serverName, bool acceptInvalidCertificates = false, int maxMessageLength = ushort.MaxValue)
18 | where T : EasyTcpClient
19 | {
20 | client.Protocol = new PrefixLengthSslProtocol(serverName, acceptInvalidCertificates, maxMessageLength);
21 | return client;
22 | }
23 |
24 | ///
25 | /// Use the ssl prefix length protocol
26 | ///
27 | ///
28 | /// server certificate
29 | /// maximimum amount of bytes for one message
30 | public static T UseServerSsl(this T server, X509Certificate certificate, int maxMessageLength = ushort.MaxValue)
31 | where T : EasyTcpServer
32 | {
33 | server.Protocol = new PrefixLengthSslProtocol(certificate, maxMessageLength);
34 | return server;
35 | }
36 |
37 | ///
38 | /// Use the plain ssl protocol
39 | ///
40 | ///
41 | /// domain name of server, must be the same as in server certificate
42 | /// determines whether the client can connect to servers that use an invalid certificate
43 | /// size of the receive buffer, maximum size of a message
44 | public static T UsePlainTcp(this T client, string serverName, bool acceptInvalidCertificates = false, int bufferSize = PlainTcpProtocol.DefaultBufferSize)
45 | where T : EasyTcpClient
46 | {
47 | client.Protocol = new PlainSslProtocol(serverName, acceptInvalidCertificates, bufferSize);
48 | return client;
49 | }
50 |
51 | ///
52 | /// Use the plain ssl protocol
53 | ///
54 | ///
55 | /// server certificate
56 | /// size of the receive buffer, maximum size of a message
57 | public static T UseServerPlainTcp(this T server, X509Certificate certificate, int bufferSize = PlainTcpProtocol.DefaultBufferSize)
58 | where T : EasyTcpServer
59 | {
60 | server.Protocol = new PlainSslProtocol(certificate, bufferSize);
61 | return server;
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/EasyTcp4.Encryption/Ssl/PlainSslProtocol.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Security.Cryptography.X509Certificates;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.Protocols;
6 |
7 | namespace EasyTcp4.Encryption.Ssl
8 | {
9 | ///
10 | /// Protocol that doesn't implements any framing
11 | /// Useful when communicating with an already existing tcp server/client
12 | ///
13 | public class PlainSslProtocol : SslProtocol
14 | {
15 | ///
16 | /// Default bufferSize when not specified
17 | ///
18 | public const int DefaultBufferSize = 1024;
19 |
20 | ///
21 | /// Size of the receive buffer
22 | ///
23 | public sealed override int BufferSize { get; protected set; }
24 |
25 | ///
26 | /// server certificate
27 | /// size of the receive buffer, maximum size of a message
28 | public PlainSslProtocol(X509Certificate certificate, int bufferSize = DefaultBufferSize) : base(certificate)
29 | => BufferSize = bufferSize;
30 |
31 | ///
32 | /// domain name of server, must be the same as in server certificate
33 | /// determines whether the client can connect to servers that use an invalid certificate
34 | /// size of the receive buffer, maximum size of a message
35 | public PlainSslProtocol(string serverName, bool acceptInvalidCertificates = false, int bufferSize = DefaultBufferSize)
36 | : base(serverName, acceptInvalidCertificates)
37 | => BufferSize = bufferSize;
38 |
39 | ///
40 | /// Send message to remote host
41 | ///
42 | public override void SendMessage(EasyTcpClient client, params byte[][] messageData)
43 | {
44 | if (messageData == null || messageData.Length == 0) return;
45 | if (client?.BaseSocket == null || !client.BaseSocket.Connected)
46 | throw new Exception("Could not send data: client not connected or null");
47 |
48 | // Calculate length of message
49 | var messageLength = messageData.Sum(t => t?.Length ?? 0);
50 | if (messageLength == 0) return;
51 | byte[] message = new byte[messageLength];
52 |
53 | // Add data to message
54 | int offset = 0;
55 | foreach (var d in messageData)
56 | {
57 | if (d == null) continue;
58 | Buffer.BlockCopy(d, 0, message, offset, d.Length);
59 | offset += d.Length;
60 | }
61 |
62 | // Send data
63 | client.FireOnDataSend(message);
64 | SslStream.Write(message, 0, message.Length);
65 | }
66 |
67 | ///
68 | /// Handle received data, this function should trigger the OnDataReceive event of the passed client
69 | ///
70 | /// received data
71 | /// amount of received bytes
72 | /// client that received the data
73 | public override async Task OnDataReceive(byte[] data, int receivedBytes, EasyTcpClient client)
74 | {
75 | #if (NETCOREAPP3_1 || NET5_0 || NET6_0)
76 | await client.DataReceiveHandler(new Message(data[..receivedBytes], client)); // More optimized solution
77 | #else
78 | byte[] receivedData = new byte[receivedBytes];
79 | Buffer.BlockCopy(data, 0, receivedData, 0, receivedBytes);
80 | await client.DataReceiveHandler(new Message(receivedData, client));
81 | #endif
82 | }
83 |
84 | ///
85 | /// Return new instance of protocol
86 | ///
87 | public override IEasyProtocol Clone()
88 | {
89 | if (Certificate != null) return new PlainSslProtocol(Certificate, BufferSize);
90 | else return new PlainSslProtocol(ServerName, AcceptInvalidCertificates, BufferSize);
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/EasyTcp4.Encryption/Ssl/PrefixLengthSslProtocol.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Security.Cryptography.X509Certificates;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.Protocols;
6 |
7 | namespace EasyTcp4.Encryption.Ssl
8 | {
9 | ///
10 | /// Protocol that determines the length of a message based on a small header
11 | /// Header is an int as byte[] with the length of the message.
12 | ///
13 | public class PrefixLengthSslProtocol : SslProtocol
14 | {
15 | ///
16 | /// Size of the receive buffer
17 | ///
18 | public override int BufferSize { get; protected set; }
19 |
20 | ///
21 | /// Offset of the receive buffer, where to start saving the received data
22 | ///
23 | public override int BufferOffset { get; protected set; }
24 |
25 | ///
26 | /// The maximum amount of bytes to receive in the receive buffer
27 | ///
28 | public override int BufferCount
29 | {
30 | get => ReceivedHeader ?
31 | Math.Min(BufferSize - BufferOffset, 10240) : 4; // Do not receive more than 10240 bytes at once
32 | protected set { }
33 | }
34 |
35 | ///
36 | /// Determines whether the header for the message is received
37 | ///
38 | protected bool ReceivedHeader;
39 |
40 | ///
41 | /// Maximimum amount of bytes for one message
42 | ///
43 | protected readonly int MaxMessageLength;
44 |
45 | ///
46 | /// server certificate
47 | /// maximimum amount of bytes for one message
48 | public PrefixLengthSslProtocol(X509Certificate certificate, int maxMessageLength = ushort.MaxValue) : base(certificate)
49 | {
50 | MaxMessageLength = maxMessageLength;
51 | BufferSize = 4;
52 | BufferCount = BufferSize;
53 | }
54 |
55 | ///
56 | /// domain name of server, must be the same as in server certificate
57 | /// determines whether the client can connect to servers that use an invalid certificate
58 | /// maximimum amount of bytes for one message
59 | public PrefixLengthSslProtocol(string serverName, bool acceptInvalidCertificates = false,
60 | int maxMessageLength = ushort.MaxValue) : base(serverName, acceptInvalidCertificates)
61 | {
62 | MaxMessageLength = maxMessageLength;
63 | BufferSize = 4;
64 | BufferCount = BufferSize;
65 | }
66 |
67 |
68 | ///
69 | /// Send message to remote host
70 | ///
71 | public override void SendMessage(EasyTcpClient client, params byte[][] messageData)
72 | {
73 | if (messageData == null || messageData.Length == 0) return;
74 | if (client?.BaseSocket == null || !client.BaseSocket.Connected)
75 | throw new Exception("Could not send data: client not connected or null");
76 |
77 | // Calculate length of message
78 | var messageLength = messageData.Sum(t => t?.Length ?? 0);
79 | if (messageLength == 0) return;
80 | if (messageLength > MaxMessageLength)
81 | throw new ArgumentException("Could not send message: message is too big, increase maxMessageLength or send message with SendArray/SendStream");
82 |
83 | // Add header to message
84 | int offset = 4;
85 | byte[] message = new byte[offset + messageLength];
86 | Buffer.BlockCopy(BitConverter.GetBytes((int)messageLength), 0, message, 0, offset);
87 |
88 | // Add data to message
89 | foreach (var d in messageData)
90 | {
91 | if (d == null) continue;
92 | Buffer.BlockCopy(d, 0, message, offset, d.Length);
93 | offset += d.Length;
94 | }
95 |
96 |
97 | // Send data
98 | // Remove prefix in OnDataSend with an offset
99 | client.FireOnDataSend(message, 4);
100 | SslStream.Write(message, 0, message.Length);
101 | }
102 |
103 | ///
104 | /// Handle received data, this function should trigger the OnDataReceive event of the passed client
105 | ///
106 | /// received data
107 | /// amount of received bytes
108 | /// client that received the data
109 | public override async Task OnDataReceive(byte[] data, int receivedBytes, EasyTcpClient client)
110 | {
111 | if (!ReceivedHeader)
112 | {
113 | ReceivedHeader = true;
114 | BufferSize = BitConverter.ToInt32(data, 0);
115 | if (BufferSize == 0) client.Dispose();
116 | }
117 | else
118 | {
119 | if (BufferOffset + receivedBytes == BufferSize)
120 | {
121 | ReceivedHeader = false;
122 | BufferSize = 4;
123 | BufferOffset = 0;
124 | await client.DataReceiveHandler(new Message(data, client));
125 | }
126 | else BufferOffset += receivedBytes;
127 | }
128 | }
129 |
130 | ///
131 | /// Return new instance of protocol
132 | ///
133 | /// new object
134 | public override IEasyProtocol Clone()
135 | {
136 | if (Certificate != null) return new PrefixLengthSslProtocol(Certificate, MaxMessageLength);
137 | else return new PrefixLengthSslProtocol(ServerName, AcceptInvalidCertificates, MaxMessageLength);
138 | }
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/Actions/Detection.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using EasyTcp4.Actions;
3 | using EasyTcp4;
4 | using NUnit.Framework;
5 | using DetectTestActions;
6 |
7 | namespace EasyTcp4.Test.Actions
8 | {
9 | public class Detection
10 | {
11 | [Test]
12 | public void DetectAllTypes()
13 | {
14 | var client = new EasyTcpActionClient(nameSpace: "DetectTestActions");
15 | Assert.AreEqual(7, client.Actions.Count);
16 | }
17 |
18 | [Test]
19 | public async Task DetectAndExecuteTypes()
20 | {
21 | var server = new EasyTcpActionServer(nameSpace: "DetectTestActions");
22 | for (int i = 1; i <= 7; i++) await server.ExecuteAction(i);
23 | Assert.AreEqual(7, TestActions.Counter);
24 | }
25 | }
26 | }
27 |
28 | namespace DetectTestActions
29 | {
30 | public class TestActions
31 | {
32 | public static int Counter;
33 |
34 | [EasyAction(1)]
35 | public void One(object sender, Message message) => Counter++;
36 |
37 | [EasyAction(2)]
38 | public void Two(Message message) => Counter++;
39 |
40 | [EasyAction(3)]
41 | public void Three() => Counter++;
42 |
43 | [EasyAction(4)]
44 | public static void StaticFour() => Counter++;
45 |
46 | [EasyAction(5)]
47 | public Task Five(object sender, Message message)
48 | {
49 | Counter++;
50 | return Task.CompletedTask;
51 | }
52 |
53 | [EasyAction(6)]
54 | public Task Six(Message message)
55 | {
56 | Counter++;
57 | return Task.CompletedTask;
58 | }
59 |
60 | [EasyAction(7)]
61 | public Task Seven()
62 | {
63 | Counter++;
64 | return Task.CompletedTask;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/Actions/EchoAction.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using EasyTcp4.Actions;
3 | using EasyTcp4.Actions.Utils.Async;
4 | using EasyTcp4.ClientUtils;
5 | using NUnit.Framework;
6 |
7 | namespace EasyTcp4.Test.Actions
8 | {
9 | public class EchoAction
10 | {
11 | [EasyAction("ECHO")]
12 | public void Echo(Message e) => e.Client.Send(e);
13 |
14 | [Test]
15 | public async Task TestEcho()
16 | {
17 | using var conn = await TestHelper.GetTestConnection(server: new EasyTcpActionServer());
18 |
19 | var reply = await conn.Client.SendActionAndGetReplyAsync("ECHO", "test echo data");
20 | Assert.AreEqual("test echo data", reply.ToString());
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/Actions/Events/Interceptor.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using EasyTcp4.Actions;
3 | using EasyTcp4.Actions.Utils;
4 | using EasyTcp4.Actions.Utils.Async;
5 | using EasyTcp4.ClientUtils;
6 | using NUnit.Framework;
7 |
8 | namespace EasyTcp4.Test.Actions.Events
9 | {
10 | public class Interceptor
11 | {
12 | [Test]
13 | public async Task InterceptorBlockRequest()
14 | {
15 | using var conn = await TestHelper.GetTestConnection(server:
16 | new EasyTcpActionServer
17 | {
18 | Interceptor = message =>
19 | {
20 | message.Client.Send("no access");
21 | return false;
22 | }
23 | });
24 |
25 | var message = await conn.Client.SendActionAndGetReplyAsync("ECHO", "has access");
26 | Assert.AreEqual("no access", message.ToString());
27 | }
28 |
29 | [Test]
30 | public async Task InterceptorAllowRequest()
31 | {
32 | using var conn = await TestHelper.GetTestConnection(server:
33 | new EasyTcpActionServer
34 | {
35 | Interceptor = message => message.IsAction("ECHO")
36 | });
37 |
38 | var message = await conn.Client.SendActionAndGetReplyAsync("ECHO", "has access");
39 | Assert.AreEqual("has access", message.ToString());
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/Actions/Events/OnUnknownAction.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using EasyTcp4.Actions;
4 | using EasyTcp4.Actions.Utils;
5 | using NUnit.Framework;
6 |
7 | namespace EasyTcp4.Test.Actions.Events
8 | {
9 | public class OnUnknownAction
10 | {
11 | [Test]
12 | public async Task TriggerOnUnknownAction()
13 | {
14 | using var conn = await TestHelper.GetTestConnection(server: new EasyTcpActionServer());
15 |
16 | int triggered = 0;
17 | ((EasyTcpActionServer)conn.Server).OnUnknownAction += (_, m)
18 | => Interlocked.Increment(ref triggered);
19 | conn.Client.SendAction("INVALIDACTION", "data");
20 |
21 | await TestHelper.WaitWhileFalse(() => triggered == 1);
22 | Assert.AreEqual(1, triggered);
23 | }
24 |
25 | [Test]
26 | public async Task NotTriggerOnUnknownAction()
27 | {
28 | using var conn = await TestHelper.GetTestConnection(server: new EasyTcpActionServer());
29 |
30 | int triggered = 0;
31 | ((EasyTcpActionServer)conn.Server).OnUnknownAction += (_, m)
32 | => Interlocked.Increment(ref triggered);
33 | conn.Client.SendActionAndGetReply("ECHO", "data");
34 |
35 | Assert.AreEqual(0, triggered);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/Actions/Filters.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using EasyTcp4.Actions;
3 | using EasyTcp4.Actions.Utils;
4 | using EasyTcp4.Actions.Utils.Async;
5 | using EasyTcp4.ClientUtils;
6 | using NUnit.Framework;
7 |
8 | namespace EasyTcp4.Test.Actions
9 | {
10 | public class Filters
11 | {
12 | [Test]
13 | public async Task FilterAllowRequest()
14 | {
15 | using var conn = await TestHelper.GetTestConnection(server: new EasyTcpActionServer());
16 |
17 | conn.Client.SendAction("LOGIN");
18 | var reply = await conn.Client.SendActionAndGetReplyAsync("AUTH");
19 | await TestHelper.WaitWhileTrue(() => reply == null);
20 | Assert.AreEqual("has access", reply.ToString());
21 | }
22 |
23 | [Test]
24 | public async Task FilterBlockRequest()
25 | {
26 | using var conn = await TestHelper.GetTestConnection(server: new EasyTcpActionServer());
27 |
28 | var reply = await conn.Client.SendActionAndGetReplyAsync("AUTH");
29 | await TestHelper.WaitWhileTrue(() => reply == null);
30 | Assert.AreEqual("no access", reply.ToString());
31 | }
32 |
33 | [EasyAction("LOGIN")]
34 | public void Login(Message message) => message.Client.Session["role"] = "user";
35 |
36 | [Authorization]
37 | [EasyAction("AUTH")]
38 | public void Auth(Message m) => m.Client.Send("has access");
39 |
40 | public class Authorization : EasyActionFilter
41 | {
42 | public override bool HasAccess(object s, Message m)
43 | {
44 | if (m.Client.Session.TryGetValue("role", out object role) && role as string == "user") return true;
45 | else
46 | {
47 | m.Client.Send("no access");
48 | return false;
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/Connect/Connect.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using EasyTcp4.ClientUtils;
3 | using EasyTcp4.ServerUtils;
4 | using NUnit.Framework;
5 |
6 | namespace EasyTcp4.Test.EasyTcp.Connect
7 | {
8 | public class Connect
9 | {
10 | [Test]
11 | public void ConnectEndpoint()
12 | {
13 | var port = TestHelper.GetPort();
14 | using var server = new EasyTcpServer().Start(port);
15 | using var client = new EasyTcpClient();
16 |
17 | Assert.IsTrue(client.Connect(new IPEndPoint(IPAddress.Loopback, port)));
18 | Assert.IsTrue(client.IsConnected());
19 | }
20 |
21 | [Test]
22 | public void ConnectIPAddress()
23 | {
24 | var port = TestHelper.GetPort();
25 | using var server = new EasyTcpServer().Start(port);
26 | using var client = new EasyTcpClient();
27 |
28 | Assert.IsTrue(client.Connect(IPAddress.Loopback, port));
29 | Assert.IsTrue(client.IsConnected());
30 | }
31 |
32 | [Test]
33 | public void ConnectIPAddressString()
34 | {
35 | var port = TestHelper.GetPort();
36 | using var server = new EasyTcpServer().Start(port);
37 | using var client = new EasyTcpClient();
38 |
39 | Assert.IsTrue(client.Connect("127.0.0.1", port));
40 | Assert.IsTrue(client.IsConnected());
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/Connect/ConnectAsync.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Threading.Tasks;
3 | using EasyTcp4.ClientUtils;
4 | using EasyTcp4.ClientUtils.Async;
5 | using EasyTcp4.ServerUtils;
6 | using NUnit.Framework;
7 |
8 | namespace EasyTcp4.Test.EasyTcp.Connect
9 | {
10 | public class ConnectAsync
11 | {
12 | [Test]
13 | public async Task ConnectAsyncEndpoint()
14 | {
15 | var port = TestHelper.GetPort();
16 | using var server = new EasyTcpServer().Start(port);
17 | using var client = new EasyTcpClient();
18 |
19 | Assert.IsTrue(await client.ConnectAsync(new IPEndPoint(IPAddress.Loopback, port)));
20 | Assert.IsTrue(client.IsConnected());
21 | }
22 |
23 | [Test]
24 | public async Task ConnectIPAddress()
25 | {
26 | var port = TestHelper.GetPort();
27 | using var server = new EasyTcpServer().Start(port);
28 | using var client = new EasyTcpClient();
29 |
30 | Assert.IsTrue(await client.ConnectAsync(IPAddress.Loopback, port));
31 | Assert.IsTrue(client.IsConnected());
32 | }
33 |
34 | [Test]
35 | public async Task ConnectIPAddressString()
36 | {
37 | var port = TestHelper.GetPort();
38 | using var server = new EasyTcpServer().Start(port);
39 | using var client = new EasyTcpClient();
40 |
41 | Assert.IsTrue(await client.ConnectAsync("127.0.0.1", port));
42 | Assert.IsTrue(client.IsConnected());
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/Connect/OnConnect.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using EasyTcp4.ClientUtils.Async;
5 | using EasyTcp4.ServerUtils;
6 | using NUnit.Framework;
7 |
8 | namespace EasyTcp4.Test.EasyTcp.Connect
9 | {
10 | public class OnConnect
11 | {
12 | [Test]
13 | public async Task OnConnectClient()
14 | {
15 | var port = TestHelper.GetPort();
16 | using var server = new EasyTcpServer().Start(port);
17 | using var client = new EasyTcpClient();
18 |
19 | int raised = 0;
20 | client.OnConnect += (_, c) => Interlocked.Increment(ref raised);
21 | await client.ConnectAsync(IPAddress.Loopback, port);
22 |
23 | Assert.AreEqual(1, raised);
24 | }
25 |
26 | [Test]
27 | public async Task OnConnectServer()
28 | {
29 | var port = TestHelper.GetPort();
30 | using var server = new EasyTcpServer().Start(port);
31 | using var client = new EasyTcpClient();
32 |
33 | int raised = 0;
34 | server.OnConnect += (_, c) => Interlocked.Increment(ref raised);
35 | await client.ConnectAsync(IPAddress.Loopback, port);
36 |
37 | await TestHelper.WaitWhileFalse(() => raised == 1);
38 | Assert.AreEqual(1, raised);
39 | }
40 |
41 | [Test]
42 | public async Task OnConnectServerMultipleConnections()
43 | {
44 | var port = TestHelper.GetPort();
45 | using var server = new EasyTcpServer().Start(port);
46 |
47 | int raised = 0;
48 | server.OnConnect += (_, c) => Interlocked.Increment(ref raised);
49 | for (int i = 0; i < 5; i++)
50 | {
51 | using var client = new EasyTcpClient();
52 | await client.ConnectAsync(IPAddress.Loopback, port);
53 | }
54 |
55 | await TestHelper.WaitWhileFalse(() => raised == 5);
56 | Assert.AreEqual(raised, 5);
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/DataTransfer/Compression.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using EasyTcp4.ClientUtils;
4 | using NUnit.Framework;
5 | using EasyTcp4.PacketUtils;
6 |
7 | namespace EasyTcp4.Test.EasyTcp.DataTransfer
8 | {
9 | public class Compression
10 | {
11 | [Test]
12 | public async Task SendCompressedArray()
13 | {
14 | using var conn = await TestHelper.GetTestConnection();
15 |
16 | int receivedBytes = 0, receivedBytesDecompressed = 0;
17 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Length);
18 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytesDecompressed, m.Decompress().Data.Length);
19 | conn.Client.Send(new byte[1000], compression: true);
20 |
21 | await TestHelper.WaitWhileFalse(()=>receivedBytesDecompressed == 1000);
22 | Assert.IsTrue(receivedBytesDecompressed > receivedBytes);
23 | Assert.AreEqual(1000, receivedBytesDecompressed);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/DataTransfer/Protocols/Delimiter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.ClientUtils;
6 | using EasyTcp4.Protocols.Tcp;
7 | using NUnit.Framework;
8 |
9 | namespace EasyTcp4.Test.EasyTcp.DataTransfer.Protocols
10 | {
11 | public class Delimiter
12 | {
13 | private const string DelimiterString = "|";
14 |
15 | [Test]
16 | public async Task DelimiterProtocolReceiveData()
17 | {
18 | using var conn = await TestHelper.GetTestConnection(
19 | new EasyTcpClient(new DelimiterProtocol(DelimiterString)),
20 | new EasyTcpServer(new DelimiterProtocol(DelimiterString)));
21 |
22 | int receivedBytes = 0;
23 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Length);
24 | conn.Client.Send(new byte[1000]);
25 |
26 | await TestHelper.WaitWhileFalse(() => receivedBytes == 1000);
27 | Assert.AreEqual(1000, receivedBytes);
28 | }
29 |
30 | [Test]
31 | public async Task DelimiterProtocolReceiveDataContent()
32 | {
33 | using var conn = await TestHelper.GetTestConnection(
34 | new EasyTcpClient(new DelimiterProtocol(DelimiterString)),
35 | new EasyTcpServer(new DelimiterProtocol(DelimiterString)));
36 |
37 | int receivedBytes = 0;
38 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Count(x=>x == 100));
39 | conn.Client.Send(Enumerable.Repeat(100, short.MaxValue * 2).ToArray());
40 |
41 | await TestHelper.WaitWhileFalse(() => receivedBytes == short.MaxValue * 2);
42 | Assert.AreEqual(short.MaxValue * 2, receivedBytes);
43 | }
44 |
45 |
46 | [Test]
47 | public async Task DelimiterProtocolReceiveLargeData()
48 | {
49 | using var conn = await TestHelper.GetTestConnection(
50 | new EasyTcpClient(new DelimiterProtocol(DelimiterString)),
51 | new EasyTcpServer(new DelimiterProtocol(DelimiterString)));
52 |
53 | int receivedBytes = 0;
54 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Count(x=>x == 100));
55 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 8).ToArray());
56 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 8).ToArray());
57 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 8).ToArray());
58 |
59 | await TestHelper.WaitWhileFalse(() => receivedBytes == ushort.MaxValue * 24, TimeSpan.FromSeconds(5));
60 | Assert.AreEqual(ushort.MaxValue * 24, receivedBytes);
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/DataTransfer/Protocols/PlainTcp.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using EasyTcp4.ClientUtils;
5 | using EasyTcp4.Protocols.Tcp;
6 | using NUnit.Framework;
7 |
8 | namespace EasyTcp4.Test.EasyTcp.DataTransfer.Protocols
9 | {
10 | public class PlainTcp
11 | {
12 | [Test]
13 | public async Task PlainTcpProtocolReceiveData()
14 | {
15 | using var conn = await TestHelper.GetTestConnection(
16 | new EasyTcpClient(new PlainTcpProtocol()),
17 | new EasyTcpServer(new PlainTcpProtocol()));
18 |
19 | int receivedBytes = 0;
20 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Length);
21 | conn.Client.Send(new byte[1000]);
22 |
23 | await TestHelper.WaitWhileFalse(() => receivedBytes == 1000);
24 | Assert.AreEqual(1000, receivedBytes);
25 | }
26 |
27 | [Test]
28 | public async Task PlainTcpProtocolReceiveDataContent()
29 | {
30 | using var conn = await TestHelper.GetTestConnection(
31 | new EasyTcpClient(new PlainTcpProtocol()),
32 | new EasyTcpServer(new PlainTcpProtocol()));
33 |
34 | int receivedBytes = 0;
35 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Count(x=>x == 100));
36 | conn.Client.Send(Enumerable.Repeat(100, short.MaxValue * 2).ToArray());
37 |
38 | await TestHelper.WaitWhileFalse(() => receivedBytes == short.MaxValue * 2);
39 | Assert.AreEqual(short.MaxValue * 2, receivedBytes);
40 | }
41 |
42 |
43 | [Test]
44 | public async Task PlainTcpProtocolReceiveLargeData()
45 | {
46 | using var conn = await TestHelper.GetTestConnection(
47 | new EasyTcpClient(new PlainTcpProtocol()),
48 | new EasyTcpServer(new PlainTcpProtocol()));
49 |
50 | int receivedBytes = 0;
51 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Count(x=>x == 100));
52 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 128).ToArray());
53 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 128).ToArray());
54 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 128).ToArray());
55 |
56 | await TestHelper.WaitWhileFalse(() => receivedBytes == ushort.MaxValue * 384);
57 | Assert.AreEqual(ushort.MaxValue * 384, receivedBytes);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/DataTransfer/Protocols/PrefixLength.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using EasyTcp4.ClientUtils;
5 | using EasyTcp4.Protocols.Tcp;
6 | using NUnit.Framework;
7 |
8 | namespace EasyTcp4.Test.EasyTcp.DataTransfer.Protocols
9 | {
10 | public class PrefixLength
11 | {
12 | [Test]
13 | public async Task PrefixLengthProtocolReceiveData()
14 | {
15 | using var conn = await TestHelper.GetTestConnection(
16 | new EasyTcpClient(new PrefixLengthProtocol()),
17 | new EasyTcpServer(new PrefixLengthProtocol()));
18 |
19 | int receivedBytes = 0;
20 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Length);
21 | conn.Client.Send(new byte[1000]);
22 |
23 | await TestHelper.WaitWhileFalse(() => receivedBytes == 1000);
24 | Assert.AreEqual(1000, receivedBytes);
25 | }
26 |
27 | [Test]
28 | public async Task PrefixLengthProtocolReceiveDataContent()
29 | {
30 | using var conn = await TestHelper.GetTestConnection(
31 | new EasyTcpClient(new PrefixLengthProtocol()),
32 | new EasyTcpServer(new PrefixLengthProtocol()));
33 |
34 | int receivedBytes = 0;
35 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Count(x=>x == 100));
36 | conn.Client.Send(Enumerable.Repeat(100, short.MaxValue * 2).ToArray());
37 |
38 | await TestHelper.WaitWhileFalse(() => receivedBytes == short.MaxValue * 2);
39 | Assert.AreEqual(short.MaxValue * 2, receivedBytes);
40 | }
41 |
42 |
43 | [Test]
44 | public async Task PrefixLengthProtocolReceiveLargeData()
45 | {
46 | using var conn = await TestHelper.GetTestConnection(
47 | new EasyTcpClient(new PrefixLengthProtocol(int.MaxValue)),
48 | new EasyTcpServer(new PrefixLengthProtocol(int.MaxValue)));
49 |
50 | int receivedBytes = 0;
51 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Count(x=>x == 100));
52 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 128).ToArray());
53 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 128).ToArray());
54 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 128).ToArray());
55 |
56 | await TestHelper.WaitWhileFalse(() => receivedBytes == ushort.MaxValue * 384);
57 | Assert.AreEqual(ushort.MaxValue * 384, receivedBytes);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/DataTransfer/SendData.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using EasyTcp4.ClientUtils;
4 | using NUnit.Framework;
5 |
6 | namespace EasyTcp4.Test.EasyTcp.DataTransfer
7 | {
8 | public class SendData
9 | {
10 | [Test]
11 | public async Task SendArray()
12 | {
13 | using var conn = await TestHelper.GetTestConnection();
14 |
15 | int receivedBytes = 0;
16 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Length);
17 | conn.Client.Send(new byte[1000]);
18 |
19 | await TestHelper.WaitWhileFalse(()=>receivedBytes == 1000);
20 | Assert.AreEqual(1000, receivedBytes);
21 | }
22 |
23 | [Test]
24 | public async Task SendMultipleArrays()
25 | {
26 | using var conn = await TestHelper.GetTestConnection();
27 |
28 | int receivedBytes = 0;
29 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Length);
30 | conn.Client.Send(new byte[1000], new byte[1000], new byte[1000]);
31 |
32 | await TestHelper.WaitWhileFalse(()=>receivedBytes == 3000);
33 | Assert.AreEqual(3000, receivedBytes);
34 | }
35 |
36 | [Test]
37 | public async Task SendString()
38 | {
39 | using var conn = await TestHelper.GetTestConnection();
40 |
41 | int receivedBytes = 0;
42 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Length);
43 | conn.Client.Send("test");
44 |
45 | await TestHelper.WaitWhileFalse(()=>receivedBytes == 4);
46 | Assert.AreEqual(4, receivedBytes);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/DataTransfer/Serialization.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using EasyTcp4.ClientUtils;
4 | using NUnit.Framework;
5 |
6 | namespace EasyTcp4.Test.EasyTcp.DataTransfer
7 | {
8 | public class Serialization
9 | {
10 | [Test]
11 | public async Task SendSerialisedArray()
12 | {
13 | using var conn = await TestHelper.GetTestConnection();
14 |
15 | char[] items = { 't', 'e', 's', 't', ' ', 'a', 'r', 'r', 'a', 'y' };
16 | int receivedItems = 0;
17 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedItems, m.To().Length);
18 | conn.Client.Send(items);
19 |
20 | await TestHelper.WaitWhileFalse(() => receivedItems == items.Length);
21 | Assert.AreEqual(items.Length, receivedItems);
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/Utils/ArrayUtil.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using EasyTcp4.ClientUtils;
4 | using EasyTcp4.ClientUtils.Async;
5 | using NUnit.Framework;
6 |
7 | namespace EasyTcp4.Test.EasyTcp.Utils
8 | {
9 | public class ArrayUtil
10 | {
11 | [Test]
12 | public async Task SendArray()
13 | {
14 | using var conn = await TestHelper.GetTestConnection();
15 |
16 | int receivedBytes = 0;
17 | conn.Server.OnDataReceive += (_, m) =>
18 | {
19 | var data = m.ReceiveArray();
20 | Interlocked.Add(ref receivedBytes, data.Length);
21 | };
22 |
23 | conn.Client.Send("Trigger OnDataReceive");
24 | conn.Client.SendArray(new byte[ushort.MaxValue * 5]);
25 |
26 | await TestHelper.WaitWhileFalse(()=> receivedBytes == ushort.MaxValue * 5);
27 | Assert.AreEqual(ushort.MaxValue * 5, receivedBytes);
28 | }
29 |
30 | [Test]
31 | public async Task SendCompressedArray()
32 | {
33 | using var conn = await TestHelper.GetTestConnection();
34 |
35 | int receivedBytes = 0;
36 | conn.Server.OnDataReceive += (_, m) =>
37 | {
38 | var data = m.ReceiveArray(compression: true);
39 | Interlocked.Add(ref receivedBytes, data.Length);
40 | };
41 |
42 | conn.Client.Send("Trigger OnDataReceive");
43 | conn.Client.SendArray(new byte[ushort.MaxValue * 5], compression: true);
44 |
45 | await TestHelper.WaitWhileFalse(()=> receivedBytes == ushort.MaxValue * 5);
46 | Assert.AreEqual(ushort.MaxValue * 5, receivedBytes);
47 | }
48 |
49 | [Test]
50 | public async Task SendArrayAsync()
51 | {
52 | using var conn = await TestHelper.GetTestConnection();
53 |
54 | int receivedBytes = 0;
55 | conn.Server.OnDataReceiveAsync += async (_, m) =>
56 | {
57 | var data = await m.ReceiveArrayAsync();
58 | Interlocked.Add(ref receivedBytes, data.Length);
59 | };
60 |
61 | conn.Client.Send("Trigger OnDataReceive");
62 | await conn.Client.SendArrayAsync(new byte[ushort.MaxValue * 5]);
63 |
64 | await TestHelper.WaitWhileFalse(()=> receivedBytes == ushort.MaxValue * 5);
65 | Assert.AreEqual(ushort.MaxValue * 5, receivedBytes);
66 | }
67 |
68 | [Test]
69 | public async Task SendCompressedArrayAsync()
70 | {
71 | using var conn = await TestHelper.GetTestConnection();
72 |
73 | int receivedBytes = 0;
74 | conn.Server.OnDataReceiveAsync += async (_, m) =>
75 | {
76 | var data = await m.ReceiveArrayAsync(compression: true);
77 | Interlocked.Add(ref receivedBytes, data.Length);
78 | };
79 |
80 | conn.Client.Send("Trigger OnDataReceive");
81 | await conn.Client.SendArrayAsync(new byte[ushort.MaxValue * 5], compression: true);
82 |
83 | await TestHelper.WaitWhileFalse(()=> receivedBytes == ushort.MaxValue * 5);
84 | Assert.AreEqual(ushort.MaxValue * 5, receivedBytes);
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/Utils/SendAndGetReply.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using System.Threading.Tasks;
3 | using EasyTcp4.ClientUtils;
4 | using NUnit.Framework;
5 |
6 | namespace EasyTcp4.Test.EasyTcp.Utils
7 | {
8 | public class SendAndGetReply
9 | {
10 | [Test]
11 | public async Task SendStringAndGetReply()
12 | {
13 | using var conn = await TestHelper.GetTestConnection();
14 | conn.Server.OnDataReceive += (_, m) => m.Client.Send(m);
15 |
16 | var message = "test message";
17 | var reply = conn.Client.SendAndGetReply(message);
18 |
19 | Assert.AreEqual(message, reply.ToString());
20 | }
21 |
22 | [Test]
23 | public async Task SendAndGetReplyDoesNotTriggerOnDataReceive()
24 | {
25 | using var conn = await TestHelper.GetTestConnection();
26 | conn.Server.OnDataReceive += (_, m) => m.Client.Send(m);
27 |
28 | int triggered = 0;
29 | conn.Client.OnDataReceive += (_, m) => Interlocked.Increment(ref triggered);
30 | var reply = conn.Client.SendAndGetReply("test message");
31 |
32 | Assert.AreEqual(0, triggered);
33 | }
34 |
35 | [Test]
36 | public async Task SendAndGetReplyInsideOnDataReceive()
37 | {
38 | using var conn = await TestHelper.GetTestConnection();
39 | conn.Server.OnDataReceive += (_, m) => m.Client.Send(m);
40 |
41 | var message = "test message";
42 | Message reply = null;
43 | conn.Client.OnDataReceive += (_, m) =>
44 | {
45 | conn.Client.Protocol.EnsureDataReceiverIsRunning(conn.Client);
46 | reply = conn.Client.SendAndGetReply(message);
47 | };
48 | conn.Client.Send("Trigger client onDataReceive");
49 |
50 | await TestHelper.WaitWhileTrue(()=>reply == null);
51 | Assert.AreEqual(message, reply.ToString());
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp/Utils/StreamUtil.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using EasyTcp4.ClientUtils;
5 | using EasyTcp4.ClientUtils.Async;
6 | using NUnit.Framework;
7 |
8 | namespace EasyTcp4.Test.EasyTcp.Utils
9 | {
10 | public class StreamUtil
11 | {
12 | [Test]
13 | public async Task SendStream()
14 | {
15 | using var conn = await TestHelper.GetTestConnection();
16 |
17 | long receivedBytes = 0;
18 | conn.Server.OnDataReceive += (_, m) =>
19 | {
20 | using var stream = new MemoryStream();
21 | m.ReceiveStream(stream);
22 | Interlocked.Add(ref receivedBytes, stream.Length);
23 | };
24 |
25 | using var stream = new MemoryStream(new byte[ushort.MaxValue * 5]);
26 | conn.Client.Send("Trigger OnDataReceive");
27 | conn.Client.SendStream(stream);
28 |
29 | await TestHelper.WaitWhileFalse(()=> receivedBytes == stream.Length);
30 | Assert.AreEqual(stream.Length, receivedBytes);
31 | }
32 |
33 | [Test]
34 | public async Task SendCompressedStream()
35 | {
36 | using var conn = await TestHelper.GetTestConnection();
37 |
38 | long receivedBytes = 0;
39 | conn.Server.OnDataReceive += (_, m) =>
40 | {
41 | using var stream = new MemoryStream();
42 | m.ReceiveStream(stream, compression: true);
43 | Interlocked.Add(ref receivedBytes, stream.Length);
44 | };
45 |
46 | using var stream = new MemoryStream(new byte[ushort.MaxValue * 5]);
47 | conn.Client.Send("Trigger OnDataReceive");
48 | conn.Client.SendStream(stream, compression: true);
49 |
50 | await TestHelper.WaitWhileFalse(()=> receivedBytes == stream.Length);
51 | Assert.AreEqual(stream.Length, receivedBytes);
52 | }
53 |
54 | [Test]
55 | public async Task SendStreamAsync()
56 | {
57 | using var conn = await TestHelper.GetTestConnection();
58 |
59 | long receivedBytes = 0;
60 | conn.Server.OnDataReceiveAsync += async (_, m) =>
61 | {
62 | using var stream = new MemoryStream();
63 | await m.ReceiveStreamAsync(stream);
64 | Interlocked.Add(ref receivedBytes, stream.Length);
65 | };
66 |
67 | using var stream = new MemoryStream(new byte[ushort.MaxValue * 5]);
68 | conn.Client.Send("Trigger OnDataReceive");
69 | await conn.Client.SendStreamAsync(stream);
70 |
71 | await TestHelper.WaitWhileFalse(()=> receivedBytes == stream.Length);
72 | Assert.AreEqual(stream.Length, receivedBytes);
73 | }
74 |
75 | [Test]
76 | public async Task SendCompressedStreamAsync()
77 | {
78 | using var conn = await TestHelper.GetTestConnection();
79 |
80 | long receivedBytes = 0;
81 | conn.Server.OnDataReceiveAsync += async (_, m) =>
82 | {
83 | using var stream = new MemoryStream();
84 | await m.ReceiveStreamAsync(stream, compression: true);
85 | Interlocked.Add(ref receivedBytes, stream.Length);
86 | };
87 |
88 | using var stream = new MemoryStream(new byte[ushort.MaxValue * 5]);
89 | conn.Client.Send("Trigger OnDataReceive");
90 | await conn.Client.SendStreamAsync(stream, compression: true);
91 |
92 | await TestHelper.WaitWhileFalse(()=> receivedBytes == stream.Length);
93 | Assert.AreEqual(stream.Length, receivedBytes);
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/EasyTcp4.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | net6.0
4 | false
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | PreserveNewest
15 |
16 |
17 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/Encryption/Ssl/ArrayUtil.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Cryptography.X509Certificates;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 | using EasyTcp4.ClientUtils;
5 | using EasyTcp4.Encryption;
6 | using EasyTcp4.ClientUtils.Async;
7 | using NUnit.Framework;
8 |
9 | namespace EasyTcp4.Test.Encryption.Ssl
10 | {
11 | public class ArrayUtil
12 | {
13 | [Test]
14 | public async Task SendArrayAsync()
15 | {
16 | using var certificate = new X509Certificate2("certificate.pfx", "password");
17 | using var conn = await TestHelper.GetTestConnection(
18 | new EasyTcpClient().UseSsl("localhost", true),
19 | new EasyTcpServer().UseServerSsl(certificate));
20 |
21 | int receivedBytes = 0;
22 | conn.Server.OnDataReceiveAsync += async (_, m) =>
23 | {
24 | var data = await m.ReceiveArrayAsync();
25 | Interlocked.Add(ref receivedBytes, data.Length);
26 | };
27 |
28 | conn.Client.Send("Trigger OnDataReceive");
29 | await conn.Client.SendArrayAsync(new byte[ushort.MaxValue * 5]);
30 |
31 | await TestHelper.WaitWhileFalse(()=> receivedBytes == ushort.MaxValue * 5);
32 | Assert.AreEqual(ushort.MaxValue * 5, receivedBytes);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/Encryption/Ssl/Protocols/PlainTcp.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Security.Cryptography.X509Certificates;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.ClientUtils;
6 | using EasyTcp4.Encryption.Ssl;
7 | using NUnit.Framework;
8 |
9 | namespace EasyTcp4.Test.Encryption.Ssl.Protocols
10 | {
11 | public class PlainTcp
12 | {
13 | [Test]
14 | public async Task PlainSslProtocolReceiveData()
15 | {
16 | using var certificate = new X509Certificate2("certificate.pfx", "password");
17 | using var conn = await TestHelper.GetTestConnection(
18 | new EasyTcpClient(new PlainSslProtocol("localhost", true)),
19 | new EasyTcpServer(new PlainSslProtocol(certificate)));
20 |
21 | int receivedBytes = 0;
22 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Length);
23 | conn.Client.Send(new byte[1000]);
24 |
25 | await TestHelper.WaitWhileFalse(() => receivedBytes == 1000);
26 | Assert.AreEqual(1000, receivedBytes);
27 | }
28 |
29 | [Test]
30 | public async Task PlainSslProtocolReceiveDataContent()
31 | {
32 | using var certificate = new X509Certificate2("certificate.pfx", "password");
33 | using var conn = await TestHelper.GetTestConnection(
34 | new EasyTcpClient(new PlainSslProtocol("localhost", true)),
35 | new EasyTcpServer(new PlainSslProtocol(certificate)));
36 |
37 |
38 | int receivedBytes = 0;
39 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Count(x=>x == 100));
40 | conn.Client.Send(Enumerable.Repeat(100, short.MaxValue * 2).ToArray());
41 |
42 | await TestHelper.WaitWhileFalse(() => receivedBytes == short.MaxValue * 2);
43 | Assert.AreEqual(short.MaxValue * 2, receivedBytes);
44 | }
45 |
46 | [Test]
47 | public async Task PlainSslProtocolReceiveLargeData()
48 | {
49 | using var certificate = new X509Certificate2("certificate.pfx", "password");
50 | using var conn = await TestHelper.GetTestConnection(
51 | new EasyTcpClient(new PlainSslProtocol("localhost", true)),
52 | new EasyTcpServer(new PlainSslProtocol(certificate)));
53 |
54 | int receivedBytes = 0;
55 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Count(x => x == 100));
56 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 2).ToArray());
57 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 2).ToArray());
58 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 2).ToArray());
59 |
60 | await TestHelper.WaitWhileFalse(() => receivedBytes == ushort.MaxValue * 6);
61 | Assert.AreEqual(ushort.MaxValue * 6, receivedBytes);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/Encryption/Ssl/Protocols/PrefixLengthSsl.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Security.Cryptography.X509Certificates;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.ClientUtils;
6 | using EasyTcp4.Encryption.Ssl;
7 | using NUnit.Framework;
8 |
9 | namespace EasyTcp4.Test.Encryption.Ssl.Protocols
10 | {
11 | public class PrefixLength
12 | {
13 | [Test]
14 | public async Task PrefixLengthSslProtocolReceiveData()
15 | {
16 | using var certificate = new X509Certificate2("certificate.pfx", "password");
17 | using var conn = await TestHelper.GetTestConnection(
18 | new EasyTcpClient(new PrefixLengthSslProtocol("localhost", true)),
19 | new EasyTcpServer(new PrefixLengthSslProtocol(certificate)));
20 |
21 | int receivedBytes = 0;
22 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Length);
23 | conn.Client.Send(new byte[1000]);
24 |
25 | await TestHelper.WaitWhileFalse(() => receivedBytes == 1000);
26 | Assert.AreEqual(1000, receivedBytes);
27 | }
28 |
29 | [Test]
30 | public async Task PrefixLengthSslProtocolReceiveDataContent()
31 | {
32 | using var certificate = new X509Certificate2("certificate.pfx", "password");
33 | using var conn = await TestHelper.GetTestConnection(
34 | new EasyTcpClient(new PrefixLengthSslProtocol("localhost", true)),
35 | new EasyTcpServer(new PrefixLengthSslProtocol(certificate)));
36 |
37 | int receivedBytes = 0;
38 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Count(x=>x == 100));
39 | conn.Client.Send(Enumerable.Repeat(100, short.MaxValue * 2).ToArray());
40 |
41 | await TestHelper.WaitWhileFalse(() => receivedBytes == short.MaxValue * 2);
42 | Assert.AreEqual(short.MaxValue * 2, receivedBytes);
43 | }
44 |
45 |
46 | [Test]
47 | public async Task PrefixLengthSslProtocolReceiveLargeData()
48 | {
49 | using var certificate = new X509Certificate2("certificate.pfx", "password");
50 | using var conn = await TestHelper.GetTestConnection(
51 | new EasyTcpClient(new PrefixLengthSslProtocol("localhost", true, int.MaxValue)),
52 | new EasyTcpServer(new PrefixLengthSslProtocol(certificate, int.MaxValue)));
53 |
54 | int receivedBytes = 0;
55 | conn.Server.OnDataReceive += (_, m) => Interlocked.Add(ref receivedBytes, m.Data.Count(x=>x == 100));
56 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 2).ToArray());
57 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 2).ToArray());
58 | conn.Client.Send(Enumerable.Repeat(100, ushort.MaxValue * 2).ToArray());
59 |
60 | await TestHelper.WaitWhileFalse(() => receivedBytes == ushort.MaxValue * 6);
61 | Assert.AreEqual(ushort.MaxValue * 6, receivedBytes);
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/Encryption/Ssl/StreamUtil.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Security.Cryptography.X509Certificates;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.ClientUtils;
6 | using EasyTcp4.Encryption;
7 | using EasyTcp4.ClientUtils.Async;
8 | using NUnit.Framework;
9 |
10 | namespace EasyTcp4.Test.Encryption.Ssl
11 | {
12 | public class StreamUtil
13 | {
14 | [Test]
15 | public async Task SendStreamAsync()
16 | {
17 | using var certificate = new X509Certificate2("certificate.pfx", "password");
18 | using var conn = await TestHelper.GetTestConnection(
19 | new EasyTcpClient().UseSsl("localhost", true),
20 | new EasyTcpServer().UseServerSsl(certificate));
21 |
22 | long receivedBytes = 0;
23 | conn.Server.OnDataReceiveAsync += async (_, m) =>
24 | {
25 | using var stream = new MemoryStream();
26 | await m.ReceiveStreamAsync(stream);
27 | Interlocked.Add(ref receivedBytes, stream.Length);
28 | };
29 |
30 | using var stream = new MemoryStream(new byte[ushort.MaxValue * 5]);
31 | conn.Client.Send("Trigger OnDataReceive");
32 | await conn.Client.SendStreamAsync(stream);
33 |
34 | await TestHelper.WaitWhileFalse(()=> receivedBytes == stream.Length);
35 | Assert.AreEqual(stream.Length, receivedBytes);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/TestHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | using EasyTcp4.ServerUtils;
4 | using System.Net;
5 | using System.Threading.Tasks;
6 | using EasyTcp4.ClientUtils.Async;
7 |
8 | namespace EasyTcp4.Test
9 | {
10 | public static class TestHelper
11 | {
12 | ///
13 | /// Unique port generater for every test
14 | ///
15 | private static int _portCounter = 1200;
16 | public static ushort GetPort() => (ushort)Math.Min(Interlocked.Increment(ref _portCounter), ushort.MaxValue);
17 |
18 | public static async Task GetTestConnection(EasyTcpClient client = null, EasyTcpServer server = null)
19 | {
20 | var port = GetPort();
21 | server ??= new EasyTcpServer();
22 | client ??= new EasyTcpClient();
23 | server.Start(port);
24 | await client.ConnectAsync(IPAddress.Loopback, port);
25 | return new TestData() { Client = client, Server = server };
26 | }
27 |
28 | ///
29 | /// Wait until function refurns true
30 | ///
31 | public static async Task WaitWhileTrue(Func f, TimeSpan? timeout = null)
32 | {
33 | for (int i = 0; i < (timeout ?? TimeSpan.FromSeconds(1)).TotalMilliseconds && f(); i += 5)
34 | await Task.Delay(5);
35 | }
36 |
37 | ///
38 | /// Wait until function refurns false
39 | ///
40 | public static Task WaitWhileFalse(Func f, TimeSpan? timeout = null) => WaitWhileTrue(() => !f(), timeout);
41 | }
42 |
43 | public class TestData : IDisposable
44 | {
45 | public EasyTcpClient Client;
46 | public EasyTcpServer Server;
47 |
48 | public void Dispose()
49 | {
50 | Client?.Dispose();
51 | Server?.Dispose();
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/EasyTcp4.Test/certificate.pfx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/job79/EasyTcp/961bf0f60a580988d345a3f3855ee27be4913b12/EasyTcp4.Test/certificate.pfx
--------------------------------------------------------------------------------
/EasyTcp4.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyTcp4", "EasyTcp4\EasyTcp4.csproj", "{D7B9F399-3548-48D8-8C7C-A70AB12DDD39}"
3 | EndProject
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyTcp4.Actions", "EasyTcp4.Actions\EasyTcp4.Actions.csproj", "{685D8027-CFF1-42B7-858C-13EBF9C093A4}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyTcp4.Encryption", "EasyTcp4.Encryption\EasyTcp4.Encryption.csproj", "{94A9A424-C8E6-4ACF-816E-914F1240B259}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyTcp4.Test", "EasyTcp4.Test\EasyTcp4.Test.csproj", "{CC3DDFD2-26EA-4BF9-B7D4-1BD6C8A8B64C}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {D7B9F399-3548-48D8-8C7C-A70AB12DDD39}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {D7B9F399-3548-48D8-8C7C-A70AB12DDD39}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {D7B9F399-3548-48D8-8C7C-A70AB12DDD39}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {D7B9F399-3548-48D8-8C7C-A70AB12DDD39}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {685D8027-CFF1-42B7-858C-13EBF9C093A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {685D8027-CFF1-42B7-858C-13EBF9C093A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {685D8027-CFF1-42B7-858C-13EBF9C093A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {685D8027-CFF1-42B7-858C-13EBF9C093A4}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {94A9A424-C8E6-4ACF-816E-914F1240B259}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {94A9A424-C8E6-4ACF-816E-914F1240B259}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {94A9A424-C8E6-4ACF-816E-914F1240B259}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {94A9A424-C8E6-4ACF-816E-914F1240B259}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {CC3DDFD2-26EA-4BF9-B7D4-1BD6C8A8B64C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {CC3DDFD2-26EA-4BF9-B7D4-1BD6C8A8B64C}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {CC3DDFD2-26EA-4BF9-B7D4-1BD6C8A8B64C}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {CC3DDFD2-26EA-4BF9-B7D4-1BD6C8A8B64C}.Release|Any CPU.Build.0 = Release|Any CPU
32 | EndGlobalSection
33 | EndGlobal
34 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/ArrayUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO.Compression;
3 |
4 | namespace EasyTcp4.ClientUtils
5 | {
6 | public static class ArrayUtil
7 | {
8 | ///
9 | /// Send array to the remote host
10 | ///
11 | ///
12 | ///
13 | /// compress data using deflate if set to true
14 | /// determines whether prefix with length of the data is send to the remote host
15 | public static void SendArray(this EasyTcpClient client, byte[] array, bool compression = false, bool sendLengthPrefix = true)
16 | {
17 | if (client?.BaseSocket == null) throw new Exception("Could not send array: client is not connected");
18 |
19 | var networkStream = client.Protocol.GetStream(client);
20 | var dataStream = compression ? new DeflateStream(networkStream, CompressionMode.Compress, true) : networkStream;
21 |
22 | if (sendLengthPrefix) dataStream.Write(BitConverter.GetBytes(array.Length), 0, 4);
23 | dataStream.Write(array, 0, array.Length);
24 |
25 | if (compression) dataStream.Dispose();
26 | }
27 |
28 | ///
29 | /// Receive array from the remote host
30 | /// Use this method only when not client is not listening for incoming messages (Inside OnReceive event handlers)
31 | ///
32 | ///
33 | /// compress data using deflate if set to true
34 | /// length of data, use prefix when 0
35 | ///
36 | public static byte[] ReceiveArray(this Message message, bool compression = false, int count = 0, int bufferSize = 1024)
37 | {
38 | if (message?.Client?.BaseSocket == null) throw new Exception("Could not receive array: client is not connected or message is invalid");
39 |
40 | var networkStream = message.Client.Protocol.GetStream(message.Client);
41 | var dataStream = compression ? new DeflateStream(networkStream, CompressionMode.Decompress, true) : networkStream;
42 |
43 | // Get length from stream
44 | if (count == 0)
45 | {
46 | var length = new byte[4];
47 | dataStream.Read(length, 0, length.Length);
48 | count = BitConverter.ToInt32(length, 0);
49 | }
50 |
51 | var receivedArray = new byte[count];
52 | int read, totalReceivedBytes = 0;
53 |
54 | while (totalReceivedBytes < count &&
55 | (read = dataStream.Read(receivedArray, totalReceivedBytes, Math.Min(bufferSize, count - totalReceivedBytes))) > 0)
56 | totalReceivedBytes += read;
57 |
58 | if (compression) dataStream.Dispose();
59 | return receivedArray;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/Async/ArrayAsyncUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO.Compression;
3 | using System.Threading.Tasks;
4 |
5 | namespace EasyTcp4.ClientUtils.Async
6 | {
7 | public static class ArrayAsyncUtil
8 | {
9 | ///
10 | /// Send array to the remote host
11 | ///
12 | ///
13 | ///
14 | /// compress data using deflate if set to true
15 | /// determines whether prefix with length of the data is send to the remote host
16 | public static async Task SendArrayAsync(this EasyTcpClient client, byte[] array, bool compression = false, bool sendLengthPrefix = true)
17 | {
18 | if (client?.BaseSocket == null) throw new Exception("Could not send array: client is not connected");
19 |
20 | var networkStream = client.Protocol.GetStream(client);
21 | var dataStream = compression ? new DeflateStream(networkStream, CompressionMode.Compress, true) : networkStream;
22 |
23 | if (sendLengthPrefix) await dataStream.WriteAsync(BitConverter.GetBytes(array.Length), 0, 4);
24 | await dataStream.WriteAsync(array, 0, array.Length);
25 |
26 | if (compression) dataStream.Dispose();
27 | }
28 |
29 | ///
30 | /// Receive array from the remote host
31 | /// Use this method only when not client is not listening for incoming messages (Inside OnReceive event handlers)
32 | ///
33 | ///
34 | /// compress data using deflate if set to true
35 | /// length of data, use prefix when 0
36 | ///
37 | public static async Task ReceiveArrayAsync(this Message message, bool compression = false, int count = 0, int bufferSize = 1024)
38 | {
39 | if (message?.Client?.BaseSocket == null) throw new Exception("Could not receive array: client is not connected or message is invalid");
40 |
41 | var networkStream = message.Client.Protocol.GetStream(message.Client);
42 | var dataStream = compression ? new DeflateStream(networkStream, CompressionMode.Decompress, true) : networkStream;
43 |
44 | // Get length from stream
45 | if (count == 0)
46 | {
47 | var length = new byte[4];
48 | await dataStream.ReadAsync(length, 0, length.Length);
49 | count = BitConverter.ToInt32(length, 0);
50 | }
51 |
52 | var receivedArray = new byte[count];
53 | int read, totalReceivedBytes = 0;
54 |
55 | while (totalReceivedBytes < count &&
56 | (read = await dataStream.ReadAsync(receivedArray, totalReceivedBytes, Math.Min(bufferSize, count - totalReceivedBytes))) > 0)
57 | totalReceivedBytes += read;
58 |
59 | if (compression) dataStream.Dispose();
60 | return receivedArray;
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/Async/ConnectAsyncUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 | using System.Threading.Tasks;
5 |
6 | namespace EasyTcp4.ClientUtils.Async
7 | {
8 | public static class ConnectAsyncUtil
9 | {
10 | ///
11 | /// Establish connection with remote host
12 | ///
13 | ///
14 | /// endPoint of remote host
15 | /// base socket for EasyTcpClient, new one is created when null
16 | /// determines whether the client connected successfully
17 | public static async Task ConnectAsync(this EasyTcpClient client, EndPoint endPoint, Socket socket = null)
18 | {
19 | if (client == null) throw new ArgumentException("Could not connect: client is null");
20 | if (endPoint == null) throw new ArgumentException("Could not connect: endpoint is null");
21 | if (client.BaseSocket != null) throw new ArgumentException("Could not connect: client is already connected");
22 |
23 | try
24 | {
25 | client.BaseSocket = socket ?? client.Protocol.GetSocket(endPoint.AddressFamily);
26 | await client.BaseSocket.ConnectAsync(endPoint);
27 |
28 | if (client.BaseSocket.Connected && client.Protocol.OnConnect(client))
29 | {
30 | client.FireOnConnect();
31 | return true;
32 | }
33 | }
34 | catch
35 | {
36 | // Ignore exception, dispose (&disconnect) client and return false
37 | }
38 |
39 | client.Dispose(); // Set socket to null
40 | return false;
41 | }
42 |
43 | ///
44 | /// Establish connection with remote host
45 | ///
46 | ///
47 | /// ipAddress of remote host
48 | /// port of remote host
49 | /// base socket for EasyTcpClient, new one is created when null
50 | /// determines whether the client connected successfully
51 | public static async Task ConnectAsync(this EasyTcpClient client, IPAddress ipAddress, ushort port, Socket socket = null)
52 | => await client.ConnectAsync(new IPEndPoint(ipAddress, port), socket);
53 |
54 | ///
55 | /// Establish connection with remote host
56 | ///
57 | ///
58 | /// ipAddress of remote host as string
59 | /// port of remote host
60 | /// base socket for EasyTcpClient, new one is created when null
61 | /// determines whether the client connected successfully
62 | /// ipAddress is not a valid IPv4/IPv6 address
63 | public static async Task ConnectAsync(this EasyTcpClient client, string ipAddress, ushort port, Socket socket = null)
64 | {
65 | if (!IPAddress.TryParse(ipAddress, out IPAddress address))
66 | throw new ArgumentException("Could not connect to remote host: ipAddress is not a valid IPv4/IPv6 address");
67 | return await client.ConnectAsync(address, port, socket);
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/Async/SendAndGetReplyAsyncUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.PacketUtils;
6 |
7 | namespace EasyTcp4.ClientUtils.Async
8 | {
9 | public static class SendAndGetReplyAsyncUtil
10 | {
11 | ///
12 | /// Default timeout when timeout parameter is not specified
13 | ///
14 | private const int DefaultTimeout = 5_000;
15 |
16 | ///
17 | /// Send data to remote host. Then wait and return the reply
18 | ///
19 | ///
20 | /// data to send to remote host
21 | /// maximum time to wait for a reply, return null when time expires
22 | /// received reply
23 | public static async Task SendAndGetReplyAsync(this EasyTcpClient client, TimeSpan? timeout = null, params byte[][] data)
24 | {
25 | Message reply = null;
26 | using var signal = new SemaphoreSlim(0, 1); // Use SemaphoreSlim as async ManualResetEventSlim
27 |
28 | client.DataReceiveHandler = message =>
29 | {
30 | reply = message;
31 | client.ResetDataReceiveHandler();
32 | signal.Release(); // Function is no longer used when signal is disposed, therefore this is completely safe
33 | return Task.CompletedTask;
34 | };
35 |
36 | client.Send(data);
37 | await signal.WaitAsync(timeout ?? TimeSpan.FromMilliseconds(DefaultTimeout));
38 | if (reply == null) client.ResetDataReceiveHandler();
39 | return reply;
40 | }
41 |
42 | ///
43 | /// Send data to remote host. Then wait and return the reply
44 | ///
45 | ///
46 | /// data to send to remote host
47 | /// maximum time to wait for a reply, return null when time expires
48 | /// compress data using deflate if set to true
49 | /// received reply
50 | public static async Task SendAndGetReplyAsync(this EasyTcpClient client, byte[] data,
51 | TimeSpan? timeout = null, bool compression = false)
52 | {
53 | if (compression) data = CompressionUtil.Compress(data);
54 | return await client.SendAndGetReplyAsync(timeout, data);
55 | }
56 |
57 | ///
58 | /// Send data to remote host. Then wait and return the reply
59 | ///
60 | ///
61 | /// data to send to remote host
62 | /// maximum time to wait for a reply, return null when time expires
63 | /// encoding type (default: UTF8)
64 | /// compress data using deflate if set to true
65 | /// received data or null
66 | public static async Task SendAndGetReplyAsync(this EasyTcpClient client, string data,
67 | TimeSpan? timeout = null, Encoding encoding = null, bool compression = false)
68 | => await client.SendAndGetReplyAsync((encoding ?? Encoding.UTF8).GetBytes(data), timeout, compression);
69 |
70 | ///
71 | /// Send data to remote host. Then wait and return the reply
72 | ///
73 | ///
74 | /// data to send to remote host
75 | /// maximum time to wait for a reply, return null when time expires
76 | /// compress data using deflate if set to true
77 | /// received data or null
78 | public static async Task SendAndGetReplyAsync(this EasyTcpClient client, IEasyPacket data,
79 | TimeSpan? timeout = null, bool compression = false)
80 | => await client.SendAndGetReplyAsync(data.Data, timeout, compression);
81 |
82 | ///
83 | /// Send data to remote host. Then wait and return the reply
84 | ///
85 | ///
86 | /// data to send to remote host
87 | /// maximum time to wait for a reply, return null when time expires
88 | /// compress data using deflate if set to true
89 | /// received data or null
90 | public static async Task SendAndGetReplyAsync(this EasyTcpClient client, object data,
91 | TimeSpan? timeout = null, bool compression = false)
92 | => await client.SendAndGetReplyAsync(client?.Serialize(data), timeout, compression);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/Async/StreamAsyncUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Compression;
4 | using System.Threading.Tasks;
5 |
6 | namespace EasyTcp4.ClientUtils.Async
7 | {
8 | public static class StreamAsyncUtil
9 | {
10 | ///
11 | /// Send stream to the remote host
12 | ///
13 | ///
14 | /// input stream
15 | /// compress data using deflate if set to true
16 | /// determines whether prefix with length of the data is send to the remote host
17 | ///
18 | public static async Task SendStreamAsync(this EasyTcpClient client, Stream stream,
19 | bool compression = false, bool sendLengthPrefix = true, int bufferSize = 1024)
20 | {
21 | if (client?.BaseSocket == null) throw new Exception("Could not send stream: client is not connected");
22 | if (!stream.CanRead) throw new InvalidDataException("Could not send stream: stream is not readable");
23 |
24 | var networkStream = client.Protocol.GetStream(client);
25 | var dataStream = compression ? new DeflateStream(networkStream, CompressionMode.Compress, true) : networkStream;
26 |
27 | if (sendLengthPrefix) await dataStream.WriteAsync(BitConverter.GetBytes(stream.Length), 0, 8);
28 |
29 | var buffer = new byte[bufferSize];
30 | int read;
31 |
32 | while ((read = await stream.ReadAsync(buffer, 0, buffer.Length)) > 0)
33 | await dataStream.WriteAsync(buffer, 0, read);
34 |
35 | if (compression) dataStream.Dispose();
36 | }
37 |
38 | ///
39 | /// Receive stream from the remote host
40 | /// Use this method only when not client is not listening for incoming messages (Inside OnReceive event handlers)
41 | ///
42 | ///
43 | /// output stream for receiving data
44 | /// compress data using deflate if set to true
45 | /// length of receiving stream, use prefix when 0
46 | ///
47 | public static async Task ReceiveStreamAsync(this Message message, Stream stream,
48 | bool compression = false, long count = 0, int bufferSize = 1024)
49 | {
50 | if (message?.Client?.BaseSocket == null) throw new Exception("Could not send stream: client is not connected or mesage is invalid");
51 | if (!stream.CanWrite) throw new InvalidDataException("Could not send stream: stream is not writable");
52 |
53 | var networkStream = message.Client.Protocol.GetStream(message.Client);
54 | var dataStream = compression ? new DeflateStream(networkStream, CompressionMode.Decompress, true) : networkStream;
55 |
56 | //Get length of stream
57 | if (count == 0)
58 | {
59 | var length = new byte[8];
60 | await dataStream.ReadAsync(length, 0, length.Length);
61 | count = BitConverter.ToInt64(length, 0);
62 | }
63 |
64 | var buffer = new byte[bufferSize];
65 | long totalReceivedBytes = 0;
66 | int read;
67 |
68 | while (totalReceivedBytes < count &&
69 | (read = await dataStream.ReadAsync(buffer, 0, (int)Math.Min(bufferSize, count - totalReceivedBytes))) > 0)
70 | {
71 | await stream.WriteAsync(buffer, 0, read);
72 | totalReceivedBytes += read;
73 | }
74 |
75 | if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin);
76 | if (compression) dataStream.Dispose();
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/ConnectUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 | using System.Net.Sockets;
4 |
5 | namespace EasyTcp4.ClientUtils
6 | {
7 | public static class ConnectUtil
8 | {
9 | ///
10 | /// Default timeout when timeout parameter is not specified
11 | ///
12 | private const int DefaultTimeout = 5_000;
13 |
14 | ///
15 | /// Establish connection with remote host
16 | ///
17 | ///
18 | /// endPoint of remote host
19 | /// maximum time for connecting with remote host
20 | /// base socket for EasyTcpClient, new one is created when null
21 | /// determines whether the client connected successfully
22 | public static bool Connect(this EasyTcpClient client, EndPoint endPoint, TimeSpan? timeout = null, Socket socket = null)
23 | {
24 | if (client == null) throw new ArgumentException("Could not connect: client is null");
25 | if (endPoint == null) throw new ArgumentException("Could not connect: endpoint is null");
26 | if (client.BaseSocket != null) throw new ArgumentException("Could not connect: client is already connected");
27 |
28 | try
29 | {
30 | client.BaseSocket = socket ?? client.Protocol.GetSocket(endPoint.AddressFamily);
31 | client.BaseSocket.ConnectAsync(endPoint).Wait((int)(timeout?.TotalMilliseconds ?? DefaultTimeout));
32 |
33 | if (client.BaseSocket.Connected && client.Protocol.OnConnect(client))
34 | {
35 | client.FireOnConnect();
36 | return true;
37 | }
38 | }
39 | catch
40 | {
41 | // Ignore exception, dispose (&disconnect) client and return false
42 | }
43 |
44 | client.Dispose(); // Set socket to null
45 | return false;
46 | }
47 |
48 | ///
49 | /// Establish connection with remote host
50 | ///
51 | ///
52 | /// ipAddress of remote host
53 | /// port of remote host
54 | /// maximum time for connecting with remote host
55 | /// base socket for EasyTcpClient, new one is created when null
56 | /// determines whether the client connected successfully
57 | public static bool Connect(this EasyTcpClient client, IPAddress ipAddress, ushort port, TimeSpan? timeout = null, Socket socket = null)
58 | => client.Connect(new IPEndPoint(ipAddress, port), timeout, socket);
59 |
60 | ///
61 | /// Establish connection with remote host
62 | ///
63 | ///
64 | /// ipAddress of remote host as string
65 | /// port of remote host
66 | /// maximum time for connecting with remote host
67 | /// base socket for EasyTcpClient, new one is created when null
68 | /// determines whether the client connected successfully
69 | public static bool Connect(this EasyTcpClient client, string ipAddress, ushort port, TimeSpan? timeout = null, Socket socket = null)
70 | {
71 | if (!IPAddress.TryParse(ipAddress, out IPAddress address))
72 | throw new ArgumentException("Could not connect: ipAddress is not a valid IPv4/IPv6 address");
73 | return client.Connect(address, port, timeout, socket);
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/InformationUtil.cs:
--------------------------------------------------------------------------------
1 | using System.Net;
2 | using System.Net.Sockets;
3 |
4 | namespace EasyTcp4.ClientUtils
5 | {
6 | public static class InformationUtil
7 | {
8 | ///
9 | /// Determines whether client is still connected to the remote endpoint
10 | ///
11 | ///
12 | /// determines whether the client is still connected
13 | public static bool IsConnected(this EasyTcpClient client)
14 | {
15 | if (client?.BaseSocket == null) return false;
16 | if (!client.BaseSocket.Connected)
17 | {
18 | client.FireOnDisconnect();
19 | client.Dispose();
20 | return false;
21 | }
22 |
23 | return true;
24 | }
25 |
26 | ///
27 | /// Get endpoint of client
28 | ///
29 | /// s
30 | /// endpoint of client
31 | public static IPEndPoint GetEndPoint(this EasyTcpClient client) => (IPEndPoint)client?.BaseSocket?.RemoteEndPoint;
32 |
33 | ///
34 | /// Get ip of client
35 | ///
36 | ///
37 | /// ip of client
38 | public static IPAddress GetIp(this EasyTcpClient client) =>
39 | client?.GetEndPoint()?.Address;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/KeepAliveUtil.cs:
--------------------------------------------------------------------------------
1 | #if (NETCOREAPP3_0 || NETCOREAPP3_1 || NET5_0 || NET6_0)
2 | using System;
3 | using System.Net.Sockets;
4 |
5 | namespace EasyTcp4.ClientUtils
6 | {
7 | public static class KeepAliveUtil
8 | {
9 | ///
10 | /// Enable keep alive
11 | ///
12 | ///
13 | /// the number of seconds a TCP connection will remain alive/idle before keepalive probes are sent to the remote
14 | /// the number of seconds a TCP connection will wait for a keepalive response before sending another keepalive probe
15 | /// the number of TCP keep alive probes that will be sent before the connection is terminated
16 | ///
17 | public static T EnableKeepAlive(this T client, int keepAliveTime = 300, int keepAliveInterval = 30, int keepAliveRetryCount = 2)
18 | where T : EasyTcpClient
19 | {
20 | if (client == null) throw new ArgumentException("Could not enable keepAlive: client is null");
21 | if (client.BaseSocket != null) client.BaseSocket.EnableKeepAlive(keepAliveTime, keepAliveInterval, keepAliveRetryCount);
22 | else client.OnConnect += (s, c) => c.BaseSocket.EnableKeepAlive(keepAliveTime, keepAliveInterval, keepAliveRetryCount);
23 | return client;
24 | }
25 |
26 | ///
27 | /// Enable keep alive (socket)
28 | ///
29 | ///
30 | /// the number of seconds a TCP connection will remain alive/idle before keepalive probes are sent to the remote
31 | /// the number of seconds a TCP connection will wait for a keepalive response before sending another keepalive probe
32 | /// the number of TCP keep alive probes that will be sent before the connection is terminated
33 | internal static void EnableKeepAlive(this Socket socket, int keepAliveTime, int keepAliveInterval, int keepAliveRetryCount)
34 | {
35 | socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
36 | socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, keepAliveTime);
37 | socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveInterval, keepAliveInterval);
38 | socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveRetryCount, keepAliveRetryCount);
39 | }
40 | }
41 | }
42 | #endif
43 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/SendAndGetReplyUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 | using EasyTcp4.PacketUtils;
6 |
7 | namespace EasyTcp4.ClientUtils
8 | {
9 | public static class SendAndGetReplyUtil
10 | {
11 | ///
12 | /// Default timeout when timeout parameter is not specified
13 | ///
14 | private const int DefaultTimeout = 5_000;
15 |
16 | ///
17 | /// Send data to remote host. Then wait and return the reply
18 | ///
19 | ///
20 | /// data to send to remote host
21 | /// maximum time to wait for a reply, return null when time expires
22 | /// received reply
23 | public static Message SendAndGetReply(this EasyTcpClient client, TimeSpan? timeout = null, params byte[][] data)
24 | {
25 | Message reply = null;
26 | using var signal = new ManualResetEventSlim();
27 |
28 | client.DataReceiveHandler = message =>
29 | {
30 | reply = message;
31 | client.ResetDataReceiveHandler();
32 | signal.Set(); // Function is no longer used when signal is disposed, therefore this is completely safe
33 | return Task.CompletedTask;
34 | };
35 |
36 | client.Send(data);
37 | signal.Wait(timeout ?? TimeSpan.FromMilliseconds(DefaultTimeout));
38 | if(reply == null) client.ResetDataReceiveHandler();
39 | return reply;
40 | }
41 |
42 | ///
43 | /// Send data to remote host. Then wait and return the reply
44 | ///
45 | ///
46 | /// data to send to remote host
47 | /// maximum time to wait for a reply, return null when time expires
48 | /// compress data using Deflate if set to true
49 | /// received reply
50 | public static Message SendAndGetReply(this EasyTcpClient client, byte[] data,
51 | TimeSpan? timeout = null, bool compression = false)
52 | {
53 | if (compression) data = CompressionUtil.Compress(data);
54 | return client.SendAndGetReply(timeout, data);
55 | }
56 |
57 | ///
58 | /// Send data to remote host. Then wait and return the reply
59 | ///
60 | ///
61 | /// data to send to remote host
62 | /// maximum time to wait for a reply, return null when time expires
63 | /// encoding (default: UTF8)
64 | /// compress data using deflate if set to true
65 | /// received reply
66 | public static Message SendAndGetReply(this EasyTcpClient client, string data,
67 | TimeSpan? timeout = null, Encoding encoding = null, bool compression = false) =>
68 | client.SendAndGetReply((encoding ?? Encoding.UTF8).GetBytes(data), timeout, compression);
69 |
70 | ///
71 | /// Send data to remote host. Then wait and return the reply
72 | ///
73 | ///
74 | /// data to send to remote host
75 | /// maximum time to wait for a reply, return null when time expires
76 | /// compress data using deflate if set to true
77 | /// received reply
78 | public static Message SendAndGetReply(this EasyTcpClient client, IEasyPacket data,
79 | TimeSpan? timeout = null, bool compression = false) =>
80 | client.SendAndGetReply(data.Data, timeout, compression);
81 |
82 | ///
83 | /// Send data to remote host. Then wait and return the reply
84 | ///
85 | ///
86 | /// data to send to remote host
87 | /// maximum time to wait for a reply, return null when time expires
88 | /// compress data using deflate if set to true
89 | /// received reply
90 | public static Message SendAndGetReply(this EasyTcpClient client, object data,
91 | TimeSpan? timeout = null, bool compression = false) =>
92 | client.SendAndGetReply(client?.Serialize(data), timeout, compression);
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/SendUtil.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using EasyTcp4.PacketUtils;
3 |
4 | namespace EasyTcp4.ClientUtils
5 | {
6 | public static class SendUtil
7 | {
8 | ///
9 | /// Send data to remote host
10 | ///
11 | ///
12 | /// data to send to remote host
13 | public static void Send(this EasyTcpClient client, params byte[][] data) =>
14 | client.Protocol.SendMessage(client, data);
15 |
16 | ///
17 | /// Send data to remote host
18 | ///
19 | ///
20 | /// data to send to remote host
21 | /// compress data using deflate if set to true
22 | public static void Send(this EasyTcpClient client, byte[] data, bool compression = false)
23 | {
24 | if (compression) data = CompressionUtil.Compress(data);
25 | client.Protocol.SendMessage(client, data);
26 | }
27 |
28 | ///
29 | /// Send data to remote host
30 | ///
31 | ///
32 | /// data to send to remote host
33 | /// encoding type (Default: UTF8)
34 | /// compress data using deflate if set to true
35 | public static void Send(this EasyTcpClient client, string data, Encoding encoding = null, bool compression = false)
36 | => client.Send((encoding ?? Encoding.UTF8).GetBytes(data), compression);
37 |
38 | ///
39 | /// Send data to remote host
40 | ///
41 | ///
42 | /// data to send to remote host
43 | /// compress data using deflate if set to true
44 | public static void Send(this EasyTcpClient client, IEasyPacket data, bool compression = false)
45 | => client.Send(data.Data, compression);
46 |
47 | ///
48 | /// Send data to remote host
49 | ///
50 | ///
51 | /// data to send to remote host
52 | /// compress data using deflate if set to true
53 | public static void Send(this EasyTcpClient client, object data, bool compression = false)
54 | => client.Send(client?.Serialize(data), compression);
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/EasyTcp4/ClientUtils/StreamUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.IO.Compression;
4 |
5 | namespace EasyTcp4.ClientUtils
6 | {
7 | public static class StreamUtil
8 | {
9 | ///
10 | /// Send stream to the remote host
11 | ///
12 | ///
13 | /// input stream
14 | /// compress data using deflate if set to true
15 | /// determines whether prefix with length of the data is send to the remote host
16 | ///
17 | public static void SendStream(this EasyTcpClient client, Stream stream,
18 | bool compression = false, bool sendLengthPrefix = true, int bufferSize = 1024)
19 | {
20 | if (client?.BaseSocket == null) throw new Exception("Could not send stream: client is not connected");
21 | if (!stream.CanRead) throw new InvalidDataException("Could not send stream: stream is not readable");
22 |
23 | var networkStream = client.Protocol.GetStream(client);
24 | var dataStream = compression ? new DeflateStream(networkStream, CompressionMode.Compress, true) : networkStream;
25 |
26 | if (sendLengthPrefix) dataStream.Write(BitConverter.GetBytes(stream.Length), 0, 8);
27 |
28 | var buffer = new byte[bufferSize];
29 | int read;
30 |
31 | while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
32 | dataStream.Write(buffer, 0, read);
33 |
34 | if (compression) dataStream.Dispose();
35 | }
36 |
37 | ///
38 | /// Receive stream from the remote host
39 | /// Use this method only when not client is not listening for incoming messages (Inside OnReceive event handlers)
40 | ///
41 | ///
42 | /// output stream for receiving data
43 | /// compress data using deflate if set to true
44 | /// length of receiving stream, use prefix when 0
45 | ///
46 | public static void ReceiveStream(this Message message, Stream stream,
47 | bool compression = false, long count = 0, int bufferSize = 1024)
48 | {
49 | if (message?.Client?.BaseSocket == null) throw new Exception("Could not send stream: client is not connected or mesage is invalid");
50 | if (!stream.CanWrite) throw new InvalidDataException("Could not send stream: stream is not writable");
51 |
52 | var networkStream = message.Client.Protocol.GetStream(message.Client);
53 | var dataStream = compression ? new DeflateStream(networkStream, CompressionMode.Decompress, true) : networkStream;
54 |
55 | //Get length of stream
56 | if (count == 0)
57 | {
58 | var length = new byte[8];
59 | dataStream.Read(length, 0, length.Length);
60 | count = BitConverter.ToInt64(length, 0);
61 | }
62 |
63 | var buffer = new byte[bufferSize];
64 | long totalReceivedBytes = 0;
65 | int read;
66 |
67 | while (totalReceivedBytes < count &&
68 | (read = dataStream.Read(buffer, 0, (int)Math.Min(bufferSize, count - totalReceivedBytes))) > 0)
69 | {
70 | stream.Write(buffer, 0, read);
71 | totalReceivedBytes += read;
72 | }
73 |
74 | if (stream.CanSeek) stream.Seek(0, SeekOrigin.Begin);
75 | if (compression) dataStream.Dispose();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/EasyTcp4/EasyTcp4.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | EasyTcp
4 | EasyTcp
5 | Simple framework for TCP clients and servers. Focused on performance and usability.
6 | 4.0.2
7 | Job79
8 | Job79
9 | MIT
10 | https://github.com/Job79/EasyTcp
11 | https://github.com/Job79/EasyTcp
12 | Socket Networking Tcp TcpClient TcpServer Server SimpleTcp EasyTcp AsyncTcp
13 | https://raw.githubusercontent.com/Job79/EasyTcp/master/icon.png
14 | netstandard1.3;netstandard2.0;netcoreapp3.1;net5.0;net6.0
15 |
16 | true
17 | 1591
18 | 8
19 |
20 |
21 |
--------------------------------------------------------------------------------
/EasyTcp4/EasyTcpClient.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Sockets;
5 | using System.Threading.Tasks;
6 | using EasyTcp4.Protocols;
7 | using EasyTcp4.Protocols.Tcp;
8 |
9 | namespace EasyTcp4
10 | {
11 | ///
12 | /// Class with all EasyTcpClient properties and basic functions
13 | /// See the "ClientUtils" folder for all the other functions.
14 | ///
15 | public class EasyTcpClient : IDisposable
16 | {
17 | ///
18 | /// BaseSocket of client
19 | /// null if client isn't connected to remote host.
20 | ///
21 | public Socket BaseSocket { get; set; }
22 |
23 | ///
24 | /// Protocol used for this connection
25 | /// The used protocol determines the internal behavior of the client.
26 | /// The protocol can't be changed when connected.
27 | ///
28 | public IEasyProtocol Protocol
29 | {
30 | get => _protocol;
31 | set
32 | {
33 | if (BaseSocket != null) throw new Exception("Can't change protocol when client is connected");
34 | _protocol = value;
35 | }
36 | }
37 |
38 | private IEasyProtocol _protocol;
39 |
40 | ///
41 | /// List with session variables
42 | /// Available to store custom information.
43 | ///
44 | public Dictionary Session
45 | {
46 | get => _session ??= new Dictionary();
47 | set => _session = value;
48 | }
49 |
50 | private Dictionary _session;
51 |
52 | ///
53 | /// Function used to serialize custom objects
54 | ///
55 | public Func