=encoding utf8

=head1 NAME

Sidef::Types::Glob::SocketHandle - An open socket connection, for network communication

=head1 DESCRIPTION

This class represents an open socket connection in Sidef, wrapping a raw Perl filehandle together with a reference to the L<Sidef::Types::Glob::Socket> that created it. SocketHandle objects are the ones you actually call C<bind>, C<listen>, C<accept>, C<connect>, C<send>, and C<recv> on — not the C<Socket> object itself.

You don't normally construct a SocketHandle directly. Instead, you get one from:

=over 4

=item * C<Socket.new.open(domain, type, protocol)> — creates a new socket

=item * C<Socket.new.socketpair(...)> — creates a connected pair of sockets

=item * an existing SocketHandle's own L</accept> — accepts an incoming connection

=back

=head1 SYNOPSIS

    # Create a TCP server
    var server = Socket.new.open(Socket.PF_INET, Socket.SOCK_STREAM, Socket.IPPROTO_TCP)
    server.bind(Socket.pack_sockaddr_in(8080, Socket.INADDR_ANY))
    server.listen(5)

    say "Server listening on port 8080..."
    var client = server.accept

    # Read from client
    var data = client.readline
    say "Received: #{data}"

    # Send response
    client.say("Hello from server!")
    client.close

    # Create a client socket
    var sock = Socket.new.open(Socket.PF_INET, Socket.SOCK_STREAM, Socket.IPPROTO_TCP)
    var addr = Socket.pack_sockaddr_in(80, Socket.inet_aton("example.com"))
    sock.connect(addr)
    sock.say("GET / HTTP/1.0\r\n\r\n")
    say sock.slurp
    sock.close

=head1 INHERITS

Inherits methods from L<Sidef::Types::Glob::FileHandle>.

This means all file handle methods (like C<read>, C<write>, C<print>, C<say>, C<close>, etc.) are available on socket handles as well.

=head1 CONSTRUCTION

=head2 new

    SocketHandle.new(sh, socket)

Low-level constructor wrapping an already-open raw filehandle (C<sh>, as produced by Perl's C<socket>/C<accept>) together with a reference to the C<Socket> object that created it. This is used internally by C<Socket>'s own C<open> and C<socketpair>, and by SocketHandle's own L</accept> — you will not normally call it directly; use C<Socket.new.open(domain, type, protocol)> instead.

Aliases: I<call>

=cut

=head1 CONNECTION SETUP

=head2 bind

    self.bind(address)

Binds the socket to a local address and port. This is typically used by server sockets before calling L</listen>. Returns a boolean.

    var sock = Socket.new.open(Socket.PF_INET, Socket.SOCK_STREAM, 0)
    var addr = Socket.pack_sockaddr_in(8080, Socket.INADDR_ANY)
    sock.bind(addr) || die "Cannot bind: #{$!}"

=cut

=head2 listen

    self.listen(queuesize)

Marks the socket as a passive socket that will accept incoming connections. Must be called after L</bind> and before L</accept>. C<queuesize> is the maximum length of the queue of pending connections (typically 5-128). Returns a boolean.

    sock.bind(addr)
    sock.listen(5) || die "Cannot listen: #{$!}"

=cut

=head2 accept

    self.accept

Accepts an incoming connection on a listening socket, blocking until a client connects. Returns a new SocketHandle representing the client connection, or C<nil> on failure.

    var server = Socket.new.open(Socket.PF_INET, Socket.SOCK_STREAM, 0)
    server.bind(Socket.pack_sockaddr_in(8080, Socket.INADDR_ANY))
    server.listen(5)

    loop {
        var client = server.accept || next
        var info = Socket.unpack_sockaddr_in(client.getpeername)
        say "Connection from #{Socket.inet_ntoa(info[1])}:#{info[0]}"
        client.say("Welcome!")
        client.close
    }

=cut

=head2 connect

    self.connect(address)

Initiates a connection to a remote socket. This is typically used by client sockets. Returns a boolean.

    var sock = Socket.new.open(Socket.PF_INET, Socket.SOCK_STREAM, 0)
    var addr = Socket.pack_sockaddr_in(80, Socket.inet_aton("192.168.1.1"))
    sock.connect(addr) || die "Cannot connect: #{$!}"

=cut

=head1 SENDING AND RECEIVING DATA

=head2 send

    self.send(message, flags=0, to=nil)

Sends a message through the socket. For connected sockets (TCP), the C<to> parameter is not needed. For connectionless sockets (UDP), C<to> specifies the destination address (a packed sockaddr structure). Returns a boolean.

    # TCP socket
    sock.send("Hello, World!")

    # UDP socket with destination
    var addr = Socket.pack_sockaddr_in(9999, Socket.inet_aton("192.168.1.100"))
    sock.send("UDP message", 0, addr)

=cut

=head2 recv

    self.recv(length, flags=0)

Receives up to C<length> bytes of data from the socket. Returns the received data as a String, or C<nil> on error.

    var data = sock.recv(1024)
    if (defined(data)) {
        say "Received: #{data}"
    }

=cut

=head1 CONNECTION INFORMATION

=head2 getsockname

    self.getsockname

Returns the local address to which the socket is bound, as a packed sockaddr structure.

    var addr = sock.getsockname
    var info = Socket.unpack_sockaddr_in(addr)
    say "Local address: #{Socket.inet_ntoa(info[1])}:#{info[0]}"

=cut

=head2 getpeername

    self.getpeername

Returns the address of the peer (remote end) connected to this socket, as a packed sockaddr structure, or C<nil> on failure.

    var addr = sock.getpeername
    if (defined(addr)) {
        var info = Socket.unpack_sockaddr_in(addr)
        say "Peer address: #{Socket.inet_ntoa(info[1])}:#{info[0]}"
    }

=cut

=head2 getsockopt

    self.getsockopt(level, optname)

Gets a socket option value, as a raw packed String — decode it according to the specific option (e.g. as an integer with C<unpack>). Returns C<nil> on failure.

    var level = Socket.SOL_SOCKET
    var raw = sock.getsockopt(level, Socket.SO_REUSEADDR)

=cut

=head2 setsockopt

    self.setsockopt(level, optname, optval)

Sets a socket option value. Returns a boolean.

    # Enable address reuse (useful for servers)
    sock.setsockopt(Socket.SOL_SOCKET, Socket.SO_REUSEADDR, 1) ||
        die "Cannot set SO_REUSEADDR: #{$!}"

    # Enable TCP keepalive
    sock.setsockopt(Socket.SOL_SOCKET, Socket.SO_KEEPALIVE, 1)

=cut

=head1 CLOSING A CONNECTION

=head2 shutdown

    self.shutdown(how)

Shuts down part or all of the socket connection, without necessarily closing the underlying filehandle (see L<Sidef::Types::Glob::FileHandle/close> for that). C<how> is one of:

=over 4

=item * 0 (C<Socket.SHUT_RD>) - Further receives are disallowed

=item * 1 (C<Socket.SHUT_WR>) - Further sends are disallowed

=item * 2 (C<Socket.SHUT_RDWR>) - Further sends and receives are disallowed

=back

Returns a boolean.

    # Finish sending data and close the write side
    sock.shutdown(Socket.SHUT_WR)

    # Read remaining data from the peer
    var remaining = sock.slurp

    # Close completely
    sock.close

=cut

=head1 EXAMPLES

=head2 TCP Server

    var server = Socket.new.open(Socket.PF_INET, Socket.SOCK_STREAM, 0)
    server.setsockopt(Socket.SOL_SOCKET, Socket.SO_REUSEADDR, 1)
    server.bind(Socket.pack_sockaddr_in(8080, Socket.INADDR_ANY))
    server.listen(10)

    say "Server started on port 8080"

    loop {
        var client = server.accept || next
        say "Client connected"

        client.say("Welcome to the server!")
        var request = client.readline
        say "Received: #{request}"

        client.close
    }

=head2 TCP Client

    var sock = Socket.new.open(Socket.PF_INET, Socket.SOCK_STREAM, 0)
    var addr = Socket.pack_sockaddr_in(80, Socket.inet_aton("example.com"))

    sock.connect(addr) || die "Cannot connect: #{$!}"

    sock.say("GET / HTTP/1.1")
    sock.say("Host: example.com")
    sock.say("Connection: close")
    sock.say("")

    say sock.slurp
    sock.close

=head2 UDP Server

    var sock = Socket.new.open(Socket.PF_INET, Socket.SOCK_DGRAM, 0)
    sock.bind(Socket.pack_sockaddr_in(9999, Socket.INADDR_ANY))

    say "UDP server listening on port 9999"

    loop {
        var data = sock.recv(1024)
        defined(data) || next

        var peer = sock.getpeername
        var info = Socket.unpack_sockaddr_in(peer)
        say "Received from #{Socket.inet_ntoa(info[1])}:#{info[0]}: #{data}"
    }

=head2 UDP Client

    var sock = Socket.new.open(Socket.PF_INET, Socket.SOCK_DGRAM, 0)
    var addr = Socket.pack_sockaddr_in(9999, Socket.inet_aton("192.168.1.100"))

    sock.send("Hello, UDP server!", 0, addr)

    var response = sock.recv(1024)
    say "Response: #{response}" if defined(response)

    sock.close

=head1 SEE ALSO

=over 4

=item * L<Sidef::Types::Glob::Socket> - Creates SocketHandle objects and provides constants/conversion helpers

=item * L<Sidef::Types::Glob::FileHandle> - Parent class with I/O methods

=item * L<Socket> - Perl's Socket module documentation for constants and helper functions

=back

=cut
