Understanding the Synergy socket API
The Synergy socket API enables your Synergy applications to directly access sockets, which are the foundation for all TCP/IP computing. Most network computing features have sockets at their core. You can use sockets to help distribute the functionality of your applications across a LAN (or intranet) without having to write C code—which requires significant accommodation on each platform. You can also use sockets to enable your applications to connect to server programs written in other languages, or enable applications in other languages to access your Synergy application logic.
This topic provides basic API information only. If you need a fuller discussion of socket programming, there are many references available on the subject, for both UNIX (where sockets originated) and Winsock (the Microsoft implementation). This topic includes the following:
- Differences between Synergy DBL sockets and Berkeley sockets
- Calling the socket functions
- Handling socket errors
- Synergy socket programming examples
Differences between Synergy DBL sockets and Berkeley sockets
The Synergy socket API documentation is written for users already familiar with Berkeley socket programming. Implementation differences are listed below.
- Synergy DBL sockets support only IP addresses.
- All Synergy DBL socket functions begin with “SS_” or “SS2_”. See %SS2_RECVFROM for details.
- Most Synergy DBL socket functions return a status value. If successful, they return zero; if unsuccessful, they return a system-specific error code, as defined in synsock.def. (The exceptions are %SS_HTONL,%SS_HTONS, %SS_NTOHL, and %SS_NTOHL, which return a converted value.) See the Socket Errors table for a list of common errors.
- The hostent structure associated with the gethost* functions in Berkeley sockets is hidden within the Synergy socket API. Only the h_name and h_addr members of this structure are passed to and from the socket API.
- The sockaddr_in and sockaddr_in6 structures are also hidden within the Synergy socket API. The members sin_port and sin_addr are the only ones used by Synergy DBL sockets. The sin_family member is either AF_INET or AF_INET6, depending on whether the address is IPv4 or IPv6.
- Synergy DBL socket functions return variables in host byte order. However, %SS_HTONL, %SS_HTONS, %SS_NTOHL, and %SS_NTOHS convert host byte order to network byte order (and back again) when sending and receiving portable data over the network.
Unlike conventional C Berkeley socket programming, Synergy DBL socket IP addresses and ports are shared among the various functions in host byte order and should not be converted to network byte order.
- Synergy DBL sockets offer blocking socket calls only.
- Synergy DBL sockets do not support out-of-band data.
- Synergy DBL sockets do not implement these Berkeley socket functions: getsockopt, setsockopt, getprotobynumber, getprotobyname, shutdown. (Although you cannot call these functions, %SS_CLOSE does use the shutdown function before closing the socket, and %SS_SOCKET sets the SO_LINGER and TCP_NODELAY setsockopt options for SS_SOCK_STREAM sockets, and sets the SO_KEEPALIVE setsockopt option for all sockets.)
Calling the socket functions
Your system must be properly configured for TCP/IP network communications. Consult your system documentation for information on configuring your system for TCP/IP networking.
To use the Synergy socket API, include the file DBLDIR:synsock.def in your program using the .INCLUDE statement. Synsock.def, which is included in your distribution, contains definitions of the socket types, flags, IP addresses, and errors.
On Synergy .NET, make sure that you explicitly close channels and free global memory handles. Implicit program or AppDomain shutdown does not ensure clean socket termination because memory handles, channels, and sockets are not closed on AppDomain shutdown—only on application shutdown.
The Synergy socket API supports both IPv4-formatted addresses (e.g., 204.33.191.3) and IPv6-formatted addresses (e.g., 2001:db8::ff00:42:8329). The original SS_ implementation is designed for IPv4-formatted IP addresses and cannot accommodate IPv6-formatted addresses. Consequently, in 10.3.3 we added a second set of routines, which begin SS2_, for IPv6 support. Not all SS_ functions have a corresponding SS2_ function—only those that pass IP addresses (that is, those with an in_addr argument) require a corresponding function. If your application will be connecting to an IPv6 socket, use the SS2_ functions when they are available; otherwise, use the SS_ functions. Some SS2_ functions support both IPv4 and IPv6.
Even if you don’t pass the optional in_addr arguments to %SS_ACCEPT and %SS_RECVFROM, you should not use these functions for IPv6. Always use %SS2_ACCEPT and %SS2_RECVFROM for IPv6. |
Sometimes a socket will terminate unexpectedly or be abruptly closed by the program. If this happens while %SS_SELECT is waiting for a packet, it returns immediately to the calling program without indicating that an error has occurred, making it appear that a message has arrived. Similarly, if a socket is lost during an %SS_RECV call using SS_MSG_PEEK, %SS_RECV will return without indicating an error condition. However, %SS_RECV does not modify the buffer (the bytes_received argument, which contains the peek length) with the length of the message; it will be 0, which is your clue that something is amiss.
We recommend that you use %SS_RECV to test the return status and to verify the peek length is not 0, before calling %SS_RECVBUF to attempt to retrieve the message. If the socket is lost during the call to %SS_RECVBUF, an error is returned.
Handling socket errors
Two types of errors are possible in the Synergy socket API: signaled and returned.
Signaled errors are caused by improper calls to socket functions. These are fatal errors and can occur for the following reasons:
- Missing or incorrect number of arguments
- Passing literals when a routine expects writable arguments
- Passing values that are invalid or out of range
Returned errors are returned as a status from a Synergy DBL socket function and reflect the state of the socket subsystem. All Synergy DBL socket functions return error values, except for the data-conversion functions %SS_HTONL, %SS_HTONS, %SS_NTOHL, and %SS_NTOHS (which return the converted value). These errors should be trapped and handled appropriately. Some of the causes of returned errors are as follows:
- Socket is not valid.
- Socket is not connected.
- Socket is already connected.
- Network subsystem failed.
- Network is unreachable.
- Connection timed out.
For error mnemonics, numbers, and descriptions see the Socket Errors table. Errors not listed in the table are mapped to SS_EUNKNOWN. The errors returned by the individual functions are listed on the function syntax pages.
We recommend that you handle all possible errors for each function even though not all platforms return all errors. This way, your application will be portable to other platforms. |
Some Synergy DBL socket errors use proprietary numeric values mapped from the system-level error codes. In these cases, you can access the system-level error code by calling %SYSERR immediately after calling the particular socket function. Calling %SYSERR is especially useful when a socket function returns the error SS_EUNKNOWN; this way you can look up the error code specific to the operating system to get more information about the problem. We also recommend calling %SYSERR when a socket function returns SS_ENULL.
When a function returns SS_EINTR, you should retry the current operation.
Mnemonic |
Description |
Number |
---|---|---|
SS_EACCES |
Varies by function:
|
10013 |
SS_EADDRINUSE |
IP address or port is in use. |
10048 |
SS_EADDRNOTAVAIL |
Address is not available from local machine. |
10049 |
SS_EBADF |
Socket parameter is not valid. |
10009 |
SS_EBADSTRING |
Bad character string provided |
3 |
SS_ECONNABORTED |
Virtual circuit aborted due to time-out or other failure |
10053 |
SS_ECONNREFUSED |
Attempt to connect was rejected. |
10061 |
SS_ECONNRESET |
Varies by function:
|
10054 |
SS_EDESTADDRREQ |
Varies by function:
|
10039 |
SS_EDISCON |
Shutdown in progress |
10101 |
SS_EFAULT |
Bad address |
10014 |
SS_EHOSTDOWN |
Networking subsystem is not up. |
10064 |
SS_EINTR |
Call was interrupted by a signal. Your application should retry the operation. |
10004 |
SS_EINVAL |
Varies by function:
|
10022 |
SS_EISCONN |
Socket is already connected. |
10056 |
SS_EMFILE |
No more descriptors available |
10024 |
SS_EMSGSIZE |
Buffer too small to receive entire datagram, or buffer containing datagram too large for sending. |
10040 |
SS_ENETDOWN |
Network subsystem has failed. |
10050 |
SS_ENETRESET |
Connection must be reset. |
10052 |
SS_ENETUNREACH |
Network is unreachable. |
10051 |
SS_ENFILE |
System’s table of open files is full. |
17002 |
SS_ENOBUFS |
No buffer space available |
10055 |
SS_ENOMEM |
Insufficient memory for operation |
17001 |
SS_ENOTCONN |
Socket not connected |
10057 |
SS_ENOTSOCK |
Socket descriptor is not a socket. |
10038 |
SS_ENULL |
Call failed. No variables loaded. Use %SYSERR to obtain the system error. |
2 |
SS_EOPNOTSUPP |
Socket is not of a type that supports this function call. |
10045 |
SS_ETIMEDOUT |
The connection timed out. The socket must be closed and a new one created before another connection can be made. |
10060 |
SS_EUNKNOWN |
Unknown error returned. Use %SYSERR to obtain the system error. |
1 |
SS_HOST_NOT_FOUND |
Authoritative answer host not found |
11001 |
SS_NO_DATA |
Valid name, no data record of requested type |
11004 |
SS_NO_RECOVERY |
Nonrecoverable error occurred. |
11003 |
SS_NOTINITIALISED |
System not initialized. You should never get this error, but if you do, call Synergex Support. (Windows only) |
10093 |
SS_TRY_AGAIN |
Nonauthoritive host not found or SERVERFAIL |
11002 |
Synergy socket programming examples
An example SMTP (Simple Mail Transport Protocol) e-mail sender called SMTPmail.dbl is available from Synergy CodeExchange in the Synergex Resource Center. (There is also an SMTP example program in the dbl\examples directory of the 32-bit Synergy/DE distribution, but the example in CodeExchange is more extensive.)
Below is a basic TCP client/server programming example. The client connects to a server and sends it a message string. The server receives the message string and prints it to the console. Since this message string is variable in length, the program uses the SS_MSG_PEEK option of %SS_RECV. Following the IPv4 example, is an IPv6 example of the same program.
IPv4 example
;*----------------------------------------------------------------------- ; Synergy Socket API - TCP Client Example - IPv4 ; ; Module: client.dbl ;*----------------------------------------------------------------------- .include "DBLDIR:synsock.def" .define SERVERPORT ,1234 .define TTCHN ,100 record err ,i4 socket ,D_ADDR server_in_addr ,i4 server_name ,a40 group packet length ,i4 message ,a256 endgroup proc xcall flags(7004020,1) open(TTCHN, o, "tt:") ;Create the socket for the client err = %ss_socket(socket, SS_SOCK_STREAM) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Get the server name to connect to writes(TTCHN, "Enter server to connect to:") reads(TTCHN, server_name) ;Get IP address for the server we’re connecting to err = %ss_getaddrinfo(server_name, server_in_addr) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Connect to the server err = %ss_connect(socket, SERVERPORT, server_in_addr) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Get message to send to server writes(TTCHN, "Enter message to send to server:") reads(TTCHN, packet.message) ;Length of packet to send including packet length field, converted to ; network byte order for portability packet.length = %ss_htonl(4 + %trim(packet.message)) ;Send the packet err = %ss_sendbuf(socket, packet(1:%ss_ntohl(packet.length))) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Close the socket err = %ss_close(socket) if (err .ne. SS_SUCCESS) goto unexpected_failure goto program_shutdown unexpected_failure, writes(TTCHN, "Unexpected failure in call to socket function.") writes(TTCHN, "Error code returned was: " + %string(err)) writes(TTCHN, "Program terminated.") program_shutdown, close(TTCHN) stop end ;*----------------------------------------------------------------------- ; Synergy Socket API - TCP Server Example - IPv4 ; ; Module: server.dbl ;*----------------------------------------------------------------------- .include "DBLDIR:synsock.def" .define SERVERPORT ,1234 .define TTCHN ,100 record err ,i4 socket ,D_ADDR client_socket ,D_ADDR bytes_received ,i4 packet_length ,i4 packet_length_peek ,i4 group packet length ,i4 message ,a256 endgroup proc xcall flags(7004020,1) open(TTCHN, o, "tt:") ;Create the server’s socket err = %ss_socket(socket, SS_SOCK_STREAM) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Bind server port and use any network interface in host err = %ss_bind(socket, SERVERPORT, SS_INADDR_ANY) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Listen for connections err = %ss_listen(socket) if (err .ne. SS_SUCCESS) goto unexpected_failure writes(TTCHN, "Server is ready to accept connection from client...") writes(TTCHN, "") ;Accept a connection from a client err = %ss_accept(socket, client_socket) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Peek into packet to get packet length err = %ss_recv(client_socket, ^A(packet_length_peek), bytes_received, SS_MSG_PEEK) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Receive packet based on the packet obtained with peek err = %ss_recvbuf(client_socket, packet(1:%ss_ntohl(packet_length_peek))) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Close the client’s socket descriptor err = %ss_close(client_socket) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Close the server’s socket descriptor err = %ss_close(socket) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Print out the client’s message writes(TTCHN, "Client’s message is as follows:") writes(TTCHN, packet.message(1:%ss_ntohl(packet.length)-4)) goto program_shutdown unexpected_failure, writes(TTCHN, "Unexpected failure in call to socket function.") writes(TTCHN, "Error code returned was: " + %string(err)) writes(TTCHN, "Program terminated.") program_shutdown, close(TTCHN) stop end
;*----------------------------------------------------------------------- ; Synergy Socket API - TCP Client Example - IPv6 ; ; Module: client.dbl ;*----------------------------------------------------------------------- .include "DBLDIR:synsock.def" .define SERVERPORT ,1234 .define TTCHN ,100 record err ,i4 socket ,D_ADDR server_in_addr ,[#][#]byte server_name ,a40 group packet length ,i4 message ,a256 endgroup proc xcall flags(7004020,1) open(TTCHN, o, "tt:") ;Create the socket for the client err = %ss_socket(socket, SS_SOCK_STREAM, 1) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Get the server name to connect to writes(TTCHN, "Enter server to connect to:") reads(TTCHN, server_name) ;Get IP address for the server we're connecting to err = %ss2_gethostbyname(server_name, server_in_addr) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Connect to the server err = %ss2_connect(socket, SERVERPORT, server_in_addr) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Get message to send to server writes(TTCHN, "Enter message to send to server:") reads(TTCHN, packet.message) ;Length of packet to send including packet length field, converted to ; network byte order for portability packet.length = %ss_htonl(4 + %trim(packet.message)) ;Send the packet err = %ss_sendbuf(socket, packet(1:%ss_ntohl(packet.length))) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Close the socket err = %ss_close(socket) if (err .ne. SS_SUCCESS) goto unexpected_failure goto program_shutdown unexpected_failure, writes(TTCHN, "Unexpected failure in call to socket function.") writes(TTCHN, "Error code returned was: " + %string(err)) writes(TTCHN, "Program terminated.") program_shutdown, close(TTCHN) stop end ;*----------------------------------------------------------------------- ; Synergy Socket API - TCP Server Example - IPv6 ; ; Module: server.dbl ;*----------------------------------------------------------------------- .include "DBLDIR:synsock.def" .define SERVERPORT ,1234 .define TTCHN ,100 record err ,i4 socket ,D_ADDR client_socket ,D_ADDR bytes_received ,i4 packet_length ,i4 packet_length_peek ,i4 group packet length ,i4 message ,a256 endgroup idx ,i4 server_in_addr ,[#]byte proc xcall flags(7004020,1) open(TTCHN, o, "tt:") ;Create the server's socket err = %ss_socket(socket, SS_SOCK_STREAM, 1) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Bind server port and use any network interface in host server_in_addr = new byte[16] for idx from 1 thru 16 server_in_addr[idx] = 0 err = %ss2_bind(socket, SERVERPORT, server_in_addr) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Listen for connections err = %ss_listen(socket) if (err .ne. SS_SUCCESS) goto unexpected_failure writes(TTCHN, "Server is ready to accept connection from client...") writes(TTCHN, "") ;Accept a connection from a client err = %ss2_accept(socket, client_socket) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Peek into packet to get packet length err = %ss_recv(client_socket, ^A(packet_length_peek), bytes_received, SS_MSG_PEEK) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Receive packet based on the packet length obtained peek err = %ss_recvbuf(client_socket, packet(1:%ss_ntohl(packet_length_peek))) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Close the server's socket descriptor err = %ss_close(socket) if (err .ne. SS_SUCCESS) goto unexpected_failure ;Print out the client's message writes(TTCHN, "Client's message is as follows:") writes(TTCHN, packet.message(1:%ss_ntohl(packet.length)-4)) goto program_shutdown unexpected_failure, writes(TTCHN, "Unexpected failure in call to socket function.") writes(TTCHN, "Error code returned was: " + %string(err)) writes(TTCHN, "Program terminated.") program_shutdown, close(TTCHN) stop end