std.net
API সারমর্ম (Beans source থেকে npm run coverage দিয়ে বানানো): 2টা package function · 5টা type · 1টা constructor · 6টা static method · 32টা instance method · 4টা public field।
std.net দেয় TCP আর UDP socket, name resolve করার সুবিধা, আর দুটো async readiness helper। এটা std.sock-এর raw socket syscall-গুলোর উপর একটা সহজে-পড়া layer। source আছে এখানে: stdlib/std/net/net.b।
import std.netযে নিয়মগুলো এই package-টাকে গড়ে তুলেছে
“যে নিয়মগুলো এই package-টাকে গড়ে তুলেছে” সেকশন- socket বানাতে constructor না, একটা named static লাগে। যে construction fail করতে পারে সেটা
Resultফেরত দেয়, তাই লেখা হয়TcpStream.connect(...),TcpListener.bind(...), আরUdpSocket.bind(...)— একদমFile.open-এর মতো গড়ন। - প্রতিটা socket একটা
unique class। এটা move-only, আর scope থেকে বেরিয়ে গেলেdeinit-এ নিজেই বন্ধ হয়ে যায়। socket-কেthread.spawn-এ ধরে নেওয়া যায় না, কারণuniqueSendনা। close-এর error দেখতে চাইলেclose()আছে; না দেখলে scope শেষ হলেই কাজটা হয়ে যায়। - address family বাছা হয় না, resolve হয়। প্রতিটা entry point host-টাকে
getaddrinfoদিয়ে চালায়, তাই"localhost","127.0.0.1", আর"::1"— সবই কোনো family flag ছাড়াই চলে। - read আর write নিয়ম করেই partial।
readযা এসেছে তা-ই ফেরত দেয়, আরwriteকতটা গেল সেটা জানায়।readথেকে খালিBytesবলতে বোঝায় peer connection বন্ধ করে দিয়েছে।write_allআরread_exactনিজে থেকে loop করে নেয়। - blocking call EINTR-এ আবার চেষ্টা করে। timeout হলে
timeoutkind-এর একটাerrআসে, কখনো hang হয় না।
যেসব error kind দেখা যেতে পারে: refused, in_use, timeout, reset, unreachable, not_found, closed, eof, invalid, permission, io।
Address
“Address” সেকশনএকটা সাধারণ value: একটা numeric host আর একটা port। ইচ্ছেমতো copy করা যায়।
pub class Addressnew Address(host: string, port: int)
pub host: stringpub port: int
pub static fn resolve(host: string, port: int) -> Result<List<Address>>pub fn to_string() -> stringpub fn is_ipv6() -> boolpub fn is_loopback() -> boolresolveএকটা নাম যত distinct numeric address-এ map করে সবগুলো ফেরত দেয়, resolver যে order-এ দেয় সেই order-এই। যে নাম resolve হয় না সেটাnot_foundkind-এর একটাerr।to_stringhost:portছাপে, আর IPv6-এর জন্য[host]:port।
Datagram
“Datagram” সেকশনএকটা এসে পৌঁছানো UDP message: পাঠানোর address আর বাইটগুলো।
pub class Datagrampub from: Addresspub data: BytesTcpStream
“TcpStream” সেকশনএকটা connected TCP socket। move-only; move দিয়ে পাস করা হয়, ? দিয়ে Result থেকে বের করে নেওয়া হয়।
pub unique class TcpStream
pub static fn connect(host: string, port: int) -> Result<TcpStream>pub static fn connect_timeout(host: string, port: int, ms: int) -> Result<TcpStream>
pub fn write(data: Bytes) -> Result<int>pub fn write_all(data: Bytes) -> Result<int>pub fn write_text(text: string) -> Result<int>pub fn read(max: int) -> Result<Bytes>pub fn read_exact(count: int) -> Result<Bytes>pub fn read_to_end(limit: int) -> Result<Bytes>pub fn peer_address() -> Result<Address>pub fn local_address() -> Result<Address>pub fn set_timeouts(read_ms: int, write_ms: int) -> Result<bool>pub fn set_nonblocking(on: bool) -> Result<bool>pub fn shutdown_write() -> Result<bool>pub fn shutdown_read() -> Result<bool>pub fn close() -> Result<bool>pub fn poll_handle() -> intconnectOS যতক্ষণ অপেক্ষা করে ততক্ষণ অপেক্ষা করে;connect_timeoutmsমিলিসেকেন্ড পরে হাল ছেড়েtimeoutkind দেয়।writeহয়তোdata-র পুরোটা না পাঠিয়ে count ফেরত দেয়।write_allসব পাঠানো শেষ না হওয়া পর্যন্ত loop করে।readবড়জোরmaxবাইট ফেরত দেয়; খালি result বলতে বোঝায় peer বন্ধ করে দিয়েছে।read_exactcountবাইট আসার আগেই peer বন্ধ করলেeofkind-এ fail করে।read_exactআরread_to_endcopy-করা chunk জোড়া না দিয়ে একটাই result buffer বড় করে।write_textstring storage সরাসরি পাঠিয়ে দেয়।shutdown_writepeer-কে EOF পাঠায়, কিন্তু read half খোলা রাখে।poll_handledescriptor-টা borrow করে ফেরত দেয়, যাতে একটা poller বা async helper-এ register করা যায়। এটা ownership হস্তান্তর করে না; ওটা close করা যাবে না।
loopback-এর উপর ছোট্ট একটা request আর reply:
import std.ioimport std.net
fn main() { let listener: net.TcpListener = net.TcpListener.bind("127.0.0.1", 0).expect("bind") let port: int = listener.port().expect("port")
let client: net.TcpStream = net.TcpStream.connect("127.0.0.1", port).expect("connect") let server: net.TcpStream = listener.accept().expect("accept")
client.write_text("ping").expect("write") let got: Bytes = server.read(64).expect("read") io.println(got.to_string())
client.close().expect("close") server.close().expect("close")}TcpListener
“TcpListener” সেকশনএকটা socket যেটা আসা TCP connection accept করে।
pub unique class TcpListener
pub static fn bind(host: string, port: int) -> Result<TcpListener>pub static fn bind_with_backlog(host: string, port: int, depth: int) -> Result<TcpListener>
pub fn accept() -> Result<TcpStream>pub fn accept_timeout(ms: int) -> Result<TcpStream>pub fn local_address() -> Result<Address>pub fn port() -> Result<int>pub fn set_nonblocking(on: bool) -> Result<bool>pub fn close() -> Result<bool>pub fn poll_handle() -> intbind128-এর backlog ব্যবহার করে;bind_with_backlogদিয়ে accept-queue-র depth নিজে ঠিক করা যায়।- port
0দিলে system একটা খালি port দিয়ে দেয়।port()দিয়ে সেটা পড়ে নেওয়া যায় — একটা test এভাবেই কোনো নম্বর আন্দাজ না করে bind করে। acceptconnection আসা পর্যন্ত block করে।accept_timeout(0)হলো একটা non-blocking check; positive timeout শেষ হয়ে গেলেtimeoutkind।
UdpSocket
“UdpSocket” সেকশনএকটা bound UDP socket। প্রতিটা send একটা message, আর প্রতিটা receive একটা message ফেরত দেয়, সাথে পাঠানোর address জুড়ে।
pub unique class UdpSocket
pub static fn bind(host: string, port: int) -> Result<UdpSocket>
pub fn send_to(data: Bytes, to: Address) -> Result<int>pub fn recv_from(max: int) -> Result<Datagram>pub fn local_address() -> Result<Address>pub fn port() -> Result<int>pub fn set_timeouts(read_ms: int, write_ms: int) -> Result<bool>pub fn set_nonblocking(on: bool) -> Result<bool>pub fn close() -> Result<bool>pub fn poll_handle() -> intsend_toজানায় কত বাইট গেল। একটা datagram হয় পুরোটা যায় না হলে একেবারেই যায় না, তাই কম count বলতে বোঝায় message-টা বড্ড বড় ছিল।recv_fromএকটা datagram বড়জোরmaxবাইট পড়ে; একটা datagram-এmax-এর পরে যা থাকে সেটা OS ফেলে দেয়, তাইmax-কে protocol অনুযায়ী মাপতে হবে। ফেরত-আসাDatagramএসে-পৌঁছানো payload-এর ownership নিয়ে নেয়, একটা জোড়া-লাগানো runtime buffer জোড়া দিয়ে-কেটে না নিয়েই।
loopback-এ দুটো UDP socket, একটা আরেকটাকে পাঠাচ্ছে:
import std.ioimport std.net
fn main() { let inbox: net.UdpSocket = net.UdpSocket.bind("127.0.0.1", 0).expect("bind") let port: int = inbox.port().expect("port")
let sender: net.UdpSocket = net.UdpSocket.bind("127.0.0.1", 0).expect("bind") let target: net.Address = new net.Address("127.0.0.1", port) sender.send_to(Bytes.from("hello"), target).expect("send")
let note: net.Datagram = inbox.recv_from(64).expect("recv") io.println("from {note.from.to_string()}: {note.data.to_string()}")}Async readiness
“Async readiness” সেকশনদুটো async function দিয়ে একটা async task একটা thread না ধরে রেখেই socket-এর জন্য অপেক্ষা করতে পারে। poll_handle() থেকে পাওয়া descriptor-টা পাস করা হয়। এগুলো level-triggered: socket যদি আগে থেকেই ready থাকে, তবে সাথে সাথেই complete হয়ে যায়।
pub async fn readable(handle: int) -> boolpub async fn writable(handle: int) -> boolasync function কীভাবে চলে সেটা async guide-এ বোঝানো আছে। একটা thread থেকে একসাথে অনেক socket-এর জন্য অপেক্ষা করতে চাইলে বরং std.poll ব্যবহার করুন।