SO32DLL.LIB
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:
sendmsgandrecvmsgare backed by_sendrecvmsgMSCwith a direction flag.readvandwritevare backed by_readvwritevMSCwith 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__cdeclvariant ofsock_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_INETfor IPv4.type—SOCK_STREAM(TCP, reliable byte stream) orSOCK_DGRAM(UDP, unreliable datagram) orSOCK_RAW(raw IP, requires elevated privilege).protocol— set to 0 to let the system choose; orIPPROTO_TCP/IPPROTO_UDPexplicitly.
- 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/DosWriteorclose().
Closing a socket
soclose(s)- Gracefully closes a socket. Performs the TCP four-way close handshake for
SOCK_STREAMsockets. Usesoclose(), notclose()— OS/2 socket descriptors are not file descriptors and the C runtimeclose()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.
nameis a pointer to astruct sockaddr_in. Setsin_addr.s_addr = INADDR_ANYto accept connections on all local interfaces. Setsin_port = 0to 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.
backlogis 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 socketsremains open and continues listening.addris 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 inname. Blocks until the connection is established or an error occurs (e.g.SOCECONNREFUSED,SOCETIMEOUT). ForSOCK_DGRAM: sets the default destination forsend()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
namewith the local IP address and port bound tos. Useful afterbind(s, ..., port=0)to discover the ephemeral port the OS assigned.
getpeername(s, name, namelen)- Fills
namewith the remote address of a connected socket. Equivalent to caching the address fromaccept(), but works after the fact.
Sending and Receiving
Connected sockets (TCP)
send(s, buf, len, flags)- Sends up to
lenbytes frombuf. Returns the number of bytes actually sent, which may be less thanlen— 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
lenbytes intobuf. 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 fulllenbytes).
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
fromwith the sender's address. Returns the datagram size; bytes beyondlenare discarded.
Scatter/gather I/O
sendmsg(s, msg, flags)- Sends data described by a
struct msghdrcontaining an array ofiovecbuffers. Allows sending from non-contiguous memory regions in a single system call.
recvmsg(s, msg, flags)- Receives into a scatter array of
iovecbuffers. Also receives ancillary data (e.g.IP_RECVDSTADDR) viamsg_control.
readv(s, iov, iovcnt)- BSD scatter read: reads into an array of
iovcntbuffers described byiovin a single call.
writev(s, iov, iovcnt)- BSD gather write: sends from an array of buffers in a single call. Equivalent to
sendmsgwithflags = 0.
Socket Options
setsockopt(s, level, optname, optval, optlen)- Sets a socket option.
levelis eitherSOL_SOCKET(generic) or a protocol level such asIPPROTO_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_SETSIZEsockets for readability, writability, or exceptional condition.nfdsis one more than the highest socket descriptor to check.timeoutis astruct 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 callsbsdselect()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.hwith theSOCEprefix:
| 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\nto stderr using the text forsock_errno(). Equivalent toperror()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
bufwith 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). |