Send Data

Essentials

This section summarises the content on this page (read on for the full tutorial):

In this tutorial we are considering send over a TCP connection. If you are writing a UDP (connectionless) application you need to look at the sendto request but the principles remain similar to those listed below.

On UNIX oriented APIs send is psynomonous with write (although write does not support flags, see below). Write has the potential to allow applications to be written as if handling file IO, more an aim of the TLI and XTI APIs that also support an open request in place of socket request.

Send parameter definition

The parameters of a typical send request are as follows:

Socket numberThe send request is on a per socket basis, you must provide the socket number thus identifying the connection for which the request is being made.
Data addressThis is the address of an application owned area of storage containing the data to be sent. With interpreted languages this will typically be the name of a variable containing the data to be sent.
Data lengthThe length of the data to be sent.
FlagsAPI specific flags to modify the basic send function.

Returns: A numeric code as follows:

PositiveThe length of the data sent, normally the same as the length requested.
0 (zero)Unlikely, but should be handled same as a positive return code.
NegativeUsually -1 indicating an error occurred, refer to the ERRNO value for an explanation of the error. Some codes indicate that the connection may be lost, in this case due to an error.

Normally a send request will be accepted across the API with the data to be sent being copied into a system buffer and a return code equal to the length of the data returned.

Things that can go wrong are:

Note: there is no way to ensure that the partner application has received your sent data unless that partner application returns an acknowledgement.

Send error handling

When things start to go wrong, especially if sending large buffers of data, the Stack may choose to accept only part of the data being passed in the request. When the return code is not equal to the length of the data that was to be sent then the residual data must be sent via a new send request.

Prefixing the data with a header that provides the length of data and the address or offset to that data simplifies error recovery within the send routine. This routine then simply increments the address or offset and decrements the length by the number of bytes accepted on the request. The same routine can then be passed this updated buffer to issue a new send.

When should an application re-issue the send? Simple applications could wait a small period of time and retry with subsequent failures leading to the connection being shutdown. A better solution is to use the Select function to receive notification when send activity is possible.

Note that in blocking mode (the default) a send can also hang the task indefinitely pending the stack's ability to process the request. When the socket is moved to non-blocking mode the same request will complete with return code -1 (typically) and errno 3406 EWOULDBLOCK (or EAGAIN on some stacks). This can be treated the same as a partial send, in this case, one where 0 bytes were successfully transmitted.

 

Continue with the Tutorial

Receive
Data
Next
Sockets
Contents
Contents
Client
Connections
Prev

 

Example Sends

UNIX
C
Lua
ooREXX
IBM TSO
REXX
IBM HPNS
HLASM

C - Generic UNIX

This example illustrates a simple send function that is passed a connection block (structure) containing the socket file descriptor, work areas and a buffer containing the data to be sent.

Note how using a buffer structure that includes a header to prefix the data simplifies processing when only part of the data can be processed by a send api call. When rc equals the number of bytes sent, we need only increase the value of pad by rc to skip the bytes already sent and reduce dlen, the length of data to sent, by the same amount.

You can see the full program here


/***
  Send all the data in the send buffer associated with the
  passed connection
***/
int doSend( struct snys_conn *pConn) {
  struct snys_glob *pGlob;
  struct snys_buf *pBuf;
  int nBytes = 0;
  int nTotal;
  ssize_t rc = 0;

  pGlob = pConn->pGlob;
  pBuf = pConn->pSndBuf;
  nTotal = pBuf->dlen;

  while ( nBytes < nTotal) {
     rc = send( pConn->fd, pBuf->data + pBuf->pad, (size_t)pBuf->dlen, 0);
     if ( rc < 0) {
        doMessage( pGlob, 2, "send", errno, strerror(errno));
        return -1;
     }
     nBytes += rc;
     doMessage( pGlob, 5, nBytes, nTotal, &pConn->serverName);
     if ( pConn->status & conn_status_dump)
        doDump( pGlob, pBuf->data+pBuf->pad, rc);
     pBuf->pad += rc;
     pBuf->dlen -= rc;
  }
  return 0;
}

Lua

This simple send routine sends the data held in Lua variable data. Note that a send failure returns nil as a first return parameter followed by an error message.

You can see the full program here


local olen = string.len( data);
local tlen = 0;
while ( tlen < olen) do
   count, emsg, ecnt = clnt:send( data);
   if ( count == nil) then
      print( "Send to " .. host .. "failed: "
               .. emsg);
      cstat = nil;
      break;
   end;
   tlen = tlen + count;
   if ( tlen < olen) then
      print( "Partial send, " .. tlen .. " of "
                  .. olen .. " bytes sent");
   end;
end;

ooREXX

A simple receive function that is passed a socket number and returns the received data or exits. Note the drop of buffer before the receive. If buffer was already defined the receive length would be implicitly set to the length of the current variable buffer.

You can see the full program here


/*********************************************************************/
/*  Send data                                                        */
/*********************************************************************/
  SndLen = SockSend( socNum, data)
  if sndLen < 0 then do
      say 'Error sending data, ERRNO:' errno
      exit
  end
  if sndLen <> LENGTH(data) then do
      say 'SockSend Partially Complete, Sent:' sndLen 'bytes'
      exit
  end

IBM TSO REXX

This example is a simple inline send with very limited error handling. The intent here is to send the contents of the variable "data" on the connection defined by the socket number contained in variable "socNum".

You can see the full program here


/*********************************************************************/
/*  Send data                                                        */
/*********************************************************************/

  Resp = SOCKET( 'SEND', socNum, data)
  if WORD( resp, 1) <> 0 then do
      say 'Error sending data'
      say resp
      exit
  end
  Retc = WORD( resp, 2)
  if retc <> LENGTH(data) then do
      say 'SockSend Partially Complete, Sent:' retc 'bytes'
      exit
  end

IBM HPNS API

The DO_SEND subroutine of this example expects to receive a Connection Work Area (CWA) block in R9. The CWA contains the required connection oriented information to complete the send. The data to be sent is held in the buffer pointed to by CWA_SBUF, a buffer header prefixes the data which can be at any offset (see BUF_DOFF) and of any length (see BUF_DLEN). On a partial send, this routine updates these fields to skip the already sent data before calling SEND_RETRY to process the remaining data.

You can see the full program here


********************************************************************** 
* Send data to target system                                         * 
********************************************************************** 
         USING CWA_BLOCK,R9 
DO_SEND  DS    0H                      ******************************* 
         STMG  R0,R15,W1_SAV2          * Save callers regs           * 
         MVC   CWA_REQNM,=CL12'SEND'   * Request name                * 
         NI    CWA_STATE,255-CWA_STATE_SRTY ensure retry off         * 
         L     R4,CWA_SBUF             * Pick up send buffer         * 
         USING BUF_BLOCK,R4            *                             * 
         LTR   R2,R4                   * Copy to R2                  * 
         JZ    SEND_NO_BUF             * Skip if no buffer           * 
         AH    R2,BUF_DOFF             * R2 addr of data to send     * 
SEND_RETRY DS  0H                      *                             * 
         MVC   CWA_PLIST(MODEL_EZASMI_LENGTH),MODEL_EZASMI init plst * 
         EZASMI TYPE=SEND,             *                             * X 
               S=CWA_SOCNO,            *                             * X 
               BUF=(R2),               * address of data to send     * X 
               NBYTE=BUF_DLEN,         * length of data to send      * X 
               TASK=W1T_TASK_WA,       *                             * X 
               ERRNO=CWA_ERRNO,        *                             * X 
               RETCODE=CWA_RETCD,      *                             * X 
               MF=(E,CWA_PLIST)        *                             * 
         CLC   =F'0',CWA_RETCD         * Complete OK ?               * 
         JH    SEND_REQ_ERR            * Y - skip err msg            * 
         CLC   BUF_DLEN,CWA_RETCD      * All data sent ?             * 
         JNE   SEND_PART               * N - retry ?                 * 
         JAS   R14,DO_REQU_OK          *                             * 
         XGR   R15,R15                 *                             * 
         J     SEND_RET                * return                      * 
SEND_PART DS   0H                      *                             * 
         TM    CWA_STATE,CWA_STATE_SRTY  Already retried ?           * 
         JO    SEND_FAIL               * Y - failure                 * 
         OI    CWA_STATE,CWA_STATE_SRTY  Flag retry                  * 
         L     R2,BUF_DLEN             * Data length                 * 
         S     R2,CWA_RETCD            * Minus bytes sent            * 
         ST    R2,BUF_DLEN             * Save new data length        * 
         L     R2,CWA_RETCD            * Number sent                 * 
         AH    R2,BUF_DOFF             * Next byte to be sent        * 
         STH   R2,BUF_DOFF             * In case code more retries   * 
         J     SEND_RETRY              *                             * 
SEND_FAIL DS   0H                      *                             * 
         DC    H'0'                    * Oh No! Abend0C1             * 
****************************************                             * 
* DO_SEND entered with no buffer in CWA                              * 
****************************************                             * 
SEND_NO_BUF DS 0H                      *                             * 
         LA    R15,4                   * Return code                 * 
         J     SEND_RET                * return                      * 
****************************************                             * 
* EZASMI call error processing, issue message set return code        * 
****************************************                             * 
SEND_REQ_ERR DS 0H                     *                             * 
         JAS   R14,DO_REQ_ERR          * Go report error             * 
         LA    R15,16                  * Return code                 * 
SEND_RET DS    0H                      *                             * 
         LMG   R0,R14,W1_SAV2          * Restore callers regs        * 
         BR    R14                     * Return                      * 
*                                      ******************************* 
         DROP  R4,R9 

 

Continue with the Tutorial

Receive
Data
Next
Sockets
Contents
Contents
Client
Connections
Prev