├── .travis.yml
├── LICENSE
├── README.md
└── Source
├── Managed
├── NanoSockets.cs
└── NanoSockets.csproj
└── Native
├── CMakeLists.txt
├── nanosockets.c
└── nanosockets.h
/.travis.yml:
--------------------------------------------------------------------------------
1 | os:
2 | - osx
3 |
4 | language: c
5 |
6 | compiler: clang
7 |
8 | script:
9 | - cmake ./Source/Native/ -DCMAKE_BUILD_TYPE=Release -DNANOSOCKETS_SHARED=1
10 | - cmake --build .
11 |
12 | deploy:
13 | provider: surge
14 | domain: nxrighthere.surge.sh
15 | skip_cleanup: true
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 Stanislav Denisov (nxrighthere@gmail.com)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | [](https://github.com/nxrighthere/NanoSockets/releases)
6 |
7 | This is a highly portable, lightweight and straightforward, zero-cost abstraction of UDP sockets with dual-stack IPv4/IPv6 support for rapid implementation of message-oriented protocols. The library is designed for cross-language compatibility with C, C++, C#, and other languages. For .NET environment, functions support blittable pointers as an alternative to managed types for usage with unmanaged memory allocator.
8 |
9 | Building
10 | --------
11 | The native library can be built using [CMake](https://cmake.org/download/) with GNU Make or Visual Studio.
12 |
13 | A managed assembly can be built using any available compiling platform that supports C# 4.0 or higher.
14 |
15 | Compiled libraries
16 | --------
17 | You can grab compiled libraries from the [release](https://github.com/nxrighthere/NanoSockets/releases) section.
18 |
19 | Binaries are provided only for traditional platforms: Windows, Linux, and macOS (x64).
20 |
21 | Supported OS versions:
22 | - Windows 7 or higher
23 | - Linux 4.4 or higher
24 | - macOS 10.12 or higher
25 |
26 | Usage
27 | --------
28 | Before starting to work, the library should be initialized using `NanoSockets.UDP.Initialize();` function.
29 |
30 | After the work is done, deinitialize the library using `NanoSockets.UDP.Deinitialize();` function.
31 |
32 | ### .NET environment
33 | ##### Start a new server
34 | ```c#
35 | Socket server = UDP.Create(256 * 1024, 256 * 1024);
36 | Address listenAddress = new Address();
37 |
38 | listenAddress.port = port;
39 |
40 | if (UDP.SetIP(ref listenAddress, "::0") == Status.OK)
41 | Console.WriteLine("Address set!");
42 |
43 | if (UDP.Bind(server, ref listenAddress) == 0)
44 | Console.WriteLine("Socket bound!");
45 |
46 | if (UDP.SetDontFragment(server) != Status.OK)
47 | Console.WriteLine("Don't fragment option error!");
48 |
49 | if (UDP.SetNonBlocking(server) != Status.OK)
50 | Console.WriteLine("Non-blocking option error!");
51 |
52 | StringBuilder ip = new StringBuilder(UDP.hostNameSize);
53 | Address address = new Address();
54 | byte[] buffer = new byte[1024];
55 |
56 | while (!Console.KeyAvailable) {
57 | if (UDP.Poll(server, 15) > 0) {
58 | int dataLength = 0;
59 |
60 | while ((dataLength = UDP.Receive(server, ref address, buffer, buffer.Length)) > 0) {
61 | UDP.GetIP(ref address, ip, ip.Capacity);
62 |
63 | Console.WriteLine("Message received from - IP: " + ip + ", Data length: " + dataLength);
64 |
65 | UDP.Send(server, ref address, buffer, buffer.Length);
66 | }
67 | }
68 | }
69 |
70 | UDP.Destroy(ref server);
71 | ```
72 |
73 | ##### Start a new client
74 | ```c#
75 | Socket client = UDP.Create(256 * 1024, 256 * 1024);
76 | Address connectionAddress = new Address();
77 |
78 | connectionAddress.port = port;
79 |
80 | if (UDP.SetIP(ref connectionAddress, "::1") == Status.OK)
81 | Console.WriteLine("Address set!");
82 |
83 | if (UDP.Connect(client, ref connectionAddress) == 0)
84 | Console.WriteLine("Socket connected!");
85 |
86 | if (UDP.SetDontFragment(client) != Status.OK)
87 | Console.WriteLine("Don't fragment option error!");
88 |
89 | if (UDP.SetNonBlocking(client) != Status.OK)
90 | Console.WriteLine("Non-blocking option error!");
91 |
92 | byte[] buffer = new byte[1024];
93 |
94 | UDP.Send(client, IntPtr.Zero, buffer, buffer.Length);
95 |
96 | while (!Console.KeyAvailable) {
97 | if (UDP.Poll(client, 15) > 0) {
98 | int dataLength = 0;
99 |
100 | while ((dataLength = UDP.Receive(client, IntPtr.Zero, buffer, buffer.Length)) > 0) {
101 | Console.WriteLine("Message received from server - Data length: " + dataLength);
102 |
103 | UDP.Send(client, IntPtr.Zero, buffer, buffer.Length);
104 | }
105 | }
106 | }
107 |
108 | UDP.Destroy(ref client);
109 | ```
110 |
111 | ### Unity
112 | Usage is almost the same as in the .NET environment, except that the console functions must be replaced with functions provided by Unity. If the `UDP.Poll()` will be called in a game loop, then make sure that the timeout parameter set to 0 which means non-blocking. Also, keep Unity run in background by enabling the appropriate option in the player settings.
113 |
114 | API reference
115 | --------
116 | ### Enumerations
117 | #### Status
118 | Definitions of status types for functions:
119 |
120 | `OK`
121 |
122 | `Error`
123 |
124 | ### Structures
125 | #### Socket
126 | Contains a blittable structure with socket handle.
127 |
128 | `Socket.handle` a socket handle.
129 |
130 | `Socket.IsCreated` checks if a socket is created.
131 |
132 | #### Address
133 | Contains a blittable structure with anonymous host data and port number.
134 |
135 | `Address.port` a port number.
136 |
137 | ### Classes
138 | #### UDP
139 | `UDP.Initialize()` initializes the native library. Should be called before starting the work. Returns status with a result.
140 |
141 | `UDP.Deinitialize()` deinitializes the native library. Should be called after the work is done.
142 |
143 | `UDP.Create(int sendBufferSize, int receiveBufferSize)` creates a new socket with a specified size of buffers for sending and receiving. Returns the `Socket` structure with the handle.
144 |
145 | `UDP.Destroy(ref Socket socket)` destroys a socket and reset the handle.
146 |
147 | `UDP.Bind(Socket socket, ref Address address)` assigns an address to a socket. The address parameter can be set to `IntPtr.Zero` to let the operating system assign any address. Returns 0 on success or != 0 on failure.
148 |
149 | `UDP.Connect(Socket socket, ref Address address)` connects a socket to an address. Returns 0 on success or != 0 on failure.
150 |
151 | `UDP.SetOption(Socket socket, int level, int optionName, ref int optionValue, int optionLength)` sets the current value for a socket option associated with a socket. This function can be used to set platform-specific options that were not specified at socket creation by default. Returns status with a result.
152 |
153 | `UDP.GetOption(Socket socket, int level, int optionName, ref int optionValue, ref int optionLength)` gets the current value for a socket option associated with a socket. A length of an option value should be initially set to an appropriate size. Returns status with a result.
154 |
155 | `UDP.SetNonBlocking(Socket socket, bool shouldBlock)` sets a non-blocking I/O mode for a socket. Returns status with a result.
156 |
157 | `UDP.SetDontFragment(Socket socket)` sets a don't fragment mode for a socket. Returns status with a result.
158 |
159 | `UDP.Poll(Socket socket, long timeout)` determines the status of a socket and waiting if necessary. This function can be used for readiness-oriented receiving. The timeout parameter may be specified in milliseconds to control polling duration. If a timeout of 0 is specified, this function will return immediately. If the time limit expired it will return 0. If a socket is ready for receiving it will return 1. Otherwise, it will return < 0 if an error occurred.
160 |
161 | `UDP.Send(Socket socket, ref Address address, byte[] buffer, int bufferLength)` sends a message to the specified address of a receiver. The address parameter can be set to `IntPtr.Zero` if a socket is connected to an address. A pointer `IntPtr` to a native buffer can be used instead of a reference to a byte array. Returns the total number of bytes sent, which can be less than the number indicated by the buffer length. Otherwise, it will return < 0 if an error occurred.
162 |
163 | `UDP.Receive(Socket socket, ref Address address, byte[] buffer, int bufferLength)` receives a message and obtains the address of a sender. The address parameter can be set to `IntPtr.Zero` to skip the address obtainment. A pointer `IntPtr` to a native buffer can be used instead of a reference to a byte array. Returns the total number of bytes received. Otherwise, it will return < 0 if an error occurred.
164 |
165 | `UDP.GetAddress(Socket socket, ref Address address)` gets an address from a bound or connected socket. This function is especially useful to determine the local association that has been set by the operating system. Returns status with a result.
166 |
167 | `UDP.IsEqual(ref Address left, ref Address right)` compares two addresses for equality. Returns status with a result.
168 |
169 | `UDP.SetIP(ref Address address, string ip)` sets an IP address. A pointer `IntPtr` can be used instead of the immutable string. Returns status with a result.
170 |
171 | `UDP.GetIP(ref Address address, StringBuilder ip, int ipLength)` gets an IP address. The capacity of the mutable string should be equal to `UDP.hostNameSize` constant field. A pointer `IntPtr` can be used instead of the mutable string. Returns status with a result.
172 |
173 | `UDP.SetHostName(ref Address address, string name)` sets host name or an IP address. A pointer `IntPtr` can be used instead of the immutable string. Returns status with a result.
174 |
175 | `UDP.GetHostName(ref Address address, StringBuilder name, int nameLength)` attempts to do a reverse lookup from the address. A pointer `IntPtr` can be used instead of the mutable string. Returns status with a result.
176 |
--------------------------------------------------------------------------------
/Source/Managed/NanoSockets.cs:
--------------------------------------------------------------------------------
1 | /*
2 | * Lightweight UDP sockets abstraction for rapid implementation of message-oriented protocols
3 | * Copyright (c) 2019 Stanislav Denisov
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in all
13 | * copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 |
24 | using System;
25 | using System.Runtime.InteropServices;
26 | using System.Security;
27 | using System.Text;
28 |
29 | namespace NanoSockets {
30 | public enum Status {
31 | OK = 0,
32 | Error = -1
33 | }
34 |
35 | [StructLayout(LayoutKind.Explicit, Size = 8)]
36 | public struct Socket {
37 | [FieldOffset(0)]
38 | private long handle;
39 |
40 | public bool IsCreated {
41 | get {
42 | return handle > 0;
43 | }
44 | }
45 | }
46 |
47 | [StructLayout(LayoutKind.Explicit, Size = 18)]
48 | public struct Address : IEquatable {
49 | [FieldOffset(0)]
50 | private ulong address0;
51 | [FieldOffset(8)]
52 | private ulong address1;
53 | [FieldOffset(16)]
54 | public ushort port;
55 | [FieldOffset(16)]
56 | public ushort Port;
57 |
58 | public bool Equals(Address other) {
59 | return address0 == other.address0 && address1 == other.address1 && port == other.port;
60 | }
61 |
62 | public override bool Equals(object obj) {
63 | if (obj is Address)
64 | return Equals((Address)obj);
65 |
66 | return false;
67 | }
68 |
69 | public override int GetHashCode() {
70 | int hash = 17;
71 |
72 | hash = hash * 31 + address0.GetHashCode();
73 | hash = hash * 31 + address1.GetHashCode();
74 | hash = hash * 31 + port.GetHashCode();
75 |
76 | return hash;
77 | }
78 |
79 | public override string ToString() {
80 | StringBuilder ip = new StringBuilder(64);
81 |
82 | NanoSockets.UDP.GetIP(ref this, ip, 64);
83 |
84 | return String.Format("IP:{0} Port:{1}", ip, this.port);
85 | }
86 |
87 | public static Address CreateFromIpPort(string ip, ushort port) {
88 | Address address = default(Address);
89 |
90 | NanoSockets.UDP.SetIP(ref address, ip);
91 | address.port = port;
92 |
93 | return address;
94 | }
95 | }
96 |
97 | [SuppressUnmanagedCodeSecurity]
98 | public static class UDP {
99 | #if __IOS__ || UNITY_IOS && !UNITY_EDITOR
100 | private const string nativeLibrary = "__Internal";
101 | #else
102 | private const string nativeLibrary = "nanosockets";
103 | #endif
104 |
105 | public const int hostNameSize = 1025;
106 |
107 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_initialize", CallingConvention = CallingConvention.Cdecl)]
108 | public static extern Status Initialize();
109 |
110 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_deinitialize", CallingConvention = CallingConvention.Cdecl)]
111 | public static extern void Deinitialize();
112 |
113 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_create", CallingConvention = CallingConvention.Cdecl)]
114 | public static extern Socket Create(int sendBufferSize, int receiveBufferSize);
115 |
116 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_destroy", CallingConvention = CallingConvention.Cdecl)]
117 | public static extern void Destroy(ref Socket socket);
118 |
119 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_bind", CallingConvention = CallingConvention.Cdecl)]
120 | public static extern int Bind(Socket socket, IntPtr address);
121 |
122 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_bind", CallingConvention = CallingConvention.Cdecl)]
123 | public static extern int Bind(Socket socket, ref Address address);
124 |
125 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_connect", CallingConvention = CallingConvention.Cdecl)]
126 | public static extern int Connect(Socket socket, ref Address address);
127 |
128 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_set_option", CallingConvention = CallingConvention.Cdecl)]
129 | public static extern Status SetOption(Socket socket, int level, int optionName, ref int optionValue, int optionLength);
130 |
131 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_get_option", CallingConvention = CallingConvention.Cdecl)]
132 | public static extern Status GetOption(Socket socket, int level, int optionName, ref int optionValue, ref int optionLength);
133 |
134 | public static Status SetNonBlocking(Socket socket, bool shouldBlock = false) => SetNonBlocking(socket, shouldBlock ? 0 : 1);
135 |
136 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_set_nonblocking", CallingConvention = CallingConvention.Cdecl)]
137 | private static extern Status SetNonBlocking(Socket socket, byte state);
138 |
139 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_set_dontfragment", CallingConvention = CallingConvention.Cdecl)]
140 | public static extern Status SetDontFragment(Socket socket);
141 |
142 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_poll", CallingConvention = CallingConvention.Cdecl)]
143 | public static extern int Poll(Socket socket, long timeout);
144 |
145 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_send", CallingConvention = CallingConvention.Cdecl)]
146 | public static extern int Send(Socket socket, IntPtr address, IntPtr buffer, int bufferLength);
147 |
148 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_send", CallingConvention = CallingConvention.Cdecl)]
149 | public static extern int Send(Socket socket, IntPtr address, byte[] buffer, int bufferLength);
150 |
151 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_send", CallingConvention = CallingConvention.Cdecl)]
152 | public static extern int Send(Socket socket, ref Address address, IntPtr buffer, int bufferLength);
153 |
154 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_send", CallingConvention = CallingConvention.Cdecl)]
155 | public static extern int Send(Socket socket, ref Address address, byte[] buffer, int bufferLength);
156 |
157 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_send_offset", CallingConvention = CallingConvention.Cdecl)]
158 | public static extern int Send(Socket socket, IntPtr address, byte[] buffer, int offset, int bufferLength);
159 |
160 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_send_offset", CallingConvention = CallingConvention.Cdecl)]
161 | public static extern int Send(Socket socket, ref Address address, byte[] buffer, int offset, int bufferLength);
162 |
163 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_receive", CallingConvention = CallingConvention.Cdecl)]
164 | public static extern int Receive(Socket socket, IntPtr address, IntPtr buffer, int bufferLength);
165 |
166 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_receive", CallingConvention = CallingConvention.Cdecl)]
167 | public static extern int Receive(Socket socket, IntPtr address, byte[] buffer, int bufferLength);
168 |
169 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_receive", CallingConvention = CallingConvention.Cdecl)]
170 | public static extern int Receive(Socket socket, ref Address address, IntPtr buffer, int bufferLength);
171 |
172 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_receive", CallingConvention = CallingConvention.Cdecl)]
173 | public static extern int Receive(Socket socket, ref Address address, byte[] buffer, int bufferLength);
174 |
175 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_receive_offset", CallingConvention = CallingConvention.Cdecl)]
176 | public static extern int Receive(Socket socket, IntPtr address, byte[] buffer, int offset, int bufferLength);
177 |
178 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_receive_offset", CallingConvention = CallingConvention.Cdecl)]
179 | public static extern int Receive(Socket socket, ref Address address, byte[] buffer, int offset, int bufferLength);
180 |
181 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_address_get", CallingConvention = CallingConvention.Cdecl)]
182 | public static extern Status GetAddress(Socket socket, ref Address address);
183 |
184 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_address_is_equal", CallingConvention = CallingConvention.Cdecl)]
185 | public static extern Status IsEqual(ref Address left, ref Address right);
186 |
187 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_address_set_ip", CallingConvention = CallingConvention.Cdecl)]
188 | public static extern Status SetIP(ref Address address, IntPtr ip);
189 |
190 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_address_set_ip", CallingConvention = CallingConvention.Cdecl)]
191 | public static extern Status SetIP(ref Address address, string ip);
192 |
193 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_address_get_ip", CallingConvention = CallingConvention.Cdecl)]
194 | public static extern Status GetIP(ref Address address, IntPtr ip, int ipLength);
195 |
196 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_address_get_ip", CallingConvention = CallingConvention.Cdecl)]
197 | public static extern Status GetIP(ref Address address, StringBuilder ip, int ipLength);
198 |
199 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_address_set_hostname", CallingConvention = CallingConvention.Cdecl)]
200 | public static extern Status SetHostName(ref Address address, IntPtr name);
201 |
202 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_address_set_hostname", CallingConvention = CallingConvention.Cdecl)]
203 | public static extern Status SetHostName(ref Address address, string name);
204 |
205 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_address_get_hostname", CallingConvention = CallingConvention.Cdecl)]
206 | public static extern Status GetHostName(ref Address address, IntPtr name, int nameLength);
207 |
208 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_address_get_hostname", CallingConvention = CallingConvention.Cdecl)]
209 | public static extern Status GetHostName(ref Address address, StringBuilder name, int nameLength);
210 |
211 | #if NANOSOCKETS_UNSAFE_API
212 | public static unsafe class Unsafe {
213 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_receive", CallingConvention = CallingConvention.Cdecl)]
214 | public static extern int Receive(Socket socket, Address* address, byte* buffer, int bufferLength);
215 |
216 | [DllImport(nativeLibrary, EntryPoint = "nanosockets_send", CallingConvention = CallingConvention.Cdecl)]
217 | public static extern int Send(Socket socket, Address* address, byte* buffer, int bufferLength);
218 | }
219 | #endif
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/Source/Managed/NanoSockets.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Library
5 | netstandard2.0;netcoreapp2.0
6 | x64
7 | NanoSockets
8 | 3
9 |
10 |
11 |
12 | false
13 | True
14 |
15 |
16 |
17 | true
18 | False
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Source/Native/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 2.6)
2 | project(nanosockets C)
3 |
4 | set(NANOSOCKETS_STATIC "0" CACHE BOOL "Create a static library")
5 | set(NANOSOCKETS_SHARED "0" CACHE BOOL "Create a shared library")
6 |
7 | if (MSYS OR MINGW)
8 | set(CMAKE_C_FLAGS "-static")
9 |
10 | add_definitions(-DWINVER=0x0601)
11 | add_definitions(-D_WIN32_WINNT=0x0601)
12 | endif()
13 |
14 | if (NANOSOCKETS_STATIC)
15 | add_library(nanosockets_static STATIC nanosockets.c ${SOURCES})
16 |
17 | if (NOT UNIX)
18 | target_link_libraries(nanosockets_static ws2_32)
19 | SET_TARGET_PROPERTIES(nanosockets_static PROPERTIES PREFIX "")
20 | endif()
21 | endif()
22 |
23 | if (NANOSOCKETS_SHARED)
24 | add_definitions(-DNANOSOCKETS_DLL)
25 | add_library(nanosockets SHARED nanosockets.c ${SOURCES})
26 |
27 | if (NOT UNIX)
28 | target_link_libraries(nanosockets ws2_32)
29 | SET_TARGET_PROPERTIES(nanosockets PROPERTIES PREFIX "")
30 | endif()
31 | endif()
32 |
--------------------------------------------------------------------------------
/Source/Native/nanosockets.c:
--------------------------------------------------------------------------------
1 | /*
2 | * Lightweight UDP sockets abstraction for rapid implementation of message-oriented protocols
3 | * Copyright (c) 2019 Stanislav Denisov
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in all
13 | * copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 |
24 | #define NANOSOCKETS_IMPLEMENTATION
25 | #include "nanosockets.h"
26 |
--------------------------------------------------------------------------------
/Source/Native/nanosockets.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Lightweight UDP sockets abstraction for rapid implementation of message-oriented protocols
3 | * Copyright (c) 2019 Stanislav Denisov
4 | *
5 | * Permission is hereby granted, free of charge, to any person obtaining a copy
6 | * of this software and associated documentation files (the "Software"), to deal
7 | * in the Software without restriction, including without limitation the rights
8 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | * copies of the Software, and to permit persons to whom the Software is
10 | * furnished to do so, subject to the following conditions:
11 | *
12 | * The above copyright notice and this permission notice shall be included in all
13 | * copies or substantial portions of the Software.
14 | *
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | * SOFTWARE.
22 | */
23 |
24 | #ifndef NANOSOCKETS_H
25 | #define NANOSOCKETS_H
26 |
27 | #include
28 |
29 | #define NANOSOCKETS_VERSION_MAJOR 1
30 | #define NANOSOCKETS_VERSION_MINOR 0
31 | #define NANOSOCKETS_VERSION_PATCH 6
32 |
33 | #ifdef _WIN32
34 | #define NANOSOCKETS_WINDOWS 1
35 | #elif defined __unix__
36 | #define NANOSOCKETS_UNIX 2
37 | #elif defined __APPLE__
38 | #define NANOSOCKETS_MAC 3
39 | #endif
40 |
41 | #if defined(NANOSOCKETS_WINDOWS) && defined(NANOSOCKETS_DLL)
42 | #ifdef NANOSOCKETS_IMPLEMENTATION
43 | #define NANOSOCKETS_API __declspec(dllexport)
44 | #else
45 | #define NANOSOCKETS_API __declspec(dllimport)
46 | #endif
47 | #else
48 | #define NANOSOCKETS_API extern
49 | #endif
50 |
51 | #ifdef NANOSOCKETS_WINDOWS
52 | #include
53 | #else
54 | #ifdef NANOSOCKETS_MAC
55 | #define __APPLE_USE_RFC_3542
56 | #endif
57 |
58 | #include
59 | #endif
60 |
61 | #define NANOSOCKETS_HOSTNAME_SIZE 1025
62 |
63 | // API
64 |
65 | #ifdef __cplusplus
66 | extern "C" {
67 | #endif
68 |
69 | typedef int64_t NanoSocket;
70 |
71 | typedef enum _NanoStatus {
72 | NANOSOCKETS_STATUS_OK = 0,
73 | NANOSOCKETS_STATUS_ERROR = -1
74 | } NanoStatus;
75 |
76 | typedef struct _NanoAddress {
77 | union {
78 | struct in6_addr ipv6;
79 | struct {
80 | uint8_t zeros[10];
81 | uint16_t ffff;
82 | struct in_addr ip;
83 | } ipv4;
84 | };
85 | uint16_t port;
86 | } NanoAddress;
87 |
88 | NANOSOCKETS_API NanoStatus nanosockets_initialize(void);
89 |
90 | NANOSOCKETS_API void nanosockets_deinitialize(void);
91 |
92 | NANOSOCKETS_API NanoSocket nanosockets_create(int, int);
93 |
94 | NANOSOCKETS_API void nanosockets_destroy(NanoSocket*);
95 |
96 | NANOSOCKETS_API int nanosockets_bind(NanoSocket, const NanoAddress*);
97 |
98 | NANOSOCKETS_API int nanosockets_connect(NanoSocket, const NanoAddress*);
99 |
100 | NANOSOCKETS_API NanoStatus nanosockets_set_option(NanoSocket, int, int, const int*, int);
101 |
102 | NANOSOCKETS_API NanoStatus nanosockets_get_option(NanoSocket, int, int, int*, int*);
103 |
104 | NANOSOCKETS_API NanoStatus nanosockets_set_nonblocking(NanoSocket, uint8_t);
105 |
106 | NANOSOCKETS_API NanoStatus nanosockets_set_dontfragment(NanoSocket);
107 |
108 | NANOSOCKETS_API int nanosockets_poll(NanoSocket, long);
109 |
110 | NANOSOCKETS_API int nanosockets_send(NanoSocket, const NanoAddress*, const uint8_t*, int);
111 |
112 | NANOSOCKETS_API int nanosockets_send_offset(NanoSocket, const NanoAddress*, const uint8_t*, int, int);
113 |
114 | NANOSOCKETS_API int nanosockets_receive(NanoSocket, NanoAddress*, uint8_t*, int);
115 |
116 | NANOSOCKETS_API int nanosockets_receive_offset(NanoSocket, NanoAddress*, uint8_t*, int, int);
117 |
118 | NANOSOCKETS_API NanoStatus nanosockets_address_get(NanoSocket, NanoAddress*);
119 |
120 | NANOSOCKETS_API NanoStatus nanosockets_address_is_equal(const NanoAddress*, const NanoAddress*);
121 |
122 | NANOSOCKETS_API NanoStatus nanosockets_address_set_ip(NanoAddress*, const char*);
123 |
124 | NANOSOCKETS_API NanoStatus nanosockets_address_get_ip(const NanoAddress*, char*, int);
125 |
126 | NANOSOCKETS_API NanoStatus nanosockets_address_set_hostname(NanoAddress*, const char*);
127 |
128 | NANOSOCKETS_API NanoStatus nanosockets_address_get_hostname(const NanoAddress*, char*, int);
129 |
130 | #ifdef __cplusplus
131 | }
132 | #endif
133 |
134 | #if defined(NANOSOCKETS_IMPLEMENTATION) && !defined(NANOSOCKETS_IMPLEMENTATION_DONE)
135 | #define NANOSOCKETS_IMPLEMENTATION_DONE 1
136 |
137 | #include
138 |
139 | #ifndef NANOSOCKETS_WINDOWS
140 | #include
141 | #include
142 | #include
143 | #include
144 | #include
145 | #endif
146 |
147 | // Macros
148 |
149 | #define NANOSOCKETS_HOST_TO_NET_16(value) (htons(value))
150 | #define NANOSOCKETS_HOST_TO_NET_32(value) (htonl(value))
151 | #define NANOSOCKETS_NET_TO_HOST_16(value) (ntohs(value))
152 | #define NANOSOCKETS_NET_TO_HOST_32(value) (ntohl(value))
153 |
154 | // Functions
155 |
156 | inline static int nanosockets_array_is_zeroed(const uint8_t* array, int length) {
157 | for (size_t i = 0; i < length; i++) {
158 | if (array[i] != 0)
159 | return -1;
160 | }
161 |
162 | return 0;
163 | }
164 |
165 | inline static void nanosockets_address_extract(NanoAddress* address, const struct sockaddr_storage* source) {
166 | if (source->ss_family == AF_INET) {
167 | struct sockaddr_in* socketAddress = (struct sockaddr_in*)source;
168 |
169 | memset(address, 0, sizeof(address->ipv4.zeros));
170 |
171 | address->ipv4.ffff = 0xFFFF;
172 | address->ipv4.ip = socketAddress->sin_addr;
173 | address->port = NANOSOCKETS_NET_TO_HOST_16(socketAddress->sin_port);
174 | } else if (source->ss_family == AF_INET6) {
175 | struct sockaddr_in6* socketAddress = (struct sockaddr_in6*)source;
176 |
177 | address->ipv6 = socketAddress->sin6_addr;
178 | address->port = NANOSOCKETS_NET_TO_HOST_16(socketAddress->sin6_port);
179 | }
180 | }
181 |
182 | NanoStatus nanosockets_initialize(void) {
183 | #ifdef NANOSOCKETS_WINDOWS
184 | WSADATA wsaData = { 0 };
185 |
186 | if (WSAStartup(MAKEWORD(2, 2), &wsaData))
187 | return NANOSOCKETS_STATUS_ERROR;
188 |
189 | if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
190 | WSACleanup();
191 |
192 | return NANOSOCKETS_STATUS_ERROR;
193 | }
194 | #endif
195 |
196 | return NANOSOCKETS_STATUS_OK;
197 | }
198 |
199 | void nanosockets_deinitialize(void) {
200 | #ifdef NANOSOCKETS_WINDOWS
201 | WSACleanup();
202 | #endif
203 | }
204 |
205 | NanoSocket nanosockets_create(int sendBufferSize, int receiveBufferSize) {
206 | int socketType = SOCK_DGRAM;
207 |
208 | #ifdef SOCK_CLOEXEC
209 | socketType |= SOCK_CLOEXEC;
210 | #endif
211 |
212 | NanoSocket socketHandle = socket(PF_INET6, socketType, 0);
213 |
214 | if (socketHandle > -1) {
215 | int onlyIPv6 = 0;
216 |
217 | if (setsockopt(socketHandle, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&onlyIPv6, sizeof(onlyIPv6)) != 0)
218 | goto destroy;
219 |
220 | if (setsockopt(socketHandle, SOL_SOCKET, SO_SNDBUF, (const char*)&sendBufferSize, sizeof(sendBufferSize)) != 0)
221 | goto destroy;
222 |
223 | if (setsockopt(socketHandle, SOL_SOCKET, SO_RCVBUF, (const char*)&receiveBufferSize, sizeof(receiveBufferSize)) != 0)
224 | goto destroy;
225 |
226 | goto create;
227 |
228 | destroy:
229 |
230 | nanosockets_destroy(&socketHandle);
231 |
232 | return -1;
233 | }
234 |
235 | create:
236 |
237 | return socketHandle;
238 | }
239 |
240 | void nanosockets_destroy(NanoSocket* socket) {
241 | if (*socket > 0) {
242 | #if NANOSOCKETS_WINDOWS
243 | closesocket(*socket);
244 | #else
245 | close(*socket);
246 | #endif
247 |
248 | *socket = 0;
249 | }
250 | }
251 |
252 | int nanosockets_bind(NanoSocket socket, const NanoAddress* address) {
253 | struct sockaddr_in6 socketAddress = { 0 };
254 |
255 | socketAddress.sin6_family = AF_INET6;
256 |
257 | if (address == NULL) {
258 | socketAddress.sin6_addr = in6addr_any;
259 | socketAddress.sin6_port = 0;
260 | } else {
261 | socketAddress.sin6_addr = address->ipv6;
262 | socketAddress.sin6_port = NANOSOCKETS_HOST_TO_NET_16(address->port);
263 | }
264 |
265 | return bind(socket, (struct sockaddr*)&socketAddress, sizeof(socketAddress));
266 | }
267 |
268 | int nanosockets_connect(NanoSocket socket, const NanoAddress* address) {
269 | struct sockaddr_in6 socketAddress = { 0 };
270 |
271 | socketAddress.sin6_family = AF_INET6;
272 | socketAddress.sin6_addr = address->ipv6;
273 | socketAddress.sin6_port = NANOSOCKETS_HOST_TO_NET_16(address->port);
274 |
275 | return connect(socket, (struct sockaddr*)&socketAddress, sizeof(socketAddress));
276 | }
277 |
278 | NanoStatus nanosockets_set_option(NanoSocket socket, int level, int optionName, const int* optionValue, int optionLength) {
279 | if (setsockopt(socket, level, optionName, (const char*)optionValue, optionLength) == 0)
280 | return NANOSOCKETS_STATUS_OK;
281 | else
282 | return NANOSOCKETS_STATUS_ERROR;
283 | }
284 |
285 | NanoStatus nanosockets_get_option(NanoSocket socket, int level, int optionName, int* optionValue, int* optionLength) {
286 | if (getsockopt(socket, level, optionName, (char*)optionValue, (socklen_t*)optionLength) == 0)
287 | return NANOSOCKETS_STATUS_OK;
288 | else
289 | return NANOSOCKETS_STATUS_ERROR;
290 | }
291 |
292 | NanoStatus nanosockets_set_nonblocking(NanoSocket socket, uint8_t state) {
293 | #ifdef NANOSOCKETS_WINDOWS
294 | DWORD nonBlocking = state;
295 |
296 | if (ioctlsocket(socket, FIONBIO, &nonBlocking) != 0)
297 | return NANOSOCKETS_STATUS_ERROR;
298 | #else
299 | int nonBlocking = state;
300 |
301 | if (fcntl(socket, F_SETFL, O_NONBLOCK, nonBlocking) == -1)
302 | return NANOSOCKETS_STATUS_ERROR;
303 | #endif
304 |
305 | return NANOSOCKETS_STATUS_OK;
306 | }
307 |
308 | NanoStatus nanosockets_set_dontfragment(NanoSocket socket) {
309 | #ifdef IP_DONTFRAG
310 | int dontFragment = 1;
311 |
312 | if (setsockopt(socket, IPPROTO_IPV6, IP_DONTFRAG, (const char*)&dontFragment, sizeof(dontFragment)) != 0)
313 | return NANOSOCKETS_STATUS_ERROR;
314 | #elif defined IP_DONTFRAGMENT
315 | DWORD dontFragment = 1;
316 |
317 | if (setsockopt(socket, IPPROTO_IPV6, IP_DONTFRAGMENT, (const char*)&dontFragment, sizeof(dontFragment)) != 0)
318 | return NANOSOCKETS_STATUS_ERROR;
319 | #elif defined IPV6_DONTFRAG
320 | int dontFragment = 1;
321 |
322 | if (setsockopt(socket, IPPROTO_IPV6, IPV6_DONTFRAG, (const char*)&dontFragment, sizeof(dontFragment)) != 0)
323 | return NANOSOCKETS_STATUS_ERROR;
324 | #else
325 | #error "Don't fragment socket option is not implemented for this platform"
326 | #endif
327 |
328 | return NANOSOCKETS_STATUS_OK;
329 | }
330 |
331 | int nanosockets_poll(NanoSocket socket, long timeout) {
332 | fd_set set = { 0 };
333 | struct timeval time = { 0 };
334 |
335 | FD_ZERO(&set);
336 | FD_SET(socket, &set);
337 |
338 | time.tv_sec = timeout / 1000;
339 | time.tv_usec = (timeout % 1000) * 1000;
340 |
341 | #pragma warning(suppress: 4244)
342 | return select(socket + 1, &set, NULL, NULL, &time);
343 | }
344 |
345 | int nanosockets_send(NanoSocket socket, const NanoAddress* address, const uint8_t* buffer, int bufferLength) {
346 | struct sockaddr_in6 socketAddress = { 0 };
347 |
348 | if (address != NULL) {
349 | socketAddress.sin6_family = AF_INET6;
350 | socketAddress.sin6_addr = address->ipv6;
351 | socketAddress.sin6_port = NANOSOCKETS_HOST_TO_NET_16(address->port);
352 | }
353 |
354 | return sendto(socket, (const char*)buffer, bufferLength, 0, (address != NULL ? (struct sockaddr*)&socketAddress : NULL), sizeof(socketAddress));
355 | }
356 |
357 | int nanosockets_send_offset(NanoSocket socket, const NanoAddress* address, const uint8_t* buffer, int offset, int bufferLength) {
358 | return nanosockets_send(socket, address, buffer + offset, bufferLength);
359 | }
360 |
361 | int nanosockets_receive(NanoSocket socket, NanoAddress* address, uint8_t* buffer, int bufferLength) {
362 | struct sockaddr_storage addressStorage = { 0 };
363 | socklen_t addressLength = sizeof(addressStorage);
364 |
365 | int socketBytes = recvfrom(socket, (char*)buffer, bufferLength, 0, (struct sockaddr*)&addressStorage, &addressLength);
366 |
367 | if (address != NULL)
368 | nanosockets_address_extract(address, &addressStorage);
369 |
370 | return socketBytes;
371 | }
372 |
373 | int nanosockets_receive_offset(NanoSocket socket, NanoAddress* address, uint8_t* buffer, int offset, int bufferLength) {
374 | return nanosockets_receive(socket, address, buffer + offset, bufferLength);
375 | }
376 |
377 | NanoStatus nanosockets_address_get(NanoSocket socket, NanoAddress* address) {
378 | struct sockaddr_storage addressStorage = { 0 };
379 | socklen_t addressLength = sizeof(addressStorage);
380 |
381 | if (getsockname(socket, (struct sockaddr*)&addressStorage, &addressLength) == -1)
382 | return NANOSOCKETS_STATUS_ERROR;
383 |
384 | nanosockets_address_extract(address, &addressStorage);
385 |
386 | return NANOSOCKETS_STATUS_OK;
387 | }
388 |
389 | NanoStatus nanosockets_address_is_equal(const NanoAddress* left, const NanoAddress* right) {
390 | if (memcmp(left, right, sizeof(struct in6_addr)) == 0 && left->port == right->port)
391 | return NANOSOCKETS_STATUS_OK;
392 | else
393 | return NANOSOCKETS_STATUS_ERROR;
394 | }
395 |
396 | NanoStatus nanosockets_address_set_ip(NanoAddress* address, const char* ip) {
397 | int type = AF_INET6;
398 | void* destination = &address->ipv6;
399 |
400 | if (strchr(ip, ':') == NULL) {
401 | type = AF_INET;
402 |
403 | memset(address, 0, sizeof(address->ipv4.zeros));
404 |
405 | address->ipv4.ffff = 0xFFFF;
406 | destination = &address->ipv4.ip;
407 | }
408 |
409 | if (!inet_pton(type, ip, destination))
410 | return NANOSOCKETS_STATUS_ERROR;
411 |
412 | return NANOSOCKETS_STATUS_OK;
413 | }
414 |
415 | NanoStatus nanosockets_address_get_ip(const NanoAddress* address, char* ip, int ipLength) {
416 | if (address->ipv4.ffff == 0xFFFF && nanosockets_array_is_zeroed(address->ipv4.zeros, sizeof(address->ipv4.zeros)) == 0) {
417 | if (inet_ntop(AF_INET, &address->ipv4.ip, ip, ipLength) == NULL)
418 | return NANOSOCKETS_STATUS_ERROR;
419 | } else if (inet_ntop(AF_INET6, &address->ipv6, ip, ipLength) == NULL) {
420 | return NANOSOCKETS_STATUS_ERROR;
421 | }
422 |
423 | return NANOSOCKETS_STATUS_OK;
424 | }
425 |
426 | NanoStatus nanosockets_address_set_hostname(NanoAddress* address, const char* name) {
427 | struct addrinfo addressInfo = { 0 }, *result = NULL, *resultList = NULL;
428 |
429 | addressInfo.ai_family = AF_UNSPEC;
430 |
431 | if (getaddrinfo(name, NULL, &addressInfo, &resultList) != 0)
432 | return NANOSOCKETS_STATUS_ERROR;
433 |
434 | for (result = resultList; result != NULL; result = result->ai_next) {
435 | if (result->ai_addr != NULL && result->ai_addrlen >= sizeof(struct sockaddr_in)) {
436 | if (result->ai_family == AF_INET) {
437 | struct sockaddr_in* socketAddress = (struct sockaddr_in*)result->ai_addr;
438 |
439 | memset(address, 0, sizeof(address->ipv4.zeros));
440 |
441 | address->ipv4.ffff = 0xFFFF;
442 | address->ipv4.ip.s_addr = socketAddress->sin_addr.s_addr;
443 |
444 | freeaddrinfo(resultList);
445 |
446 | return NANOSOCKETS_STATUS_OK;
447 | } else if (result->ai_family == AF_INET6) {
448 | struct sockaddr_in6* socketAddress = (struct sockaddr_in6*)result->ai_addr;
449 |
450 | address->ipv6 = socketAddress->sin6_addr;
451 |
452 | freeaddrinfo(resultList);
453 |
454 | return NANOSOCKETS_STATUS_OK;
455 | }
456 | }
457 | }
458 |
459 | if (resultList != NULL)
460 | freeaddrinfo(resultList);
461 |
462 | return nanosockets_address_set_ip(address, name);
463 | }
464 |
465 | NanoStatus nanosockets_address_get_hostname(const NanoAddress* address, char* name, int nameLength) {
466 | struct sockaddr_in6 socketAddress = { 0 };
467 |
468 | socketAddress.sin6_family = AF_INET6;
469 | socketAddress.sin6_addr = address->ipv6;
470 | socketAddress.sin6_port = NANOSOCKETS_HOST_TO_NET_16(address->port);
471 |
472 | int error = getnameinfo((struct sockaddr*)&socketAddress, sizeof(socketAddress), name, nameLength, NULL, 0, NI_NAMEREQD);
473 |
474 | if (!error) {
475 | if (name != NULL && nameLength > 0 && !memchr(name, '\0', nameLength))
476 | return NANOSOCKETS_STATUS_ERROR;
477 |
478 | return NANOSOCKETS_STATUS_OK;
479 | }
480 |
481 | if (error != EAI_NONAME)
482 | return NANOSOCKETS_STATUS_ERROR;
483 |
484 | return nanosockets_address_get_ip(address, name, nameLength);
485 | }
486 |
487 | #endif // NANOSOCKETS_IMPLEMENTATION
488 |
489 | #endif // NANOSOCKETS_H
490 |
--------------------------------------------------------------------------------