Use sockets with new IP Address structs
This commit is contained in:
parent
be8fcb3e1c
commit
c15a8993ec
@ -35,9 +35,6 @@ type information at compile-time, to transform from one type to another. It has
|
||||
also different algorithms for iterating, searching and modifying template
|
||||
arguments.
|
||||
* `net`: URL-Parsing, network programming.
|
||||
* `network`: Socket implementation. `network` is currently under rework.
|
||||
After finishing the new socket implementation will land in the `net` package and
|
||||
`network` will be deprecated.
|
||||
* `os`: Platform-independent interfaces to operating system functionality.
|
||||
* `range`: Generic functions and templates for D ranges.
|
||||
* `test`: Test suite for unittest-blocks.
|
||||
@ -167,7 +164,7 @@ parameter is used)
|
||||
|
||||
| DMD | GCC |
|
||||
|:-------:|:---------:|
|
||||
| 2.091.1 | gdc trunk |
|
||||
| 2.096.0 | 10.3 |
|
||||
|
||||
## Further characteristics
|
||||
|
||||
|
@ -31,7 +31,7 @@ import tanya.async.transport;
|
||||
import tanya.async.watcher;
|
||||
import tanya.container.array;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.network.socket;
|
||||
import tanya.net.socket;
|
||||
|
||||
extern (C) nothrow @nogc
|
||||
{
|
||||
|
@ -26,7 +26,7 @@ import tanya.async.watcher;
|
||||
import tanya.container.array;
|
||||
import tanya.container.buffer;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.network.socket;
|
||||
import tanya.net.socket;
|
||||
|
||||
/**
|
||||
* Transport for stream sockets.
|
||||
|
@ -34,8 +34,9 @@
|
||||
*
|
||||
* void main()
|
||||
* {
|
||||
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1", cast(ushort) 8192);
|
||||
*
|
||||
* auto address = address4("127.0.0.1");
|
||||
* auto endpoint = Endpoint(address.get, cast(ushort) 8192);
|
||||
*
|
||||
* version (Windows)
|
||||
* {
|
||||
* auto sock = defaultAllocator.make!OverlappedStreamSocket(AddressFamily.inet);
|
||||
@ -46,19 +47,18 @@
|
||||
* sock.blocking = false;
|
||||
* }
|
||||
*
|
||||
* sock.bind(address);
|
||||
* sock.bind(endpoint);
|
||||
* sock.listen(5);
|
||||
*
|
||||
*
|
||||
* auto io = defaultAllocator.make!ConnectionWatcher(sock);
|
||||
* io.setProtocol!EchoProtocol;
|
||||
*
|
||||
*
|
||||
* defaultLoop.start(io);
|
||||
* defaultLoop.run();
|
||||
*
|
||||
*
|
||||
* sock.shutdown();
|
||||
* defaultAllocator.dispose(io);
|
||||
* defaultAllocator.dispose(sock);
|
||||
* defaultAllocator.dispose(address);
|
||||
* }
|
||||
* ---
|
||||
*
|
||||
@ -78,7 +78,7 @@ import tanya.bitmanip;
|
||||
import tanya.container.buffer;
|
||||
import tanya.container.list;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.network.socket;
|
||||
import tanya.net.socket;
|
||||
|
||||
version (DisableBackends)
|
||||
{
|
||||
|
@ -19,7 +19,7 @@
|
||||
module tanya.async.protocol;
|
||||
|
||||
import tanya.async.transport;
|
||||
import tanya.network.socket;
|
||||
import tanya.net.socket;
|
||||
|
||||
/**
|
||||
* Common protocol interface.
|
||||
|
@ -16,7 +16,7 @@
|
||||
module tanya.async.transport;
|
||||
|
||||
import tanya.async.protocol;
|
||||
import tanya.network.socket;
|
||||
import tanya.net.socket;
|
||||
|
||||
/**
|
||||
* Base transport interface.
|
||||
|
@ -20,7 +20,7 @@ import tanya.async.transport;
|
||||
import tanya.container.buffer;
|
||||
import tanya.container.list;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.network.socket;
|
||||
import tanya.net.socket;
|
||||
|
||||
/**
|
||||
* A watcher is an opaque structure that you allocate and register to record
|
||||
|
@ -142,3 +142,23 @@ String indexToName(uint index) @nogc nothrow @trusted
|
||||
return String(findNullTerminated(buffer));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL AddressFamily) specifies a communication domain; this selects
|
||||
* the protocol family which will be used for communication.
|
||||
*/
|
||||
enum AddressFamily : int
|
||||
{
|
||||
unspec = 0, /// Unspecified.
|
||||
local = 1, /// Local to host (pipes and file-domain).
|
||||
unix = local, /// POSIX name for PF_LOCAL.
|
||||
inet = 2, /// IP protocol family.
|
||||
ax25 = 3, /// Amateur Radio AX.25.
|
||||
ipx = 4, /// Novell Internet Protocol.
|
||||
appletalk = 5, /// Appletalk DDP.
|
||||
netrom = 6, /// Amateur radio NetROM.
|
||||
bridge = 7, /// Multiprotocol bridge.
|
||||
atmpvc = 8, /// ATM PVCs.
|
||||
x25 = 9, /// Reserved for X.25 project.
|
||||
inet6 = 10, /// IP version 6.
|
||||
}
|
||||
|
@ -14,7 +14,6 @@
|
||||
*/
|
||||
module tanya.net.ip;
|
||||
|
||||
import core.sys.posix.sys.socket;
|
||||
import std.algorithm.comparison;
|
||||
import std.ascii;
|
||||
import std.typecons;
|
||||
@ -1331,7 +1330,7 @@ struct Address
|
||||
*/
|
||||
struct Endpoint
|
||||
{
|
||||
private sa_family_t family = AF_UNSPEC;
|
||||
private AddressFamily family = AddressFamily.unspec;
|
||||
private ubyte[ushort.sizeof] service;
|
||||
private Address4 address4; // Unused sin6_flowinfo if IPv6
|
||||
private Address6 address6; // Unused if IPv4
|
||||
@ -1345,10 +1344,12 @@ struct Endpoint
|
||||
* Constructs an endpoint.
|
||||
*
|
||||
* Params:
|
||||
* T = Address type (IPv4 or IPv6).
|
||||
* address = IP address that should be associated with the endpoint.
|
||||
* port = Port number in network byte order.
|
||||
*/
|
||||
this(Address address, const ushort port = anyPort)
|
||||
this(T)(T address, const ushort port = anyPort)
|
||||
if (is(T == Address) || is(T == Address4) || is(T == Address6))
|
||||
{
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
@ -1357,7 +1358,7 @@ struct Endpoint
|
||||
/**
|
||||
* Returns: Port number in network byte order.
|
||||
*/
|
||||
@property ushort port() const @nogc nothrow pure @safe
|
||||
@property inout(ushort) port() inout const @nogc nothrow pure @safe
|
||||
{
|
||||
return this.service[].toHostOrder!ushort();
|
||||
}
|
||||
@ -1374,13 +1375,13 @@ struct Endpoint
|
||||
/**
|
||||
* Returns: IP address associated with the endpoint.
|
||||
*/
|
||||
@property Address address() @nogc nothrow pure @safe
|
||||
@property inout(Address) address() inout @nogc nothrow pure @safe
|
||||
{
|
||||
if (this.family == AF_INET)
|
||||
if (this.family == AddressFamily.inet)
|
||||
{
|
||||
return Address(this.address4);
|
||||
}
|
||||
else if (this.family == AF_INET6)
|
||||
else if (this.family == AddressFamily.inet6)
|
||||
{
|
||||
return Address(this.address6);
|
||||
}
|
||||
@ -1395,14 +1396,26 @@ struct Endpoint
|
||||
{
|
||||
if (address.isV4())
|
||||
{
|
||||
this.family = AF_INET;
|
||||
this.address4 = address.toV4();
|
||||
this.address = address.toV4();
|
||||
}
|
||||
else if (address.isV6())
|
||||
{
|
||||
this.family = AF_INET6;
|
||||
this.address4 = Address4(0);
|
||||
this.address6 = address.toV6();
|
||||
this.address = address.toV6();
|
||||
}
|
||||
}
|
||||
|
||||
/// ditto
|
||||
@property void address(Address4 address) @nogc nothrow pure @safe
|
||||
{
|
||||
this.family = AddressFamily.inet;
|
||||
this.address4 = address;
|
||||
}
|
||||
|
||||
/// ditto
|
||||
@property void address(Address6 address) @nogc nothrow pure @safe
|
||||
{
|
||||
this.family = AddressFamily.inet6;
|
||||
this.address4 = Address4(0);
|
||||
this.address6 = address;
|
||||
}
|
||||
}
|
||||
|
@ -17,4 +17,5 @@ module tanya.net;
|
||||
public import tanya.net.iface;
|
||||
public import tanya.net.inet;
|
||||
public import tanya.net.ip;
|
||||
public import tanya.net.socket;
|
||||
public import tanya.net.uri;
|
||||
|
@ -7,37 +7,6 @@
|
||||
*
|
||||
* Current API supports only server-side TCP communication.
|
||||
*
|
||||
* Here is an example of a cross-platform blocking server:
|
||||
*
|
||||
* ---
|
||||
* import std.stdio;
|
||||
* import tanya.memory;
|
||||
* import tanya.network;
|
||||
*
|
||||
* void main()
|
||||
* {
|
||||
* auto socket = defaultAllocator.make!StreamSocket(AddressFamily.inet);
|
||||
* auto address = defaultAllocator.make!InternetAddress("127.0.0.1",
|
||||
* cast(ushort) 8192);
|
||||
*
|
||||
* socket.setOption(SocketOptionLevel.SOCKET, SocketOption.REUSEADDR, true);
|
||||
* socket.blocking = true;
|
||||
* socket.bind(address);
|
||||
* socket.listen(5);
|
||||
*
|
||||
* auto client = socket.accept();
|
||||
* client.send(cast(const(ubyte)[]) "Test\n");
|
||||
*
|
||||
* ubyte[100] buf;
|
||||
* auto response = client.receive(buf[]);
|
||||
*
|
||||
* writeln(cast(const(char)[]) buf[0 .. response]);
|
||||
*
|
||||
* defaultAllocator.dispose(client);
|
||||
* defaultAllocator.dispose(socket);
|
||||
* }
|
||||
* ---
|
||||
*
|
||||
* For an example of an asynchronous server refer to the documentation of the
|
||||
* $(D_PSYMBOL tanya.async.loop) module.
|
||||
*
|
||||
@ -48,7 +17,7 @@
|
||||
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/network/socket.d,
|
||||
* tanya/network/socket.d)
|
||||
*/
|
||||
module tanya.network.socket;
|
||||
module tanya.net.socket;
|
||||
|
||||
import core.stdc.errno;
|
||||
import core.time;
|
||||
@ -57,6 +26,8 @@ import tanya.bitmanip;
|
||||
import tanya.memory.allocator;
|
||||
import tanya.meta.trait;
|
||||
import tanya.os.error;
|
||||
import tanya.net.iface;
|
||||
import tanya.net.ip;
|
||||
|
||||
/// Value returned by socket operations on error.
|
||||
enum int socketError = -1;
|
||||
@ -588,26 +559,6 @@ shared static this()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL AddressFamily) specifies a communication domain; this selects
|
||||
* the protocol family which will be used for communication.
|
||||
*/
|
||||
enum AddressFamily : int
|
||||
{
|
||||
unspec = 0, /// Unspecified.
|
||||
local = 1, /// Local to host (pipes and file-domain).
|
||||
unix = local, /// POSIX name for PF_LOCAL.
|
||||
inet = 2, /// IP protocol family.
|
||||
ax25 = 3, /// Amateur Radio AX.25.
|
||||
ipx = 4, /// Novell Internet Protocol.
|
||||
appletalk = 5, /// Appletalk DDP.
|
||||
netrom = 6, /// Amateur radio NetROM.
|
||||
bridge = 7, /// Multiprotocol bridge.
|
||||
atmpvc = 8, /// ATM PVCs.
|
||||
x25 = 9, /// Reserved for X.25 project.
|
||||
inet6 = 10, /// IP version 6.
|
||||
}
|
||||
|
||||
/**
|
||||
* $(D_PSYMBOL SocketException) should be thrown only if one of the socket functions
|
||||
* $(D_PSYMBOL socketError) and sets $(D_PSYMBOL errno), because
|
||||
@ -1064,13 +1015,13 @@ class StreamSocket : Socket, ConnectionOrientedSocket
|
||||
* Associate a local address with this socket.
|
||||
*
|
||||
* Params:
|
||||
* address = Local address.
|
||||
* endpoint = Local address.
|
||||
*
|
||||
* Throws: $(D_PSYMBOL SocketException) if unable to bind.
|
||||
*/
|
||||
void bind(Address address) const @trusted @nogc
|
||||
void bind(const Endpoint endpoint) const @nogc
|
||||
{
|
||||
if (.bind(handle_, address.name, address.length) == socketError)
|
||||
if (.bind(handle_, cast(const(sockaddr)*) &endpoint, Endpoint.sizeof))
|
||||
{
|
||||
throw defaultAllocator.make!SocketException("Unable to bind socket");
|
||||
}
|
||||
@ -1268,157 +1219,6 @@ class ConnectedSocket : Socket, ConnectionOrientedSocket
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Socket address representation.
|
||||
*/
|
||||
abstract class Address
|
||||
{
|
||||
/**
|
||||
* Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure.
|
||||
*/
|
||||
abstract @property inout(sockaddr)* name() inout pure nothrow @nogc;
|
||||
|
||||
/**
|
||||
* Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure.
|
||||
*/
|
||||
abstract @property inout(socklen_t) length() inout const pure nothrow @nogc;
|
||||
}
|
||||
|
||||
class InternetAddress : Address
|
||||
{
|
||||
version (Windows)
|
||||
{
|
||||
/// Internal internet address representation.
|
||||
protected SOCKADDR_STORAGE storage;
|
||||
}
|
||||
else version (Posix)
|
||||
{
|
||||
/// Internal internet address representation.
|
||||
protected sockaddr_storage storage;
|
||||
}
|
||||
const ushort port_;
|
||||
|
||||
enum ushort anyPort = 0;
|
||||
|
||||
this(string host, const ushort port = anyPort) @nogc
|
||||
{
|
||||
if (getaddrinfoPointer is null || freeaddrinfoPointer is null)
|
||||
{
|
||||
throw make!SocketException(defaultAllocator,
|
||||
"Address info lookup is not available on this system");
|
||||
}
|
||||
addrinfo* ai_res;
|
||||
this.port_ = port;
|
||||
|
||||
// Make C-string from host.
|
||||
auto node = cast(char[]) allocator.allocate(host.length + 1);
|
||||
node[0 .. $ - 1] = host;
|
||||
node[$ - 1] = '\0';
|
||||
scope (exit)
|
||||
{
|
||||
allocator.deallocate(node);
|
||||
}
|
||||
|
||||
// Convert port to a C-string.
|
||||
char[6] service = [0, 0, 0, 0, 0, 0];
|
||||
const(char)* servicePointer;
|
||||
if (port)
|
||||
{
|
||||
ushort originalPort = port;
|
||||
ushort start;
|
||||
for (ushort j = 10, i = 4; i > 0; j *= 10, --i)
|
||||
{
|
||||
ushort rest = originalPort % 10;
|
||||
if (rest != 0)
|
||||
{
|
||||
service[i] = cast(char) (rest + '0');
|
||||
start = i;
|
||||
}
|
||||
originalPort /= 10;
|
||||
}
|
||||
servicePointer = service[start .. $].ptr;
|
||||
}
|
||||
|
||||
auto ret = getaddrinfoPointer(node.ptr, servicePointer, null, &ai_res);
|
||||
if (ret)
|
||||
{
|
||||
throw defaultAllocator.make!SocketException("Address info lookup failed");
|
||||
}
|
||||
scope (exit)
|
||||
{
|
||||
freeaddrinfoPointer(ai_res);
|
||||
}
|
||||
|
||||
ubyte* dp = cast(ubyte*) &storage, sp = cast(ubyte*) ai_res.ai_addr;
|
||||
for (auto i = ai_res.ai_addrlen; i > 0; --i, *dp++, *sp++)
|
||||
{
|
||||
*dp = *sp;
|
||||
}
|
||||
if (ai_res.ai_family != AddressFamily.inet && ai_res.ai_family != AddressFamily.inet6)
|
||||
{
|
||||
throw defaultAllocator.make!SocketException("Wrong address family");
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
{
|
||||
auto address = defaultAllocator.make!InternetAddress("127.0.0.1");
|
||||
assert(address.port == InternetAddress.anyPort);
|
||||
assert(address.name !is null);
|
||||
assert(address.family == AddressFamily.inet);
|
||||
|
||||
defaultAllocator.dispose(address);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Pointer to underlying $(D_PSYMBOL sockaddr) structure.
|
||||
*/
|
||||
override @property inout(sockaddr)* name() inout pure nothrow @nogc
|
||||
{
|
||||
return cast(sockaddr*) &storage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Actual size of underlying $(D_PSYMBOL sockaddr) structure.
|
||||
*/
|
||||
override @property inout(socklen_t) length() inout const pure nothrow @nogc
|
||||
{
|
||||
// FreeBSD wants to know the exact length of the address on bind.
|
||||
switch (family)
|
||||
{
|
||||
case AddressFamily.inet:
|
||||
return sockaddr_in.sizeof;
|
||||
case AddressFamily.inet6:
|
||||
return sockaddr_in6.sizeof;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns: Family of this address.
|
||||
*/
|
||||
@property inout(AddressFamily) family() inout const pure nothrow @nogc
|
||||
{
|
||||
return cast(AddressFamily) storage.ss_family;
|
||||
}
|
||||
|
||||
@property inout(ushort) port() inout const pure nothrow @nogc
|
||||
{
|
||||
return port_;
|
||||
}
|
||||
|
||||
///
|
||||
unittest
|
||||
{
|
||||
auto address = defaultAllocator.make!InternetAddress("127.0.0.1",
|
||||
cast(ushort) 1234);
|
||||
assert(address.port == 1234);
|
||||
defaultAllocator.dispose(address);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the last error is a serious error or just a special
|
||||
* behaviour error of non-blocking sockets (for example an error
|
@ -1,17 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
/**
|
||||
* Network programming.
|
||||
*
|
||||
* Copyright: Eugene Wissner 2016-2020.
|
||||
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
||||
* Mozilla Public License, v. 2.0).
|
||||
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
||||
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/network/package.d,
|
||||
* tanya/network/package.d)
|
||||
*/
|
||||
module tanya.network;
|
||||
|
||||
public import tanya.network.socket;
|
Loading…
Reference in New Issue
Block a user