Jump to content

SO32DLL.LIB

From EDM2

SO32DLL.LIB is the import library for the BSD socket API provided by IBM TCP/IP for OS/2. It is distributed with the IBM OS/2 Developer's Toolkit and provides linker stubs for socket, accept, bind, connect, listen, recv, send, select, and all related socket functions. The companion library TCP32DLL.LIB provides name resolution (gethostbyname, getservbyname, etc.), address conversion (inet_addr), and the DNS resolver (res_*). Both libraries are needed for a complete TCP/IP application.

The library contains no executable code. It is an OMF import library mapping function names (and ordinals) to SO32DLL.DLL, the OS/2 socket DLL.

File size in the OS/2 Warp 4.52 Toolkit 4.5: 9,728 bytes (2000-10-16).

API Coverage

SO32DLL.LIB exports approximately 127 symbols covering six functional areas:

Area Functions
Socket lifecycle socket, soclose, shutdown, soabort, so_cancel, sock_init
Server (passive) bind, listen, accept
Client (active) connect
Addressing getsockname, getpeername
Send / receive send, recv, sendto, recvfrom, sendmsg, recvmsg, readv, writev
Options and control getsockopt, setsockopt, select, bsdselect, ioctl
Error handling sock_errno, psock_errno
Miscellaneous gethostid, getinetversion

Each function is exported in three forms to support both OS/2 calling conventions — see #Calling convention variants below.

Calling Convention Variants

Every function in SO32DLL.LIB is present in three export forms:

Form Example Convention Used by
Lowercase / mixed-case accept _System IBM C Set++, VisualAge C++, OpenWatcom
Uppercase MSC suffix ACCEPTMSC __cdecl EMX/GCC, Borland C
Underscore + lowercase + MSC suffix _acceptMSC __cdecl Alternate GCC form

The IBM TCP/IP header files (sys\socket.h, utils.h) select the correct form via preprocessor conditionals. Application code calls accept() and the headers map it to the right export automatically.

Two functions share a single underlying export:

  • sendmsg and recvmsg are backed by _sendrecvmsgMSC with a direction flag.
  • readv and writev are backed by _readvwritevMSC with a direction flag.

Socket Lifecycle

Initialization

sock_init()
Must be called once per process before any other socket function. Initializes the TCP/IP stack interface. On UNIX, this call does not exist; on OS/2, omitting it causes subsequent socket calls to fail silently or trap. Returns 0 on success, non-zero on failure (consult sock_errno()).
SOCK_INITMSC
__cdecl variant of sock_init, selected automatically by the headers under GCC.

Creating a socket

socket(domain, type, protocol)
Allocates a new socket descriptor. Parameters:
  • domain — address family; AF_INET for IPv4.
  • type — SOCK_STREAM (TCP, reliable byte stream) or SOCK_DGRAM (UDP, unreliable datagram) or SOCK_RAW (raw IP, requires elevated privilege).
  • protocol — set to 0 to let the system choose; or IPPROTO_TCP / IPPROTO_UDP explicitly.
Returns a socket descriptor (a non-negative integer) or −1. Unlike file descriptors, OS/2 socket descriptors are managed by SO32DLL.DLL and cannot be passed to DosRead/DosWrite or close().

Closing a socket

soclose(s)
Gracefully closes a socket. Performs the TCP four-way close handshake for SOCK_STREAM sockets. Use soclose(), not close() — OS/2 socket descriptors are not file descriptors and the C runtime close() does not reach SO32DLL.DLL.
shutdown(s, how)
Partially closes the connection without releasing the socket descriptor:
  • how = 0 — no further receives (SHUT_RD).
  • how = 1 — no further sends; sends a FIN (SHUT_WR).
  • how = 2 — both directions (SHUT_RDWR).
The socket descriptor remains valid until soclose() is called.
soabort(s)
Closes the socket immediately by sending a TCP RST segment. The remote end receives a connection reset error. Use instead of soclose() when a graceful close is not possible (e.g. the remote end is unresponsive).
so_cancel(s)
Cancels a blocking socket call (recv, send, accept, connect, select) that is currently blocking on another thread. The blocked call returns with an error. Used in multithreaded servers to implement graceful shutdown.

Server-Side API

Binding to a local address

bind(s, name, namelen)
Assigns a local IP address and port to a socket. name is a pointer to a struct sockaddr_in. Set sin_addr.s_addr = INADDR_ANY to accept connections on all local interfaces. Set sin_port = 0 to have the OS assign an ephemeral port.

Common pattern for a server:

struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family      = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port        = htons(8080);
bind(s, (struct sockaddr *)&addr, sizeof(addr));

If bind() fails with SOCEINVAL or SOCEADDRINUSE, the port is already in use; set SO_REUSEADDR with setsockopt() before binding to reclaim it.

Listening for connections

listen(s, backlog)
Marks the socket as a passive listener. backlog is the maximum number of connections that may be pending in the kernel accept queue; typical values are 5–128. Does not block. Returns 0 on success.

Accepting a connection

accept(s, addr, addrlen)
Blocks until an incoming TCP connection arrives on a listen-ed socket, then returns a new socket descriptor for that connection. The original socket s remains open and continues listening. addr is filled with the remote client's IP and port.

Multithreaded servers typically call accept() in a loop and spin up a worker thread per accepted connection:

while (1) {
    struct sockaddr_in client;
    int len = sizeof(client);
    int cs = accept(ls, (struct sockaddr *)&client, &len);
    if (cs < 0) break;
    /* hand cs to a worker thread */
}

Client-Side API

Connecting to a server

connect(s, name, namelen)
For SOCK_STREAM: initiates the TCP three-way handshake with the remote address in name. Blocks until the connection is established or an error occurs (e.g. SOCECONNREFUSED, SOCETIMEOUT). For SOCK_DGRAM: sets the default destination for send() calls (no actual handshake).

To make connect() non-blocking, set FIONBIO with ioctl() before calling; the call returns immediately with SOCEINPROGRESS and completion is detected with select() on the write set.

Addressing

getsockname(s, name, namelen)
Fills name with the local IP address and port bound to s. Useful after bind(s, ..., port=0) to discover the ephemeral port the OS assigned.
getpeername(s, name, namelen)
Fills name with the remote address of a connected socket. Equivalent to caching the address from accept(), but works after the fact.

Sending and Receiving

Connected sockets (TCP)

send(s, buf, len, flags)
Sends up to len bytes from buf. Returns the number of bytes actually sent, which may be less than len — callers must loop. Common flags: MSG_OOB (send out-of-band data), MSG_DONTWAIT (non-blocking for this call only).
recv(s, buf, len, flags)
Receives up to len bytes into buf. Blocks until data arrives or the connection closes (returns 0 on orderly close). Common flags: MSG_PEEK (inspect without consuming), MSG_OOB (receive out-of-band), MSG_WAITALL (wait for the full len bytes).

Datagram sockets (UDP)

sendto(s, buf, len, flags, to, tolen)
Sends a datagram to the address in to. Each call is one discrete UDP packet.
recvfrom(s, buf, len, flags, from, fromlen)
Receives one UDP datagram and fills from with the sender's address. Returns the datagram size; bytes beyond len are discarded.

Scatter/gather I/O

sendmsg(s, msg, flags)
Sends data described by a struct msghdr containing an array of iovec buffers. Allows sending from non-contiguous memory regions in a single system call.
recvmsg(s, msg, flags)
Receives into a scatter array of iovec buffers. Also receives ancillary data (e.g. IP_RECVDSTADDR) via msg_control.
readv(s, iov, iovcnt)
BSD scatter read: reads into an array of iovcnt buffers described by iov in a single call.
writev(s, iov, iovcnt)
BSD gather write: sends from an array of buffers in a single call. Equivalent to sendmsg with flags = 0.

Socket Options

setsockopt(s, level, optname, optval, optlen)
Sets a socket option. level is either SOL_SOCKET (generic) or a protocol level such as IPPROTO_TCP.
getsockopt(s, level, optname, optval, optlen)
Reads a socket option into the buffer at optval.

Common options at SOL_SOCKET:

Option Type Effect
SO_REUSEADDR int (bool) Allow re-binding a port in TIME_WAIT. Set before bind() on server sockets.
SO_KEEPALIVE int (bool) Enable TCP keepalive probes; detects dead connections after several minutes.
SO_LINGER struct linger Control soclose() behaviour: wait for data to drain (l_onoff=1, l_linger=seconds) or reset immediately (l_linger=0).
SO_RCVBUF / SO_SNDBUF int Kernel receive/send buffer size in bytes. Increase for high-throughput applications.
SO_ERROR int Reads and clears the pending socket error (used after a non-blocking connect()).
SO_TYPE int Returns the socket type (SOCK_STREAM, SOCK_DGRAM).

Common option at IPPROTO_TCP:

Option Type Effect
TCP_NODELAY int (bool) Disable Nagle algorithm. Send small packets immediately without waiting to coalesce. Use for interactive / low-latency protocols.

Multiplexed I/O

select(nfds, readfds, writefds, exceptfds, timeout)
Monitors up to FD_SETSIZE sockets for readability, writability, or exceptional condition. nfds is one more than the highest socket descriptor to check. timeout is a struct timeval; pass NULL to block indefinitely, or a zero timeval for an immediate poll.
Important OS/2 restriction: socket descriptors are not OS/2 file handles. select() can only monitor sockets from SO32DLL. It cannot monitor OS/2 pipes, named pipes, or file handles in the same call.
bsdselect(nfds, readfds, writefds, exceptfds, timeout)
Identical to select(); provided for source-level compatibility with code that calls bsdselect() explicitly on BSD systems.

Typical server loop using select() for multiple clients:

fd_set readset;
int maxfd = listenfd;

FD_ZERO(&readset);
FD_SET(listenfd, &readset);

while (select(maxfd+1, &readset, NULL, NULL, NULL) > 0) {
    if (FD_ISSET(listenfd, &readset)) {
        int cs = accept(listenfd, NULL, NULL);
        FD_SET(cs, &readset);
        if (cs > maxfd) maxfd = cs;
    }
    /* check each client fd ... */
}

I/O Control

ioctl(s, cmd, arg)
Performs device-level control on a socket. Common commands (defined in sys\ioctl.h):
Command Argument Effect
FIONBIO int * (0/1) Set or clear non-blocking mode. When set, accept/connect/recv/send return immediately with SOCEWOULDBLOCK instead of blocking.
FIONREAD int * Number of bytes available to read without blocking.
FIOASYNC int * (0/1) Enable asynchronous I/O (SIGIO equivalent); rarely used on OS/2.

Non-blocking example:

int nb = 1;
ioctl(s, FIONBIO, (char *)&nb);   /* set non-blocking */

Error Handling

sock_errno()
Returns the last socket error code for the calling thread. Always call this immediately after a failed socket call — it is per-thread and is overwritten by the next socket call. Error codes are defined in nerrno.h with the SOCE prefix:
Code Meaning
SOCEWOULDBLOCK Non-blocking call would have blocked
SOCENOTSOCK Descriptor is not a socket
SOCECONNREFUSED Remote end actively refused connection
SOCECONNRESET Connection reset by peer (TCP RST)
SOCECONNABORTED Connection aborted (software-initiated reset)
SOCETIMOUT Connection timed out
SOCEINPROGRESS Non-blocking connect() in progress
SOCEALREADY Non-blocking connect() already in progress
SOCEADDRINUSE Address/port already in use
SOCEADDRNOTAVAIL Cannot assign requested address
SOCENETUNREACH Network unreachable
SOCEHOSTUNREACH Host unreachable
SOCEMFILE No free socket descriptors
SOCEPIPE Write on a closed socket (broken pipe)
psock_errno(msg)
Prints msg: error description\n to stderr using the text for sock_errno(). Equivalent to perror() for socket errors. Useful for quick diagnostics.

Note: do not use the C runtime errno for socket errors on OS/2. Socket errors are stored separately by SO32DLL.DLL in a per-thread slot, not in the C runtime errno variable.

Miscellaneous

gethostid()
Returns the primary IP address of the local machine as a 32-bit integer in network byte order. Equivalent to resolving the local hostname.
getinetversion(buf)
Fills buf with the IBM TCP/IP version string (e.g. "IBM TCP/IP Version 4.21"). Useful for diagnostics and about boxes.
addsockettolist(s) / removesocketfromlist(s) / getsocketfromlist(index)
Manage an internal per-process socket list maintained by SO32DLL. Not normally called by application code; used internally by the IBM TCP/IP runtime and some IBM utilities.
set_errno(err)
Sets the socket errno for the current thread. Used by wrapper libraries that need to synthesize socket errors.

Usage

SO32DLL.LIB must be linked alongside TCP32DLL.LIB and OS2386.LIB for any TCP/IP application:

IBM VisualAge C++ / ILINK

icc -O2 -Gm -c myapp.c
ilink /PM:VIO myapp.obj os2386.lib so32dll.lib tcp32dll.lib

OpenWatcom (wlink)

wcl386 -bt=os2 -mf -c myapp.c
wlink system os2v2 file myapp.obj library os2386.lib library so32dll.lib library tcp32dll.lib

EMX/GCC

gcc -Zomf -c myapp.c
gcc -Zomf -o myapp.exe myapp.o os2386.lib so32dll.lib tcp32dll.lib

Include files

Header Contents
types.h BSD type aliases: u_char, u_short, u_long, u_int. Must be included before sys\socket.h.
sys\socket.h struct sockaddr, struct sockaddr_in, AF_*, SOCK_*, SOL_SOCKET, SO_*; prototypes for all socket functions.
netinet\in.h struct in_addr, IPPROTO_*, INADDR_ANY, INADDR_NONE, INADDR_BROADCAST.
sys\select.h fd_set, FD_ZERO, FD_SET, FD_CLR, FD_ISSET, FD_SETSIZE (= 64 on OS/2).
sys\ioctl.h FIONBIO, FIONREAD, FIOASYNC.
nerrno.h SOCE* error codes.
utils.h sock_errno(), psock_errno(), htons(), htonl(), ntohs(), ntohl() macros.

Always include types.h first; many other TCP/IP headers depend on its type definitions.

OS/2-Specific Differences from UNIX

Behaviour UNIX OS/2 (SO32DLL)
Library init Not needed sock_init() required before first socket call
Closing a socket close(fd) soclose(s); never use C close()
Socket descriptors Share the fd namespace with files Separate namespace; cannot use DosRead/DosWrite
select() mixing Can mix files and sockets Sockets only; cannot mix with OS/2 file handles
Thread-safety of errors errno is per-thread sock_errno() is per-thread; errno is unrelated
FD_SETSIZE Typically 1024 64 on OS/2
Non-blocking I/O O_NONBLOCK via fcntl() FIONBIO via ioctl(); no fcntl()
Abort with RST SO_LINGER with l_linger=0 soabort(s) or SO_LINGER

Version History

Version IBM TCP/IP release Date Notes
1.0 IBM TCP/IP for OS/2 2.0 ~1993 First 32-bit release; SO32DLL split from TCP32DLL established. Core BSD 4.3 socket API.
2.0 IBM TCP/IP for OS/2 3.0 (Warp 3) 1994 Added bsdselect, so_cancel, improved thread safety.
3.0 IBM TCP/IP for OS/2 4.1 (Warp 4) 1996 sock_errno() made fully per-thread. sendmsg/recvmsg added. FD_SETSIZE raised to 64.
4.21 IBM TCP/IP for OS/2 4.21 / OS/2 Warp 4.52 2000 Final IBM release. File size: 9,728 bytes (2000-10-16).

See Also