15#include <Poco/Net/StreamSocket.h>
16#include <Poco/Net/ServerSocket.h>
17#include <Poco/Net/DatagramSocket.h>
18#include <Poco/Net/SocketAddress.h>
19#include <Poco/Net/NetException.h>
20#include <Poco/Exception.h>
22#include <simplesquirrel/simplesquirrel.hpp>
31void callScript1(
const ssq::Object& obj,
const std::string&
s) {
32 if (obj.isEmpty())
return;
33 ssq::Function
f = obj.toFunction();
34 if (
f.isEmpty())
return;
36 SQInteger top = sq_gettop(raw);
37 sq_pushobject(raw,
f.getRaw());
38 sq_pushroottable(raw);
40 sq_call(raw, 2, SQFalse, SQTrue);
44void callScript2(
const ssq::Object& obj, int64_t
a,
const std::string&
s) {
45 if (obj.isEmpty())
return;
46 ssq::Function
f = obj.toFunction();
47 if (
f.isEmpty())
return;
49 SQInteger top = sq_gettop(raw);
50 sq_pushobject(raw,
f.getRaw());
51 sq_pushroottable(raw);
54 sq_call(raw, 3, SQFalse, SQTrue);
58void callScript3(
const ssq::Object& obj, int64_t
a, int64_t
b,
const std::string&
s) {
59 if (obj.isEmpty())
return;
60 ssq::Function
f = obj.toFunction();
61 if (
f.isEmpty())
return;
63 SQInteger top = sq_gettop(raw);
64 sq_pushobject(raw,
f.getRaw());
65 sq_pushroottable(raw);
69 sq_call(raw, 4, SQFalse, SQTrue);
78 worker_ = std::make_unique<NetWorker>(
this);
83 if (worker_) worker_->stop();
96 return new HttpRequest(
this, std::move(method), std::move(url));
100 if (!socket)
return nullptr;
101 auto* ch =
new Channel(socket);
121 if (!socket)
return nullptr;
122 auto* link =
new UdpLink(
this, socket);
128 return link ?
new NetRpc(link) :
nullptr;
136 if (sock) udpLinks_[sock] = link;
140 if (sock) udpLinks_.erase(sock);
144 if (sock) udpHosts_[sock] = host;
148 if (sock) udpHosts_.erase(sock);
168 if (worker_) worker_->post(std::move(
c));
172 if (worker_) worker_->drain(out);
176 std::lock_guard<std::mutex> lock(watchMu_);
177 if (std::find(watchedTcp_.begin(), watchedTcp_.end(), sock) == watchedTcp_.end())
178 watchedTcp_.push_back(sock);
182 std::lock_guard<std::mutex> lock(watchMu_);
183 watchedTcp_.erase(std::remove(watchedTcp_.begin(), watchedTcp_.end(), sock), watchedTcp_.end());
187 std::lock_guard<std::mutex> lock(watchMu_);
188 if (std::find(watchedUdp_.begin(), watchedUdp_.end(), sock) == watchedUdp_.end())
189 watchedUdp_.push_back(sock);
193 std::lock_guard<std::mutex> lock(watchMu_);
194 watchedUdp_.erase(std::remove(watchedUdp_.begin(), watchedUdp_.end(), sock), watchedUdp_.end());
198 std::lock_guard<std::mutex> lock(channelMu_);
199 channels_[sock] = ch;
203 std::lock_guard<std::mutex> lock(channelMu_);
204 channels_.erase(sock);
208 std::lock_guard<std::mutex> lock(channelMu_);
209 auto it = channels_.find(sock);
210 return it == channels_.end() ? nullptr : it->second;
214 std::vector<TcpSocket*> tcpCopy;
215 std::vector<UdpSocket*> udpCopy;
217 std::lock_guard<std::mutex> lock(watchMu_);
218 tcpCopy = watchedTcp_;
219 udpCopy = watchedUdp_;
223 if (sock->isListening() && sock->server()) {
225 Poco::Net::StreamSocket ss = sock->server()->acceptConnection();
226 ss.setBlocking(
false);
227 auto peer = std::make_unique<TcpSocket>(
this);
228 auto stream = std::make_unique<Poco::Net::StreamSocket>(ss);
229 peer->setConnectedSocket(std::move(stream));
243 if (sock->isConnected() && sock->stream()) {
247 int n = sock->stream()->receiveBytes(buf,
sizeof(buf));
253 c.bytes = std::make_shared<std::vector<char>>(buf, buf +
n);
254 c.peer = sock->getPeer();
265 }
catch (
const Poco::Exception&) {
271 if (!sock || !sock->datagram())
continue;
272 for (
int i = 0; i < 64; ++i) {
275 Poco::Net::SocketAddress sender;
276 int n = sock->datagram()->receiveFrom(buf,
sizeof(buf), sender);
282 c.bytes = std::make_shared<std::vector<char>>(buf, buf +
n);
283 c.peer = sender.toString();
285 }
catch (
const Poco::Exception&) {
295 auto* ev = eve::ModuleManager::getInstance<eve::event::Event>(
"Event");
301 std::vector<Variant> args;
304 args.push_back(Variant::makePtr(
c.handle));
305 args.push_back(Variant::makeString(
c.reason.empty() ?
"ok" :
c.reason));
306 ev->push(
new Message(
"netconn", args));
315 if (
c.bytes && !
c.bytes->empty())
317 args.push_back(Variant::makePtr(
c.handle));
318 args.push_back(Variant::makePtr(bd));
319 args.push_back(Variant::makeString(
c.peer));
320 ev->push(
new Message(
"netdata", args));
324 Channel* ch =
channelFor(
static_cast<TcpSocket*
>(
c.handle));
326 args.push_back(Variant::makePtr(ch));
327 args.push_back(Variant::makeString(
c.reason));
328 ev->push(
new Message(
"chclose", args));
331 args.push_back(Variant::makePtr(
c.handle));
332 args.push_back(Variant::makeString(
c.reason));
333 ev->push(
new Message(
"neterr", args));
338 if (
c.bytes && !
c.bytes->empty())
340 args.push_back(Variant::makePtr(
c.handle));
341 args.push_back(Variant::makeInt(
c.status));
342 args.push_back(Variant::makePtr(bd));
343 ev->push(
new Message(
"httpresp", args));
348 if (
c.bytes && !
c.bytes->empty())
350 args.push_back(Variant::makePtr(
c.handle));
351 args.push_back(Variant::makePtr(bd));
352 ev->push(
new Message(
"chmsg", args));
356 args.push_back(Variant::makePtr(
c.handle));
357 args.push_back(Variant::makeString(
c.reason));
358 ev->push(
new Message(
"chclose", args));
364 std::vector<NetCompletion> batch;
365 if (worker_) worker_->drain(batch);
366 const int64_t now = std::chrono::duration_cast<std::chrono::milliseconds>(
367 std::chrono::steady_clock::now().time_since_epoch())
369 for (
auto&
c : batch) {
371 auto* sock =
static_cast<UdpSocket*
>(
c.handle);
372 auto hit = udpHosts_.find(sock);
373 if (hit != udpHosts_.end()) {
374 if (
c.bytes) hit->second->onDatagram(*
c.bytes,
c.peer);
377 auto lit = udpLinks_.find(sock);
378 if (lit != udpLinks_.end()) {
379 if (
c.bytes) lit->second->onDatagram(*
c.bytes,
c.peer);
385 for (
auto& kv : udpHosts_) kv.second->pump(now);
386 for (
auto& kv : udpLinks_) kv.second->pump(now);
389void Network::expose(ssq::Table& table) {
390 auto cls = table.addClass(
name, Network::create,
false);
394 "TcpSocket", std::function<TcpSocket*()>([]() {
return new TcpSocket(Network::create()); }),
true);
404 auto udp = table.addClass<UdpSocket>(
405 "UdpSocket", std::function<UdpSocket*()>([]() {
return new UdpSocket(Network::create()); }),
true);
414 auto http = table.addClass<HttpRequest>(
416 std::function<HttpRequest*()>(
417 []() {
return new HttpRequest(Network::create(),
"GET",
"http://127.0.0.1/"); }),
426 auto ch = table.addClass<
Channel>(
427 "Channel", std::function<Channel*()>([]() {
return new Channel(
nullptr); }),
true);
432 auto sess = table.addClass<Session>(
433 "Session", std::function<Session*()>([]() {
return new Session(); }),
true);
439 auto writer = table.addClass<NetWriter>(
440 "NetWriter", std::function<NetWriter*()>([]() {
return new NetWriter(); }),
true);
441 writer.addFunc(
"writeU8", [](NetWriter*
w, int64_t
v) {
w->writeU8(
static_cast<uint8_t
>(
v)); });
442 writer.addFunc(
"writeI8", [](NetWriter*
w, int64_t
v) {
w->writeI8(
static_cast<int8_t
>(
v)); });
443 writer.addFunc(
"writeU16", [](NetWriter*
w, int64_t
v) {
w->writeU16(
static_cast<uint16_t
>(
v)); });
444 writer.addFunc(
"writeI16", [](NetWriter*
w, int64_t
v) {
w->writeI16(
static_cast<int16_t
>(
v)); });
445 writer.addFunc(
"writeU32", [](NetWriter*
w, int64_t
v) {
w->writeU32(
static_cast<uint32_t
>(
v)); });
446 writer.addFunc(
"writeI32", [](NetWriter*
w, int64_t
v) {
w->writeI32(
static_cast<int32_t
>(
v)); });
447 writer.addFunc(
"writeU64", [](NetWriter*
w, int64_t
v) {
w->writeU64(
static_cast<uint64_t
>(
v)); });
448 writer.addFunc(
"writeI64", [](NetWriter*
w, int64_t
v) {
w->writeI64(
static_cast<int64_t
>(
v)); });
454 if (
d)
w->writeBytes(
d->getData(),
d->getSize());
456 writer.addFunc(
"toString", [](NetWriter*
w) {
return w->toString(); });
457 writer.addFunc(
"size", [](NetWriter*
w) {
return static_cast<int64_t
>(
w->size()); });
459 auto reader = table.addClass<NetReader>(
460 "NetReader", std::function<NetReader*()>([]() {
return new NetReader(); }),
true);
462 return d ? r->init(
d->getData(),
d->getSize()) : false;
464 reader.addFunc(
"initString", [](NetReader* r,
const std::string&
s) {
return r->setBytes(
s); });
465 reader.addFunc(
"u8", [](NetReader* r) {
return static_cast<int64_t
>(r->u8()); });
466 reader.addFunc(
"i8", [](NetReader* r) {
return static_cast<int64_t
>(r->i8()); });
467 reader.addFunc(
"u16", [](NetReader* r) {
return static_cast<int64_t
>(r->u16()); });
468 reader.addFunc(
"i16", [](NetReader* r) {
return static_cast<int64_t
>(r->i16()); });
469 reader.addFunc(
"u32", [](NetReader* r) {
return static_cast<int64_t
>(r->u32()); });
470 reader.addFunc(
"i32", [](NetReader* r) {
return static_cast<int64_t
>(r->i32()); });
471 reader.addFunc(
"u64", [](NetReader* r) {
return static_cast<int64_t
>(r->u64()); });
472 reader.addFunc(
"i64", [](NetReader* r) {
return static_cast<int64_t
>(r->i64()); });
476 reader.addFunc(
"str", [](NetReader* r) {
return r->str(); });
477 reader.addFunc(
"bytes", [](NetReader* r, int64_t
n) {
478 auto v = r->bytes(
static_cast<size_t>(
n));
479 return std::string(
v.data(),
v.size());
481 reader.addFunc(
"remaining", [](NetReader* r) {
return static_cast<int64_t
>(r->remaining()); });
482 reader.addFunc(
"pos", [](NetReader* r) {
return static_cast<int64_t
>(r->pos()); });
483 reader.addFunc(
"ok", [](NetReader* r) {
return r->ok(); });
485 auto link = table.addClass<UdpLink>(
487 std::function<UdpLink*()>([]() {
return new UdpLink(
nullptr,
nullptr); }),
true);
490 link.addFunc(
"sendReliable", [](UdpLink* l, int64_t ch,
const std::string&
s) {
493 link.addFunc(
"sendUnreliable", [](UdpLink* l, int64_t ch,
const std::string&
s) {
496 link.addFunc(
"sendOrdered", [](UdpLink* l, int64_t ch,
const std::string&
s) {
499 link.addFunc(
"onMessage", [](UdpLink* l, ssq::Object
fn) {
501 l->setMessageHandler(
503 callScript2(
fn, ch, std::string(
d,
n));
508 link.addFunc(
"isAlive", [](UdpLink* l) {
return l && l->isAlive(); });
510 link.addFunc(
"peerId", [](UdpLink* l) {
return static_cast<int64_t
>(l->peerId()); });
511 link.addFunc(
"pendingReliable",
512 [](UdpLink* l) {
return static_cast<int64_t
>(l->pendingReliable()); });
513 link.addFunc(
"pendingFragments",
514 [](UdpLink* l) {
return static_cast<int64_t
>(l->pendingFragments()); });
516 auto rpc = table.addClass<NetRpc>(
517 "NetRpc", std::function<NetRpc*()>([]() {
return new NetRpc(
nullptr); }),
true);
518 rpc.addFunc(
"callRpc", [](NetRpc* r, int64_t msgId,
const std::string& payload,
520 r->callString(
static_cast<uint16_t
>(msgId), payload, reliable);
522 rpc.addFunc(
"registerRpc", [](NetRpc* r, int64_t msgId, ssq::Object
fn) {
523 r->registerScript(
static_cast<uint16_t
>(msgId),
fn);
526 auto host = table.addClass<NetHost>(
527 "NetHost", std::function<NetHost*()>([]() {
return new NetHost(
nullptr); }),
true);
529 host.addFunc(
"onMessage", [](NetHost*
h, ssq::Object
fn) {
531 h->setMessageHandler(
533 callScript3(
fn, peerId, ch, std::string(
d,
n));
536 host.addFunc(
"onPeerConnected", [](NetHost*
h, ssq::Object
fn) {
538 h->setPeerConnectedHandler([
fn](uint32_t
id) { callScript2(
fn,
id,
""); });
540 host.addFunc(
"onPeerDisconnected", [](NetHost*
h, ssq::Object
fn) {
542 h->setPeerDisconnectedHandler([
fn](uint32_t
id) { callScript2(
fn,
id,
""); });
544 host.addFunc(
"sendReliable", [](NetHost*
h, int64_t peerId, int64_t ch,
545 const std::string&
s) {
547 static_cast<uint8_t
>(ch),
s);
549 host.addFunc(
"sendUnreliable", [](NetHost*
h, int64_t peerId, int64_t ch,
550 const std::string&
s) {
552 static_cast<uint8_t
>(ch),
s);
554 host.addFunc(
"sendOrdered", [](NetHost*
h, int64_t peerId, int64_t ch,
555 const std::string&
s) {
557 static_cast<uint8_t
>(ch),
s);
560 host.addFunc(
"peerCount", [](NetHost*
h) {
return static_cast<int64_t
>(
h->peerCount()); });
565void Network::expose(ssq::Class&
cls) {
struct SQVM * HSQUIRRELVM
#define Module_IMPL(ModuleName, newExpr)
SettlementPipeline::Stage fn
virtual std::string getName() const =0
In-memory byte buffer implementing eve::Data (ref-counted).
A named event carrying an ordered list of Variant payloads. Pushed messages are heap-allocated; the q...
Length-prefixed (big-endian uint32) message framing over a TcpSocket. sendMsg() writes one framed mes...
bool sendMsg(eve::data::ByteData *data)
Sends one framed message.
TcpSocket * getSocket() const
The underlying TCP socket, or nullptr.
void feed(const std::vector< char > &bytes)
Feeds received bytes into the frame parser. Called on the main thread from Network::pump / the emitCo...
bool sendMsgString(std::string s)
Sends a string payload as one framed message (empty strings are rejected).
Asynchronous HTTP request (Poco-based). Configure headers/body, call submit(), then receive the respo...
void setBodyString(std::string s)
Sets the request body as a string.
void setTimeout(int ms)
Overrides the module default timeout for this request (ms).
void setBody(eve::data::ByteData *data)
Sets the request body bytes.
void setHeader(std::string k, std::string v)
Sets one request header.
bool submit()
Queues the request on the worker; true when accepted.
void setVerifySsl(bool verify)
Overrides TLS verification for this request.
void setLossRate(float rate)
bool start(uint16_t port)
void setTimeoutMs(int ms)
UdpLink * linkByPeerId(uint32_t peerId) const
void writeString(const std::string &s)
Network module: TCP/UDP/HTTP factories, background worker, and completion event plumbing....
NetHost * newHost()
Creates a UDP host (peer discovery / broadcast).
NetReader * newReader(std::string bytes)
Creates a streaming byte reader over a string buffer.
bool getVerifySsl() const
void setTimeout(int ms)
Default socket/HTTP timeout in milliseconds.
Session * newSession()
Creates a named-channel session container.
void watchTcp(TcpSocket *sock)
Internal: register/unregister sockets for pump polling.
void unbindUdpLink(UdpSocket *sock)
UdpLink * newUdpLink(UdpSocket *socket)
Creates a framed UDP link over a socket.
void unwatchUdp(UdpSocket *sock)
void setVerifySsl(bool verify)
Whether TLS peer certificates are verified (default true).
void bindChannel(TcpSocket *sock, Channel *ch)
Internal: bind a Channel to its socket (and reverse lookup).
HttpRequest * newHttp(std::string method, std::string url)
Creates a new HTTP request (method e.g. "GET"/"POST", full URL).
void bindUdpLink(UdpSocket *sock, UdpLink *link)
void watchUdp(UdpSocket *sock)
void unbindUdpHost(UdpSocket *sock)
Channel * channelFor(TcpSocket *sock) const
NetRpc * newRpc(UdpLink *link)
Creates an RPC client over a UDP link.
void pump()
Drains worker completions and emits them as events; call per frame.
NetWriter * newWriter()
Creates a streaming byte writer.
UdpSocket * newUdp()
Creates a new UDP socket.
void pollSockets()
Internal: polls watched sockets; called from the NetWorker thread.
void drainForTest(std::vector< NetCompletion > &out)
Test helper: drains pending completions into out.
TcpSocket * newTcp()
Creates a new TCP socket (client or server).
void unwatchTcp(TcpSocket *sock)
void post(NetCompletion c)
Posts a completion to the worker queue (thread-safe).
void unbindChannel(TcpSocket *sock)
Channel * newChannel(TcpSocket *socket)
Creates a length-prefixed Channel over a TCP socket.
void bindUdpHost(UdpSocket *sock, NetHost *host)
Named collection of Channels (a "session"). Lookup by name, close all. Does not own the channels; the...
void remove(std::string name)
Removes a channel from the session (does not delete it).
void add(std::string name, Channel *ch)
Registers a channel under a name (replaces an existing entry).
void closeAll()
Closes every channel in the session.
Channel * get(std::string name)
Finds a channel by name, or nullptr.
TCP socket backed by Poco::Net; supports both client (connect) and server (listen/accept) roles....
void pushAccepted(std::unique_ptr< TcpSocket > sock)
bool listen(uint16_t port)
Starts listening on the given port; true on success.
bool sendString(std::string s)
Sends a string payload.
void close()
Closes the socket.
std::string getPeer() const
Remote peer address string, e.g. "1.2.3.4:5678".
TcpSocket * accept()
Accepts one pending client, or nullptr if none.
bool isConnected() const
True while a stream is connected.
bool connect(std::string host, uint16_t port)
Connects to host:port; true on success.
bool send(eve::data::ByteData *data)
Sends a framed byte payload; true when queued/accepted.
bool setRemoteString(const std::string &addr)
bool setRemote(std::string host, uint16_t port)
void setTimeoutMs(int ms)
void setLossRate(float rate)
UDP socket backed by Poco::Net; supports connect/bind and datagram send.
bool sendTo(eve::data::ByteData *data, std::string host, uint16_t port)
Sends a datagram to an explicit host:port.
bool sendToString(std::string s, std::string host, uint16_t port)
Sends a string datagram to an explicit host:port.
void close()
Closes the socket.
bool bind(uint16_t port)
Binds to a local port; true on success.
bool sendString(std::string s)
Sends a string datagram to the connected peer.
bool connect(std::string host, uint16_t port)
Connects to a remote host:port (restricts send()).
bool send(eve::data::ByteData *data)
Sends a datagram to the connected peer.
void pushValue(HSQUIRRELVM vm, const eve::rx::Value &v)
One asynchronous network result/event. handle points at the originating TcpSocket/UdpSocket/HttpReque...