www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - std.socket is horrible.

reply Daniel Gibson <metalcaedes gmail.com> writes:
Hi,

I've been experimenting with passing a socket (descriptor) from one
process to another (on Linux, should be the same on most POSIX systems).
This may be done with ancillary messages (on local AF_UNIX sockets),
which needs sendmsg() and recvmsg() and related structs and
macros/functions, that are currently not available in std.c.*
That is not such a big deal though, I just implemented it in C and
linked it, really painless (if you're interested in the code:
http://pastebin.com/8h1GaAQ7 ).

However, it is not painless at all to create a std.socket.Socket
(compatible) object with an existing socket_t (like the int returned by
recv_fd).
It could be really easy: The Socket class only needs a socket_t and an
AddressFamily object - but they're private and all constructors create
the socket_t themselves - there is no public (or at least protected)
constructor to set it.
And that's where the pain starts: To make it work I derived a class from
std.socket.Socket - and, because its socket_t (sock) and AddressFamily
(_family) are private, I had to copy the whole implementation, just to
add this friggin' constructor.
To add insult to injury, this still doesn't work: apart from minor stuff
that's defined as private on module-level (like private const int
_SOCKET_ERROR and _lasterr()) the most important methods of Address -
the class that wraps struct sockaddr - which are name() (returning a
pointer to the sockaddr) and nameLen() (returning its length) are
protected - WTF?!
This makes this whole class Address useless outside of std.socket. And
it forced me to write a wrapper-class, that provides public getters for
the sockaddr and its length.. not trivial, if the wrapper-class (even
though it's derived from Address) can't access said protected methods..
I had to check (e.g. via "is(addr : InternetAddress)") what specific
kind of Address that is and use the getters (in that case port() and
addr()) of the derived class to build a *new* sockaddr (or sockaddr_in
in that case).. This of course breaks if any new classes derived from
Address appear - the wrapper would have to be updated or it will be
pretty useless (returning null for name()).
And then of course I had to adjust all those methods in Socket
expect/use an Address (bind(), remoteAddress(), sendTo() etc) to wrap
the given Address in my Wrapper-Class and to use my public getters.. -
All this mess just because someone had to protect his class members too
much...

While I'm at it: Support for local sockets (AF_UNIX) could be better -
it'd be nice if struct sockaddr_un was defined somewhere and if there
was a specific class derived from Address for that (like InternetAddress
for internet-addresses). I wrote code for that, feel free to use it:
http://pastebin.com/CLgphG6Q

To make local sockets really painless the function "int socketpair(int
domain, int type, int protocol, int sv[2])" (like defined in
sys/socket.h) or something like that would be nice.
Maybe some static function returning those two sockets as actual
std.socket.Socket objects would be helpful - or again one needs a Socket
constructor that accepts a socket_t...

I've done this with D1/phobos1, but at least the stuff in std.socket
seems the same in phobos2 (I've checked the trunk of the phobos project
at dsource).


Cheers,
- Daniel

PS: If you wanna see or even use the whole mess (all that
aforementioned D code I wrote including the Socket class):
http://pastebin.com/Ldgq8DtV

PPS: Despite my rage about the sockets situation (and more rants may
follow, for example about some things in std.stream) I really enjoy
using D, thanks :-)
Aug 22 2010
next sibling parent "Pedro Rodrigues" <pdfrodrigues gmail.com> writes:
I've run into similar problems and also ended up having to re-implement a 
great part of the sockets module. A fix for these issues would be much 
welcomed.

Regards 
Aug 22 2010
prev sibling next sibling parent reply cemiller <chris dprogramming.com> writes:
Would the problems be resolved with the following changes?


Add a Socket class constructor:
this(socket_t sock, AddressFamily af)
{
	assert(sock != socket_t.init);
	this.sock = sock;
	this._family = af;
}


Socket.newFamilyObject():
This is currently private but can be made protected to allow overriding to  
return new address types.


Address.name() and Address.nameLen():
Is there a function you need to call with these? I suppose they can be  
made public just in case they're needed elsewhere.


As for things like _SOCKET_ERROR and _lasterr(), they're just simple  
shortcuts to the low-level sockets. If you're working with the low-level  
sockets, then you have access to what _SOCKET_ERROR and _lasterr() refer  
to; otherwise, you don't need them.
Aug 22 2010
parent "Pedro Rodrigues" <pdfrodrigues gmail.com> writes:
Yes, that would most certainly help a lot.

"cemiller" <chris dprogramming.com> wrote in message 
news:op.vhu594gqycwdcp christop...
 Would the problems be resolved with the following changes?


 Add a Socket class constructor:
 this(socket_t sock, AddressFamily af)
 {
 assert(sock != socket_t.init);
 this.sock = sock;
 this._family = af;
 }


 Socket.newFamilyObject():
 This is currently private but can be made protected to allow overriding to 
 return new address types.


 Address.name() and Address.nameLen():
 Is there a function you need to call with these? I suppose they can be 
 made public just in case they're needed elsewhere.


 As for things like _SOCKET_ERROR and _lasterr(), they're just simple 
 shortcuts to the low-level sockets. If you're working with the low-level 
 sockets, then you have access to what _SOCKET_ERROR and _lasterr() refer 
 to; otherwise, you don't need them. 

Aug 23 2010
prev sibling parent Daniel Gibson <metalcaedes gmail.com> writes:
cemiller schrieb:
 Would the problems be resolved with the following changes?
 
 
 Add a Socket class constructor:
 this(socket_t sock, AddressFamily af)
 {
     assert(sock != socket_t.init);
     this.sock = sock;
     this._family = af;
 }
 

Yes, this constructor would be really helpful.
 Socket.newFamilyObject():
 This is currently private but can be made protected to allow overriding 
 to return new address types.

I stumbled upon this after sending my initial mail - yes, this should definitely be protected instead of private.
 
 Address.name() and Address.nameLen():
 Is there a function you need to call with these? I suppose they can be 
 made public just in case they're needed elsewhere.
 

The obvious case (I came across myself) is that one might want to derive Socket and override methods like bind. Another thing is that you could just use the relatively high-level/painless Address-classes (like InternetAddress) to get such an Object and still pass the contained sockaddr to low-level functions that need it. IMHO they should just be made public, I guess it won't do any harm ;)
 
 As for things like _SOCKET_ERROR and _lasterr(), they're just simple 
 shortcuts to the low-level sockets. If you're working with the low-level 
 sockets, then you have access to what _SOCKET_ERROR and _lasterr() refer 
 to; otherwise, you don't need them.

I know, that's why I called it "minor stuff" - it really doesn't have to be exposed outside of std.socket. It was mainly the fact that I couldn't set the socket_t of Socket and couldn't access Address.name() and Address.nameLen() outside of std.socket that annoyed me. Considering that (if I'm not mistaken) you're the original author of that Code: Please don't take my rant personally, I guess you just didn't consider this weird stuff I'm doing when you were writing it - after all the Socket class is meant to be a higher level abstraction of sockets so it seems natural not to expose the internal socket handle. Apart from these small issues (that can easily be fixed by the maintainer) the I find general interface of std.socket quite usable :-) Cheers, - Daniel
Aug 22 2010