Sockets refactor: allow any family/type/protocol association#3750
Sockets refactor: allow any family/type/protocol association#3750asterite merged 3 commits intocrystal-lang:masterfrom
Conversation
6e6923d to
acd148f
Compare
|
Also should close #3495, if bind is expanded to be interface/port instead of just port. |
|
Thanks, I couldn't find this issue anymore. |
src/socket.cr
Outdated
|
|
||
| property family : Family | ||
| property type : Type | ||
| property protocol : Protocol |
There was a problem hiding this comment.
Can these three be changed after creating a socket? If not, they should probably be getters.
There was a problem hiding this comment.
You're right. I'll change that.
src/socket.cr
Outdated
| # Force opened sockets to be closed on `exec(2)`. Only for platforms that don't | ||
| # support `SOCK_CLOEXEC` (e.g., Darwin). | ||
| protected def init_close_on_exec(fd : Int32) | ||
| {% unless LibC.constants.includes?("SOCK_CLOEXEC".id) %} |
There was a problem hiding this comment.
You can also use LibC.has_constant?("SOCK_CLOEXEC") (shorter plus probably a bit faster.) Not sure it works on the old compiler without .id, so it's probably better to leave the .id for now
src/socket.cr
Outdated
| # | ||
| # ``` | ||
| # sock = Socket.tcp(Socket::Family::INET6) | ||
| # sock.bind "localhost", 1234 |
There was a problem hiding this comment.
Should this doc comment just be sock.bind 1234?
src/socket/ip_socket.cr
Outdated
| # Returns the `IPAddress` for the local end of the IP socket. | ||
| def local_address | ||
| sockaddr = uninitialized LibC::SockaddrIn6 | ||
| sockaddr = Pointer(LibC::SockaddrIn6).malloc.as(LibC::Sockaddr*) |
There was a problem hiding this comment.
Why this was changed? It's an extra allocation (tiny, but still...)
There was a problem hiding this comment.
I ran into some issues. I hope I can change it back. Same below.
There was a problem hiding this comment.
No worries, we can take care of this later. It's just a tiny allocation and only used if you do request the local address :-)
src/socket/ip_socket.cr
Outdated
| def remote_address | ||
| sockaddr = uninitialized LibC::SockaddrIn6 | ||
| addrlen = LibC::SocklenT.new(sizeof(LibC::SockaddrIn6)) | ||
| sockaddr = Pointer(LibC::SockaddrIn6).malloc.as(LibC::Sockaddr*) |
|
Looks really good to me! It enables some new stuff, like #3495 and the code looks much nicer, readable and organized. I just made a few comments. |
src/socket/udp_socket.cr
Outdated
| def receive(max_message_size = 512) : {String, IPAddress} | ||
| bytes = Bytes.new(max_message_size) | ||
| bytes_read, sockaddr, addrlen = recvfrom(bytes) | ||
| {String.new(bytes.to_unsafe, bytes_read), IPAddress.from(sockaddr, addrlen)} |
There was a problem hiding this comment.
Here there's a double allocation: Bytes.new and String.new (it copies the bytes). You can do something like:
str = String.new(max_message_size) do |buffer|
bytes = Slice.new(buffer, max_message_size)
bytes_read, sockaddr, addrlen = recvfrom(bytes)
# etc.
{bytes_read, 0}
endThe only "problem" with this is that this will maybe allocate more bytes than needed, so maybe occupy more memory until the string is GC'ed. However, I'll soon change String#new to realloc the pointer if the resulting bytesize is less than the requested capacity. I even did a few benchmarks and this makes a program go much faster as later less memory needs to be reclaimed.
|
@asterite I applied all your suggestions, and skipped a few more extra allocations! I also added support for |
|
@ysbaddaden Oops, my bad! It seems |
Refactor: - Socket is now enough to create, configure and use any kind of socket association of family, type and protocol is also possible, as long as it's supported by the underlying OS implementation. - The TCPSocket, TCPServer, UDPSocket, UNIXSocket and UNIXServer classes are merely sugar to avoid having to deal with socket details. - UNIXSocket and UNIXServer can now be used in DGRAM type, in addition to the default STREAM type. Features: - Adds Socket::Server type, included by both TCPServer and UNIXServer. - Adds Addrinfo DNS resolver, that wraps results from `getaddrinfo`. Breaking Changes: - IPAddress now automatically detects the address family, so the argument was removed (limited impact).
b06c05d to
9b37ca3
Compare
|
Fixed! Let's wait for Travis :-) |
|
Almost! Just missing |
|
Ah, never mind. I'll make a release today so i'll merge this and then run the formatter. @ysbaddaden Thank you so much for this! ❤️ |
The whole idea behing this refactor is to give more possibilities over sockets, for some less common scenarios (eg: DGRAM UNIX sockets) as well as cleanup the sockets implementation, by avoiding some repetition.
Refactor:
connect,bind,listen,accept,sendandreceivemethods were moved from subclasses intoSocketitself;TCPSocket,TCPServer,UDPSocket,UNIXSocketandUNIXServerclasses are merely sugar to avoid having to deal with socket details;UNIXSocketandUNIXServercan now be used in DGRAM type, in addition to the default STREAM type.Features:
AddrinfoDNS resolver, that wraps results fromgetaddrinfo;Socket#receive : {String, IPAddress}to receive a String (like we cansend(String);Socket#bind(Int)to bind to all available interfaces;Socket::Servermodule, included by bothTCPServerandUNIXServer;SO_REUSEPORTsocket option; automatically set forTCPServer—see http://stackoverflow.com/a/14388707/199791Breaking Change:
IPAddressnow automatically detects the address family, so the argument was removed (limited impact).Examples:
closes #3214
closes #3495
refs #3586