Implement UDP socket support.

This commit is contained in:
Sönke Ludwig 2016-10-07 12:39:38 +02:00
parent 2a8c52f347
commit c4e985b73c
3 changed files with 186 additions and 18 deletions

View file

@ -25,7 +25,7 @@ Driver development status
Feature | SelectEventDriver | EpollEventDriver | IOCPEventDriver | KqueueEventDriver Feature | SelectEventDriver | EpollEventDriver | IOCPEventDriver | KqueueEventDriver
-----------------|-------------------|------------------|-----------------|------------------ -----------------|-------------------|------------------|-----------------|------------------
TCP Sockets | yes | yes | no | no TCP Sockets | yes | yes | no | no
UDP Sockets | no | no | no | no UDP Sockets | yes | yes | no | no
USDS | no | no | no | no USDS | no | no | no | no
DNS | no | no | no | no DNS | no | no | no | no
Timers | yes | yes | no | no Timers | yes | yes | no | no

View file

@ -83,11 +83,17 @@ interface EventDriverSockets {
ConnectionState getConnectionState(StreamSocketFD sock); ConnectionState getConnectionState(StreamSocketFD sock);
void setTCPNoDelay(StreamSocketFD socket, bool enable); void setTCPNoDelay(StreamSocketFD socket, bool enable);
void read(StreamSocketFD socket, ubyte[] buffer, IOMode mode, IOCallback on_read_finish); void read(StreamSocketFD socket, ubyte[] buffer, IOMode mode, IOCallback on_read_finish);
void cancelRead(StreamSocketFD socket);
void write(StreamSocketFD socket, const(ubyte)[] buffer, IOMode mode, IOCallback on_write_finish); void write(StreamSocketFD socket, const(ubyte)[] buffer, IOMode mode, IOCallback on_write_finish);
void cancelWrite(StreamSocketFD socket);
void waitForData(StreamSocketFD socket, IOCallback on_data_available); void waitForData(StreamSocketFD socket, IOCallback on_data_available);
void shutdown(StreamSocketFD socket, bool shut_read = true, bool shut_write = true); void shutdown(StreamSocketFD socket, bool shut_read = true, bool shut_write = true);
void cancelRead(StreamSocketFD socket);
void cancelWrite(StreamSocketFD socket); DatagramSocketFD createDatagramSocket(scope Address bind_address, scope Address target_address);
void receive(DatagramSocketFD socket, ubyte[] buffer, IOMode mode, DatagramIOCallback on_receive_finish);
void cancelReceive(DatagramSocketFD socket);
void send(DatagramSocketFD socket, const(ubyte)[] buffer, IOMode mode, DatagramIOCallback on_send_finish, Address target_address = null);
void cancelSend(DatagramSocketFD socket);
/** Increments the reference count of the given resource. /** Increments the reference count of the given resource.
*/ */
@ -195,6 +201,7 @@ interface EventDriverWatchers {
alias ConnectCallback = void delegate(StreamSocketFD, ConnectStatus); alias ConnectCallback = void delegate(StreamSocketFD, ConnectStatus);
alias AcceptCallback = void delegate(StreamListenSocketFD, StreamSocketFD); alias AcceptCallback = void delegate(StreamListenSocketFD, StreamSocketFD);
alias IOCallback = void delegate(StreamSocketFD, IOStatus, size_t); alias IOCallback = void delegate(StreamSocketFD, IOStatus, size_t);
alias DatagramIOCallback = void delegate(DatagramSocketFD, IOStatus, size_t, scope Address);
alias FileIOCallback = void delegate(FileFD, IOStatus, size_t); alias FileIOCallback = void delegate(FileFD, IOStatus, size_t);
alias EventCallback = void delegate(EventID); alias EventCallback = void delegate(EventID);
alias SignalCallback = void delegate(int); alias SignalCallback = void delegate(int);
@ -302,6 +309,7 @@ alias FD = Handle!("FD", int, -1);
alias SocketFD = Handle!("Socket", FD); alias SocketFD = Handle!("Socket", FD);
alias StreamSocketFD = Handle!("Stream", SocketFD); alias StreamSocketFD = Handle!("Stream", SocketFD);
alias StreamListenSocketFD = Handle!("StreamListen", SocketFD); alias StreamListenSocketFD = Handle!("StreamListen", SocketFD);
alias DatagramSocketFD = Handle!("Datagram", SocketFD);
alias FileFD = Handle!("File", FD); alias FileFD = Handle!("File", FD);
alias EventID = Handle!("Event", FD); alias EventID = Handle!("Event", FD);
alias TimerID = Handle!("Timer", int); alias TimerID = Handle!("Timer", int);

View file

@ -135,7 +135,7 @@ abstract class PosixEventDriver : EventDriver,
final override StreamSocketFD connectStream(scope Address address, ConnectCallback on_connect) final override StreamSocketFD connectStream(scope Address address, ConnectCallback on_connect)
{ {
auto sock = cast(StreamSocketFD)createSocket(address.addressFamily); auto sock = cast(StreamSocketFD)createSocket(address.addressFamily, SOCK_STREAM);
if (sock == -1) return StreamSocketFD.invalid; if (sock == -1) return StreamSocketFD.invalid;
void invalidateSocket() @nogc @trusted nothrow { closeSocket(sock); sock = StreamSocketFD.invalid; } void invalidateSocket() @nogc @trusted nothrow { closeSocket(sock); sock = StreamSocketFD.invalid; }
@ -187,7 +187,7 @@ abstract class PosixEventDriver : EventDriver,
final override StreamListenSocketFD listenStream(scope Address address, AcceptCallback on_accept) final override StreamListenSocketFD listenStream(scope Address address, AcceptCallback on_accept)
{ {
log("Listen stream"); log("Listen stream");
auto sock = cast(StreamListenSocketFD)createSocket(address.addressFamily); auto sock = cast(StreamListenSocketFD)createSocket(address.addressFamily, SOCK_STREAM);
void invalidateSocket() @nogc @trusted nothrow { closeSocket(sock); sock = StreamSocketFD.invalid; } void invalidateSocket() @nogc @trusted nothrow { closeSocket(sock); sock = StreamSocketFD.invalid; }
@ -259,7 +259,7 @@ abstract class PosixEventDriver : EventDriver,
} }
sizediff_t ret; sizediff_t ret;
() @trusted { ret = recv(socket, buffer.ptr, buffer.length, 0); } (); () @trusted { ret = .recv(socket, buffer.ptr, buffer.length, 0); } ();
if (ret < 0) { if (ret < 0) {
auto err = getSocketError(); auto err = getSocketError();
@ -323,7 +323,7 @@ abstract class PosixEventDriver : EventDriver,
} }
sizediff_t ret; sizediff_t ret;
() @trusted { ret = recv(socket, slot.readBuffer.ptr, slot.readBuffer.length, 0); } (); () @trusted { ret = .recv(socket, slot.readBuffer.ptr, slot.readBuffer.length, 0); } ();
if (ret < 0) { if (ret < 0) {
auto err = getSocketError(); auto err = getSocketError();
if (err != EAGAIN) { if (err != EAGAIN) {
@ -355,7 +355,7 @@ abstract class PosixEventDriver : EventDriver,
} }
sizediff_t ret; sizediff_t ret;
() @trusted { ret = send(socket, buffer.ptr, buffer.length, 0); } (); () @trusted { ret = .send(socket, buffer.ptr, buffer.length, 0); } ();
if (ret < 0) { if (ret < 0) {
auto err = getSocketError(); auto err = getSocketError();
@ -398,11 +398,9 @@ abstract class PosixEventDriver : EventDriver,
override void cancelWrite(StreamSocketFD socket) override void cancelWrite(StreamSocketFD socket)
{ {
assert(m_fds[socket].readCallback !is null, "Cancelling write when there is no read in progress."); assert(m_fds[socket].writeCallback !is null, "Cancelling write when there is no write in progress.");
setNotifyCallback!(EventType.write)(socket, null); setNotifyCallback!(EventType.write)(socket, null);
with (m_fds[socket]) { m_fds[socket].writeBuffer = null;
writeBuffer = null;
}
} }
private void onSocketWrite(FD fd) private void onSocketWrite(FD fd)
@ -411,13 +409,13 @@ abstract class PosixEventDriver : EventDriver,
auto socket = cast(StreamSocketFD)fd; auto socket = cast(StreamSocketFD)fd;
sizediff_t ret; sizediff_t ret;
() @trusted { ret = send(socket, slot.writeBuffer.ptr, slot.writeBuffer.length, 0); } (); () @trusted { ret = .send(socket, slot.writeBuffer.ptr, slot.writeBuffer.length, 0); } ();
if (ret < 0) { if (ret < 0) {
auto err = getSocketError(); auto err = getSocketError();
if (err != EAGAIN) { if (err != EAGAIN) {
setNotifyCallback!(EventType.write)(socket, null); setNotifyCallback!(EventType.write)(socket, null);
slot.readCallback(socket, IOStatus.error, slot.bytesRead); slot.writeCallback(socket, IOStatus.error, slot.bytesRead);
return; return;
} }
} }
@ -501,6 +499,167 @@ abstract class PosixEventDriver : EventDriver,
// TODO! // TODO!
} }
DatagramSocketFD createDatagramSocket(scope Address bind_address, scope Address target_address)
{
auto sock = cast(DatagramSocketFD)createSocket(bind_address.addressFamily, SOCK_DGRAM);
if (sock == -1) return DatagramSocketFD.invalid;
if (() @trusted { return bind(sock, bind_address.name, bind_address.nameLen); } () != 0) {
closeSocket(sock);
return DatagramSocketFD.init;
}
if (target_address && () @trusted { return connect(sock, target_address.name, target_address.nameLen); } () != 0) {
closeSocket(sock);
return DatagramSocketFD.init;
}
registerFD(sock, EventMask.read|EventMask.write|EventMask.status);
initFD(sock);
return sock;
}
void receive(DatagramSocketFD socket, ubyte[] buffer, IOMode mode, DatagramIOCallback on_receive_finish)
{
assert(mode != IOMode.all, "Only IOMode.immediate and IOMode.once allowed for datagram sockets.");
sizediff_t ret;
scope src_addr = new UnknownAddress;
socklen_t src_addr_len = src_addr.nameLen;
() @trusted { ret = .recvfrom(socket, buffer.ptr, buffer.length, 0, src_addr.name, &src_addr_len); } ();
if (ret < 0) {
auto err = getSocketError();
if (err != EAGAIN) {
print("sock error %s!", err);
on_receive_finish(socket, IOStatus.error, 0, null);
return;
}
}
if (ret < 0) {
if (mode == IOMode.immediate) {
on_receive_finish(socket, IOStatus.wouldBlock, 0, null);
} else {
with (m_fds[socket]) {
readCallback = () @trusted { return cast(IOCallback)on_receive_finish; } ();
readMode = mode;
bytesRead = 0;
readBuffer = buffer;
}
setNotifyCallback!(EventType.read)(socket, &onDgramRead);
}
return;
}
on_receive_finish(socket, IOStatus.ok, ret, src_addr);
}
void cancelReceive(DatagramSocketFD socket)
{
assert(m_fds[socket].readCallback !is null, "Cancelling read when there is no read in progress.");
setNotifyCallback!(EventType.read)(socket, null);
m_fds[socket].readBuffer = null;
}
private void onDgramRead(FD fd)
{
auto slot = &m_fds[fd];
auto socket = cast(DatagramSocketFD)fd;
sizediff_t ret;
scope src_addr = new UnknownAddress;
socklen_t src_addr_len = src_addr.nameLen;
() @trusted { ret = .recvfrom(socket, slot.readBuffer.ptr, slot.readBuffer.length, 0, src_addr.name, &src_addr_len); } ();
if (ret < 0) {
auto err = getSocketError();
if (err != EAGAIN) {
setNotifyCallback!(EventType.read)(socket, null);
() @trusted { return cast(DatagramIOCallback)slot.readCallback; } ()(socket, IOStatus.error, 0, null);
return;
}
}
setNotifyCallback!(EventType.read)(socket, null);
() @trusted { return cast(DatagramIOCallback)slot.readCallback; } ()(socket, IOStatus.ok, ret, src_addr);
}
void send(DatagramSocketFD socket, const(ubyte)[] buffer, IOMode mode, DatagramIOCallback on_send_finish, Address target_address = null)
{
assert(mode != IOMode.all, "Only IOMode.immediate and IOMode.once allowed for datagram sockets.");
sizediff_t ret;
if (target_address) {
() @trusted { ret = .sendto(socket, buffer.ptr, buffer.length, 0, target_address.name, target_address.nameLen); } ();
m_fds[socket].targetAddr = target_address;
} else {
() @trusted { ret = .send(socket, buffer.ptr, buffer.length, 0); } ();
}
if (ret < 0) {
auto err = getSocketError();
if (err != EAGAIN) {
print("sock error %s!", err);
on_send_finish(socket, IOStatus.error, 0, null);
return;
}
}
if (ret < 0) {
if (mode == IOMode.immediate) {
on_send_finish(socket, IOStatus.wouldBlock, 0, null);
} else {
with (m_fds[socket]) {
writeCallback = () @trusted { return cast(IOCallback)on_send_finish; } ();
writeMode = mode;
bytesWritten = 0;
writeBuffer = buffer;
}
setNotifyCallback!(EventType.write)(socket, &onDgramWrite);
}
return;
}
on_send_finish(socket, IOStatus.ok, ret, null);
}
void cancelSend(DatagramSocketFD socket)
{
assert(m_fds[socket].writeCallback !is null, "Cancelling write when there is no write in progress.");
setNotifyCallback!(EventType.write)(socket, null);
m_fds[socket].writeBuffer = null;
}
private void onDgramWrite(FD fd)
{
auto slot = &m_fds[fd];
auto socket = cast(DatagramSocketFD)fd;
sizediff_t ret;
if (slot.targetAddr) {
() @trusted { ret = .sendto(socket, slot.writeBuffer.ptr, slot.writeBuffer.length, 0, slot.targetAddr.name, slot.targetAddr.nameLen); } ();
} else {
() @trusted { ret = .send(socket, slot.writeBuffer.ptr, slot.writeBuffer.length, 0); } ();
}
if (ret < 0) {
auto err = getSocketError();
if (err != EAGAIN) {
setNotifyCallback!(EventType.write)(socket, null);
() @trusted { return cast(DatagramIOCallback)slot.writeCallback; } ()(socket, IOStatus.error, 0, null);
return;
}
}
setNotifyCallback!(EventType.write)(socket, null);
() @trusted { return cast(DatagramIOCallback)slot.writeCallback; } ()(socket, IOStatus.ok, ret, null);
}
final override void addRef(SocketFD fd) final override void addRef(SocketFD fd)
{ {
auto pfd = &m_fds[fd]; auto pfd = &m_fds[fd];
@ -695,10 +854,10 @@ abstract class PosixEventDriver : EventDriver,
m_fds[fd].callback[evt] = callback; m_fds[fd].callback[evt] = callback;
} }
private SocketFD createSocket(AddressFamily family) private SocketFD createSocket(AddressFamily family, int type)
{ {
int sock; int sock;
() @trusted { sock = socket(family, SOCK_STREAM, 0); } (); () @trusted { sock = socket(family, type, 0); } ();
if (sock == -1) return SocketFD.invalid; if (sock == -1) return SocketFD.invalid;
setSocketNonBlocking(cast(SocketFD)sock); setSocketNonBlocking(cast(SocketFD)sock);
return cast(SocketFD)sock; return cast(SocketFD)sock;
@ -733,12 +892,13 @@ private struct FDSlot {
size_t bytesRead; size_t bytesRead;
ubyte[] readBuffer; ubyte[] readBuffer;
IOMode readMode; IOMode readMode;
IOCallback readCallback; IOCallback readCallback; // FIXME: this type only works for stream sockets
size_t bytesWritten; size_t bytesWritten;
const(ubyte)[] writeBuffer; const(ubyte)[] writeBuffer;
IOMode writeMode; IOMode writeMode;
IOCallback writeCallback; IOCallback writeCallback; // FIXME: this type only works for stream sockets
Address targetAddr;
ConnectCallback connectCallback; ConnectCallback connectCallback;
AcceptCallback acceptCallback; AcceptCallback acceptCallback;