NAME

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

DESCRIPTION

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

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

  • Socket.new.open(domain, type, protocol) — creates a new socket

  • Socket.new.socketpair(...) — creates a connected pair of sockets

  • an existing SocketHandle's own "accept" — accepts an incoming connection

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

INHERITS

Inherits methods from Sidef::Types::Glob::FileHandle.

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

CONSTRUCTION

new

SocketHandle.new(sh, socket)

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

Aliases: call

CONNECTION SETUP

bind

self.bind(address)

Binds the socket to a local address and port. This is typically used by server sockets before calling "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: #{$!}"

listen

self.listen(queuesize)

Marks the socket as a passive socket that will accept incoming connections. Must be called after "bind" and before "accept". 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: #{$!}"

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 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
}

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: #{$!}"

SENDING AND RECEIVING DATA

send

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

Sends a message through the socket. For connected sockets (TCP), the to parameter is not needed. For connectionless sockets (UDP), 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)

recv

self.recv(length, flags=0)

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

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

CONNECTION INFORMATION

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]}"

getpeername

self.getpeername

Returns the address of the peer (remote end) connected to this socket, as a packed sockaddr structure, or 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]}"
}

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 unpack). Returns nil on failure.

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

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)

CLOSING A CONNECTION

shutdown

self.shutdown(how)

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

  • 0 (Socket.SHUT_RD) - Further receives are disallowed

  • 1 (Socket.SHUT_WR) - Further sends are disallowed

  • 2 (Socket.SHUT_RDWR) - Further sends and receives are disallowed

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

EXAMPLES

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
}

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

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}"
}

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

SEE ALSO