-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sockets refactor: allow any family/type/protocol association #3750
Sockets refactor: allow any family/type/protocol association #3750
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. |
|
||
property family : Family | ||
property type : Type | ||
property protocol : Protocol |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can these three be changed after creating a socket? If not, they should probably be getters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. I'll change that.
# 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
# | ||
# ``` | ||
# sock = Socket.tcp(Socket::Family::INET6) | ||
# sock.bind "localhost", 1234 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this doc comment just be sock.bind 1234
?
@@ -1,139 +1,25 @@ | |||
class IPSocket < Socket | |||
# 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this was changed? It's an extra allocation (tiny, but still...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ran into some issues. I hope I can change it back. Same below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 :-)
end | ||
|
||
# Returns the `IPAddress` for the remote end of the IP socket. | ||
def remote_address | ||
sockaddr = uninitialized LibC::SockaddrIn6 | ||
addrlen = LibC::SocklenT.new(sizeof(LibC::SockaddrIn6)) | ||
sockaddr = Pointer(LibC::SockaddrIn6).malloc.as(LibC::Sockaddr*) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here, why the change?
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. |
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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}
end
The 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
,send
andreceive
methods were moved from subclasses intoSocket
itself;TCPSocket
,TCPServer
,UDPSocket
,UNIXSocket
andUNIXServer
classes are merely sugar to avoid having to deal with socket details;UNIXSocket
andUNIXServer
can now be used in DGRAM type, in addition to the default STREAM type.Features:
Addrinfo
DNS 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::Server
module, included by bothTCPServer
andUNIXServer
;SO_REUSEPORT
socket option; automatically set forTCPServer
—see http://stackoverflow.com/a/14388707/199791Breaking Change:
IPAddress
now automatically detects the address family, so the argument was removed (limited impact).Examples:
closes #3214
closes #3495
refs #3586