Network bindings
SharpProspero.Interop.Net holds the low-level surface the higher-level types on
Networking sit on. Reach for it when a job needs something the friendly wrappers do
not cover: HTTP/2, hosting a wireless access point so a phone can join the console, IPv6, per-socket
counters, DNS-cache control, scatter/gather I/O, or the HTTP client’s cookie box and epoll multiplexer.
flowchart LR
subgraph app [High-level]
NI[NetworkInfo] --> NC[NetCtl]
HC[HttpClient] --> HTTP[Http + Ssl + NetPool]
TCP[TcpConnection] --> SOCK[Socket]
UDP[UdpSocket] --> SOCK
POLL[SocketPoller] --> SOCK
end
subgraph raw [Advanced interop]
H2[Http2]
AP[NetCtlAp]
APD[NetCtlApDialog]
IF[NetStats / NetDns]
RES[Resolver / Ether / Inet]
end
Each binding here is a [LibraryImport] class of blittable methods, plus the enums, structures and
constants that go with the service. The rules that govern how a binding is shaped — direct P/Invoke,
blittable parameters, StringMarshalling.Utf8 for a NUL-terminated string, exact-layout structures —
are covered on the Bindings page and apply to every entry point below.
On this page
Hosting a wireless access point
NetCtlAp (library libSceNetCtlAp, hosted by libSceNetCtl.prx) controls the softAP the console
brings up on its own wireless interface so a client can associate to the box directly. The startup
walk goes through the on-screen dialog in NetCtlApDialog; once the
runtime says the access point is running the bindings here read back its own state, the credentials
a joiner must present, and callbacks fire as the state changes.
The type surface is short. NetCtlApState is where the access point is in its softAP lifecycle:
Stopped, Waiting, ApStarting, LanSetting and Started. SceNetCtlApInfo (152 bytes) is the
readout for the running access point — BSSID, thirty-three-byte NUL-terminated SSID, wireless
channel, security kind, DHCP state, and the sixteen-byte NUL-terminated IPv4 address and netmask the
service is handing out. SceNetCtlApConnectInfo (184 bytes) is the credential block a joiner sees:
the SSID, the security kind, and the sixty-five-byte NUL-terminated pre-shared key.
The wireless security kinds WifiSecurity names in either block are WifiSecurityNoAuth,
WifiSecurityWep, WifiSecurityWpaPskTkip, WifiSecurityWpaPskAes, WifiSecurityWpa2PskTkip,
WifiSecurityWpa2PskAes, WifiSecurityWpa3Sae and WifiSecurityWpa2Wpa3.
using SharpProspero.Interop.Net;
// Bring the control service up (the dialog brings the access point up itself).
SceResult.ThrowIfFailed(NetCtlAp.sceNetCtlApInit(), nameof(NetCtlAp.sceNetCtlApInit));
int state = 0;
SceResult.ThrowIfFailed(NetCtlAp.sceNetCtlApGetState(&state), nameof(NetCtlAp.sceNetCtlApGetState));
if ((NetCtlApState)state == NetCtlApState.Started)
{
var info = new SceNetCtlApInfo { Size = (nuint)NetCtlAp.InfoSize };
SceResult.ThrowIfFailed(NetCtlAp.sceNetCtlApGetInfo(&info),
nameof(NetCtlAp.sceNetCtlApGetInfo));
// info.Ssid, info.Channel, info.WifiSecurity and info.IpAddress hold the running access point.
var creds = new SceNetCtlApConnectInfo { Size = (nuint)NetCtlAp.ConnectInfoSize };
byte* optStr = stackalloc byte[NetCtlAp.OptStrLen + 1]; // NUL-terminated, up to 8 characters
SceResult.ThrowIfFailed(NetCtlAp.sceNetCtlApGetConnectInfo(optStr, &creds),
nameof(NetCtlAp.sceNetCtlApGetConnectInfo));
// creds.Ssid and creds.WifiSecurityKey hold what a joining client must present.
}
| Call | What it does |
|---|---|
sceNetCtlApInit, sceNetCtlApTerm |
Start and stop the control service. |
sceNetCtlApGetState(int* state) |
Read the current lifecycle state as a NetCtlApState. |
sceNetCtlApGetInfo(SceNetCtlApInfo*) |
Read the running access point’s own settings; the caller writes InfoSize into the .Size field before the call. |
sceNetCtlApGetConnectInfo(byte* optStr, SceNetCtlApConnectInfo*) |
Read the credentials a client must present. The caller writes ConnectInfoSize into the .Size field. |
sceNetCtlApStop |
Tear the running access point down. |
sceNetCtlApRegisterCallback, sceNetCtlApUnregisterCallback |
Register or drop a callback that fires when the state changes. |
sceNetCtlApCheckCallback |
Deliver any pending events to the callbacks registered on this thread. |
sceNetCtlApGetResult(int eventType, int* errorCode) |
Read the error code that accompanied the most recent event of the given type; called inside a callback body to learn why the state moved. |
sceNetCtlApClearEvent(int eventType) |
Clear the last-known result the runtime holds for that event type. |
Callbacks fire from the thread that runs sceNetCtlApCheckCallback, so the application drives the
check itself — a poll each frame from the frame loop is the usual choice.
The event kinds a callback receives are named by the EventType* constants:
EventTypeStopped, EventTypeStartReqFinished, EventTypeStopReqFinished and EventTypeStarted.
CallbackMax (8) caps how many callbacks may be registered at once, and InvalidCid (-1) is the
value the runtime writes into a slot that has not been claimed.
ErrorNotStarted, ErrorInvalidType, ErrorInvalidAddr, ErrorChannelConflict, ErrorStopReq,
ErrorNotInitialized, ErrorCallbackMax, ErrorInvalidId, ErrorIdNotFound, ErrorInvalidSize,
ErrorInvalidOptStr, ErrorAppProcessSuspend, ErrorIpAddrConflict and ErrorFreqBandConflict
cover the negative return codes the runtime uses.
The access point dialog
NetCtlApDialog (library libSceNetCtlApDialog) rides on the common dialog subsystem, so
CommonDialog.sceCommonDialogInitialize from SharpProspero.Interop.Dialog is what
comes first. Fill SceNetCtlApDialogParam through the InitializeParam helper — it zeroes the
block, writes the size fields and the check value the service requires — then set Mode (only
Connect is defined today) and OptStr before the open call.
using SharpProspero.Interop.Dialog;
using SharpProspero.Interop.Net;
SceResult.ThrowIfFailed(NetCtlApDialog.sceNetCtlApDialogInitialize(),
nameof(NetCtlApDialog.sceNetCtlApDialogInitialize));
try
{
var param = default(SceNetCtlApDialogParam);
NetCtlApDialog.InitializeParam(¶m);
SceResult.ThrowIfFailed(NetCtlApDialog.sceNetCtlApDialogOpen(¶m),
nameof(NetCtlApDialog.sceNetCtlApDialogOpen));
// Pump the dialog until it finishes; a frame loop runs UpdateStatus once per frame.
CommonDialogStatus status;
do { status = NetCtlApDialog.sceNetCtlApDialogUpdateStatus(); }
while (status != CommonDialogStatus.Finished);
var result = default(SceNetCtlApDialogResult);
SceResult.ThrowIfFailed(NetCtlApDialog.sceNetCtlApDialogGetResult(&result),
nameof(NetCtlApDialog.sceNetCtlApDialogGetResult));
}
finally
{
NetCtlApDialog.sceNetCtlApDialogTerminate();
}
| Call | What it does |
|---|---|
sceNetCtlApDialogInitialize, sceNetCtlApDialogTerminate |
Bring the dialog subsystem’s access-point flow up and down. |
InitializeParam(SceNetCtlApDialogParam*) |
Zero the block, write the sizes plus the check value, and set Mode to Connect; the caller then sets OptStr if a non-default set is wanted. |
sceNetCtlApDialogOpen(SceNetCtlApDialogParam*) |
Open the dialog. |
sceNetCtlApDialogClose |
Close an open dialog before it finishes on its own. |
sceNetCtlApDialogUpdateStatus / sceNetCtlApDialogGetStatus |
Advance the dialog and return its CommonDialogStatus; the second returns the same value without advancing. |
sceNetCtlApDialogGetResult(SceNetCtlApDialogResult*) |
Read the result of a finished dialog. |
NetCtlApDialogMode names the mode set on the parameter block (only Connect at present).
OptStrLen (8) is the longest option string the block accepts; ErrorInternal is the failure code
the runtime returns for an internal error such as a network fault.
HTTP/2
Http2 (library libSceHttp2) is the HTTP/2 client. The bindings cover the request layer end-to-end
— create request, send, read status and body, add or remove headers, follow or intercept redirects,
manage cookies in their own boxes, and hand authentication or Set-Cookie decisions to a callback.
The service init and template create sit in the linker’s stub catalog so an application that needs
them adds its own [LibraryImport] declarations under libSceHttp2; the request-time entries
already published from managed code are the surface below.
using SharpProspero.Interop.Net;
// tmplId is a template id the caller created (sceHttp2CreateTemplate is in the stub catalog;
// declare it locally with [LibraryImport("libSceHttp2")] where the app needs it).
int reqId = Http2.sceHttp2CreateRequestWithURL(tmplId, "GET", "https://example.com/", 0);
try
{
SceResult.ThrowIfFailed(Http2.sceHttp2SendRequest(reqId, null, 0),
nameof(Http2.sceHttp2SendRequest));
int status = 0;
SceResult.ThrowIfFailed(Http2.sceHttp2GetStatusCode(reqId, &status),
nameof(Http2.sceHttp2GetStatusCode));
byte* buffer = stackalloc byte[4096];
int read;
while ((read = Http2.sceHttp2ReadData(reqId, buffer, (nuint)4096)) > 0)
Consume(new ReadOnlySpan<byte>(buffer, read));
}
finally
{
Http2.sceHttp2AbortRequest(reqId); // release the request-side state
}
| Area | Calls |
|---|---|
| Request lifecycle | sceHttp2CreateRequestWithURL, sceHttp2SendRequest, sceHttp2AbortRequest, sceHttp2ReadData, sceHttp2GetStatusCode, sceHttp2GetResponseContentLength, sceHttp2SetRequestContentLength |
| Headers | sceHttp2AddRequestHeader, sceHttp2RemoveRequestHeader, sceHttp2GetAllResponseHeaders |
| Timeout and transport | sceHttp2SetConnectTimeOut, sceHttp2SetInflateGZIPEnabled |
| Cookies (box life and I/O) | sceHttp2CreateCookieBox, sceHttp2DeleteCookieBox, sceHttp2AddCookie, sceHttp2GetCookie, sceHttp2CookieImport, sceHttp2CookieExport, sceHttp2CookieFlush, sceHttp2GetCookieStats, sceHttp2SetCookieMaxNum, sceHttp2SetCookieMaxNumPerDomain, sceHttp2SetCookieMaxSize |
| Callbacks | sceHttp2SetCookieRecvCallback, sceHttp2SetCookieSendCallback, sceHttp2SetRedirectCallback, sceHttp2SetAuthEnabled, sceHttp2SetAuthInfoCallback |
| Cache flush | sceHttp2AuthCacheFlush, sceHttp2RedirectCacheFlush |
| Accounting | sceHttp2GetMemoryPoolStats |
SceHttp2HttpVersion names the HTTP version a template negotiates for: Http10, Http11, Http20.
SceHttp2AddHeaderMode picks the behaviour of sceHttp2AddRequestHeader when a header of the same
name already exists: Overwrite replaces it, Add appends. SceHttp2AuthType is the challenge the
authentication callback receives — Basic, Digest, and three reserved values. SceHttp2ContentLengthType
reports what sceHttp2GetResponseContentLength filled in: Exist when the response carries a
Content-Length header, NotFound when it does not, ChunkEncoded when the body is chunked.
SceHttp2MemoryPoolStats and SceHttp2CookieStats are the layouts the two accounting calls fill.
The library’s own limit and default constants live on the class: CookiePoolSizeMin (64 KiB, the
lowest cookie-pool size the service accepts), the microsecond defaults DefaultKeepaliveTimeout
(7 s), DefaultTimeout (120 s), DefaultConnectTimeout (30 s), DefaultSendTimeout (120 s) and
DefaultRecvTimeout (120 s), DefaultRecvBlockSize (2 KiB), the redirect and auth caps
DefaultRedirectMax (6) and DefaultTryAuthMax (6), and FixedHttp1ResponseHeaderMax (64 KiB, the
cap on a fixed HTTP/1.x response header block). The TLS flags — SslFlagServerVerify, SslFlagCnCheck,
SslFlagNotAfterCheck, SslFlagNotBeforeCheck and SslFlagKnownCaCheck — mirror the ones on
Http.SslFlag*, so an app enabling verification on a template and on the request itself uses the
same bit patterns for both.
sceHttp2SetAuthInfoCallback,sceHttp2SetRedirectCallback,sceHttp2SetCookieRecvCallbackandsceHttp2SetCookieSendCallbacktake function pointers with theCdeclcalling convention, so mark the callback method[UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]and give it the signature the class exposes for that entry.
Access-point-aware HTTP client
HttpClient (the high-level type on Networking) brings up libSceNet, libSceSsl
and libSceHttp through Sysmodule.sceSysmoduleLoadModuleInternal first, then calls sceNetInit
before sceNetPoolCreate, then sceSslInit, then sceHttpInit, then sceHttpCreateTemplate. It
installs a per-template callback through sceHttpsSetSslCallback that returns zero for every chain,
so a public host whose certificate is issued by a CA the built-in store does not carry still reaches
the transport layer. Response payloads are checked at the HTTP layer through the status code and
content length, not through the certificate chain.
The extra Http (libSceHttp) surface underneath that client is available for advanced HTTP work:
| Area | Calls |
|---|---|
| URI parsing and building | sceHttpUriParse, sceHttpUriBuild, sceHttpUriMerge, sceHttpUriEscape, sceHttpUriUnescape, sceHttpUriSweepPath |
| Cookies (jar I/O and stats) | sceHttpAddCookie, sceHttpGetCookie, sceHttpCookieExport, sceHttpCookieImport, sceHttpCookieFlush, sceHttpGetCookieStats, sceHttpGetCookieEnabled |
| Authentication and redirect caches | sceHttpAuthCacheFlush, sceHttpRedirectCacheFlush, sceHttpGetAuthEnabled |
| Alternate connection and request forms | sceHttpCreateConnection(tmplId, serverName, scheme, port, keepAlive), sceHttpCreateRequest(connId, method, path, contentLength), sceHttpCreateRequest2(connId, method, path, contentLength) |
| Epoll multiplexer | sceHttpCreateEpoll, sceHttpDestroyEpoll, sceHttpGetEpoll, sceHttpAbortWaitRequest |
| Accounting | sceHttpGetMemoryPoolStats |
SceHttpUriElement is the URL broken into scheme, user name, password, host, path, query, fragment
and port, plus an Opaque flag set when only Path is meaningful. The strings point into the pool
handed to sceHttpUriParse, so anything a caller wants past the pool’s lifetime is copied out
first. Http.UriBuildWith* flags pick which parts sceHttpUriBuild writes into the output URL, or
Http.UriBuildWithAll for every part that is present. MaxUriLength is the longest URL the URI
helpers accept, excluding the terminator (16 KiB minus one).
SceHttpMemoryPoolStats and SceHttpCookieStats are the layouts the two accounting calls fill.
Wider network status
NetCtl (library libSceNetCtl) is what NetworkInfo reads through. Alongside the IPv4 fields
Networking covers, the class also carries IPv6, interface byte
counters, and the address-translation state the box sees:
| Call | What it does |
|---|---|
sceNetCtlGetStateV6(int* state) |
Read the IPv6 connection state. |
sceNetCtlGetInfoV6(int code, SceNetCtlInfoV6*) |
Read one fact about the IPv6 connection into a 256-byte union whose requested member sits at offset zero. The codes are InfoV6IpAddress, InfoV6LinkLocalAddress, InfoV6DefaultRoute, InfoV6PrimaryDns, InfoV6SecondaryDns. |
sceNetCtlGetResultV6(int eventType, int* errorCode) |
Read the error code that accompanies the most recent IPv6 callback event. |
sceNetCtlGetIfStat(SceNetCtlIfStat*) |
Read the byte counters and device kind for the currently selected interface. |
sceNetCtlGetNatInfo(SceNetCtlNatInfo*) |
Read the address-translation state: STUN outcome, NAT type, and mapped address. |
SceNetCtlIpv6Addr carries a forty-six-byte NUL-terminated IPv6 address string and a one-to-128
prefix length. SceNetCtlInfoV6 is the 256-byte read-back union sceNetCtlGetInfoV6 fills.
SceNetCtlIfStat is fifty-six bytes: a device word, four bytes of padding, TxBytes, RxBytes,
then a thirty-two-byte reserved region. SceNetCtlNatInfo reports StunStatus
(NatInfoStunUnchecked, NatInfoStunFailed, NatInfoStunOk), NatType (NatInfoNatType1,
NatInfoNatType2, NatInfoNatType3) and MappedAddr, the address the router presents on the
outside.
V6CallbackMax (8) caps how many IPv6 callbacks may be registered at once.
Raw sockets: extras
The high-level connection and listener wrappers on Networking cover the IPv4
connection path. The Socket (library libSceNet) bindings underneath them also carry:
| Call | What it does |
|---|---|
sceNetGetSockInfo(int s, SceNetSockInfo*, int n, int flags) |
Read one or more per-socket detail entries for an IPv4 socket. |
sceNetGetSockInfo6(int s, SceNetSockInfo6*, int n, int flags) |
Read the same for a socket that may hold an IPv4 or an IPv6 endpoint; the layout carries the IPv6 addresses in place. |
sceNetSendmsg, sceNetRecvmsg(int s, SceNetMsghdr*, int flags) |
Send or receive a scatter/gather message; the header points at an SceNetIovec list. |
sceNetSocketAbort(int s, int flags) |
Cancel a blocking call on a socket so it can be closed promptly. The flags are SocketAbortFlagRcvPreservation, SocketAbortFlagSndPreservation and SocketAbortFlagSndPreservationAgain. |
sceNetEpollAbort(int eid, int flags) |
Unblock a thread waiting in the poller so a server can shut down cleanly — what SocketPoller.Abort calls into. |
sceNetErrnoLoc() |
Return the per-thread error pointer read after a socket call fails. |
SceNetSockInfo and SceNetSockInfo6 carry the debug name, owning process id, socket type, policy
and priority, receive- and send-queue lengths, local and remote endpoints, port and virtual-port
pairs, connection state (the SockInfoState* constants), status bits (the SockInfoF* constants),
throughput and buffer sizes, and drop counters. The status flag SockInfoFIpv6 on SockInfo6
distinguishes an IPv6 endpoint from an IPv4 one. SockInfoTimeWait on sceNetGetSockInfo also
lists sockets in the TIME_WAIT state; SockInfo6Ipv46 on sceNetGetSockInfo6 reports both address
families.
The SockStreamP2p and SockDgramP2p socket types open a peer-to-peer stream or datagram socket
alongside the ordinary SockStream and SockDgram.
The SceNetMsghdr fields are Name (an optional address for a datagram) and its NameLen, the
Iov scatter/gather list of IovLen entries, the ancillary-data Control pointer and its
ControlLen, and the Flags the receive path reports back. Each SceNetIovec is a Base pointer
and its Length.
DNS control
NetDns (library libSceNet) points the resolver at a chosen server pair or drops what it has
cached:
| Call | What it does |
|---|---|
sceNetSetDnsInfo(SceNetDnsInfo*, int flags) |
Set the primary and secondary DNS servers the resolver falls back to. NetDns.AddrMax (2) is how many entries SceNetDnsInfo carries. |
sceNetClearDnsCache(int flags) |
Drop the resolver’s DNS cache. |
SceNetDnsInfo holds Primary and Secondary, each a SceNetInAddr in network byte order.
Byte counters and pool accounting
NetStats (library libSceNet) reports what the interface and the memory pool are doing:
| Call | What it does |
|---|---|
sceNetGetInterfaceStats(SceNetInterfaceStats*, int flags) |
Read the byte counters for the active interface. |
sceNetGetMemoryPoolStats(int memid, SceNetMemoryPoolStats*) |
Read the current, peak and total sizes of a memory pool. |
SceNetInterfaceStats carries TxBytes and RxBytes since the interface came up.
SceNetMemoryPoolStats carries PoolSize, MaxInuseSize and CurrentInuseSize.
Address helpers
Ether and Inet (both libSceNet) parse and format the fixed-form addresses the socket layer
takes and swap between host and network byte order.
| Class | Calls |
|---|---|
Ether |
sceNetEtherStrton(str, out addr) and sceNetEtherNtostr(&addr, str, len) (colon-separated MAC parse and format), sceNetGetMacAddress(&addr, flags) for the interface’s own address. Constants: AddrLen (6), AddrStrLen (18). |
Inet |
sceNetInetNtop(af, src, dst, size) and sceNetInetPton(af, src, dst) (canonical string form for either address family), sceNetHtonl / sceNetHtons / sceNetNtohl / sceNetNtohs (byte-order swap). Constant: InetAddrStrLen (16). |
The resolver’s connect helper
Resolver.sceNetResolverConnect(SceNetResolverConnectInfo*, hostname, port, SceNetResolverConnectParam*)
turns a host name straight into a connected socket. The caller allocates
SceNetResolverConnectInfo (a state block that holds the resolved-name records, the two sockets the
helper opened, the pool it used, and the address it picked from the list) and
SceNetResolverConnectParam (the connect and name-resolution timeouts in microseconds, the retry
count and a flags word), and the helper walks both address families to return a socket already
connected. Alongside it, the IPv6-aware resolver entries are sceNetResolverStartNtoa6 and
sceNetResolverStartAton6, and sceNetResolverAbort cancels any blocking lookup so the resolver
can be closed promptly. Resolver.AbortFlagNtoaPreservation and AbortFlagAtonPreservation keep
the matching pending lookup across the abort.
SceNetResolverInfoEx carries up to ten SceNetResolverAddrEx records tagged with their address
family (Socket.AfInet or Socket.AfInet6), plus counts of how many are IPv4, IPv6 and the total.
Result codes and error handling
Each binding here returns a 32-bit result on the same rule as the rest of the bindings: non-negative
is success; negative is an error. SceResult.ThrowIfFailed on Bindings
raises a ProsperoException carrying the call name and the raw code. The named error constants on
each class — the NetCtlAp.Error* family, NetCtlApDialog.ErrorInternal, and the fixed
Socket.SockInfoStateXxx and Socket.SockInfoFXxx values — cover the codes each service uses.
For the friendly wrappers over the raw sockets, the libSceHttp client, and the network-status
readouts, see Networking.