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

The Synergy socket API documentation is written for users already familiar with Berkeley socket programming. Implementation differences are listed below.

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 SS_ vs SS2_ functions

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.

Note

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.

Handling lost sockets

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:

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:

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.

Tip

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.

Socket Errors

Mnemonic

Description

Number

SS_EACCES

Varies by function:

  • The requested address is protected, and the current user has inadequate permission to access it. (BIND)
  • The requested address is a broadcast address, but the appropriate flag was not set. (SEND, SENDBUF, SENDTO)

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:

  • Virtual circuit was reset by the remote side. (RECV, RECVBUF, RECVFROM, SEND, SENDBUF, SENDTO)
  • The remote peer has accepted and then aborted the connection before the connect call could complete. (CONNECT)

10054

SS_EDESTADDRREQ

Varies by function:

  • A destination address is required. (CONNECT, SENDTO)
  • The socket has not been bound with %SS_BIND or %SS2_BIND. (LISTEN, SEND, SENDBUF)

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:

  • The socket is not accepting connections. (ACCEPT)
  • The socket is already bound to an address. (BIND)
  • The socket has not been bound with %SS_BIND or %SS2_BIND. (GETSOCKNAME, LISTEN, RECV, RECVBUF)
  • The socket has been shut down. (GETSOCKNAME)
  • The socket is already connected. (LISTEN)

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 

IPv6 example

;*-----------------------------------------------------------------------
; 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