From: dsiebert@icaen.uiowa.edu (Doug Siebert)
Subject: How the #$%@$* does TCP urgent data work?
Date: Tue, 6 Oct 1992 02:42:21 GMT
Organization: ISCA
Keywords: TCP, OOB, urgent


I wrote the following in an attempt to understand how TCP urgent data works.
To use it, it is first invoked with a single argument and backgrounded to
create the listening half of the socket connection, then re-invoked with no
arguments in the foreground.  Following the listing is the resulting output
with my comments/questions.  The was compiled and run on an HP 9000/750 running
HP-UX 8.07, but as far as I am aware this program should behave in the same
way on any Unix system, subject to the usual header file differences, of
course.


---cut here---
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>
#include <errno.h>
 
main(argc)
int     argc;
{
  int     n,
          s;
  struct sockaddr_in sa;
  int     on = 1;
  int     i;
  fd_set  rfd,
          efd;
  char    buf[30];
 
  s = socket(AF_INET, SOCK_STREAM, 0);
  if (s < 0)
  {
    perror("socket");
    exit(1);
  }
  sa.sin_family = AF_INET;
  sa.sin_addr.s_addr = 0x7f000001;
  sa.sin_port = 1111;
  if (argc == 2)
  {
    setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on);
    bind(s, &sa, sizeof sa);
    listen(s, 1);
    n = accept(s, 0, 0);
    if (n < 0)
    {
      perror("accept");
      exit(1);
    }
    sleep(1);
    for (;;)
    {
      FD_ZERO(&rfd);
      FD_SET(n, &rfd);
      FD_ZERO(&efd);
      FD_SET(n, &efd);
      select(n + 1, &rfd, 0, &efd, 0);
      if (FD_ISSET(n, &efd))
      {
        ioctl(n, SIOCATMARK, &i);
        printf("atmark value = %d\n", i);
        for (;;)
        {
          i = recv(n, buf, 20, MSG_OOB);
          if (i <= 0)
          {
            if (i)
              perror("oobrecv");
            else
              printf("oob null read\n");
            break;
          }
          buf[i] = 0;
          printf("oob - got '%s'\n", buf);
        }
        ioctl(n, SIOCATMARK, &i);
        printf("atmark value = %d\n", i);
      }
      if (FD_ISSET(n, &rfd))
      {
        i = recv(n, buf, 20, 0);
        if (i <= 0)
        {
          if (i)
            perror("recv");
          else
            printf("null read...normal termination\n");
          exit(1);
        }
        buf[i] = 0;
        printf("got '%s'\n", buf);
      }
    }
  }
  else
  {
    connect(s, &sa, sizeof sa);
    send(s, "test1", 5, 0);
    send(s, "a", 1, MSG_OOB);
    send(s, "test2", 5, 0);
    send(s, "b", 1, MSG_OOB);
    send(s, "test3", 5, 0);
    send(s, "c", 1, MSG_OOB);
    send(s, "test4", 5, 0);
    sleep(2);
  }
}
---cut here ---


% ./example ignoredargument &

{ here I startup the listening/reading half in the background }

[1] 7712
% ./example

{ here I startup the connecting/writing half }

atmark value = 0
oob - got 'a'
oobrecv: Invalid argument
atmark value = 0

{ since the exception condition is checked first the 'a' is read before
  anything else, as OOB data is defined to work, apparently.  Any further
  attempts to receive OOB data get 'Invalid argument', which is the result
  expected for attempts to read OOB data when none is present.  The atmark
  value is 0 because I haven't read up to the mark yet.   So far no questions }

got 'test1'

{ I now receive the regular data, up to the mark only.  Q:  Is this the
  expected behavior, to only read up to the mark and no further?  There is
  obviously other data waiting to be read past the mark, because of the sleep
  which is executed after accepting the connection but before attempting the
  selecting and reading of data. }

atmark value = 1
oobrecv: Invalid argument
atmark value = 1

{ Here I am 'at the mark', but unable to receive further OOB data, even though
  at this point the two other bytes have most certainly been sent (again due
  to the sleep on the listening/reading side after accepting the connection)
  Q:  Why is this? }

got 'test2test3test4'

{ Now I read the rest of the data, over the places where the other urgent data
  was sent.  Q: Is this because there is only one mark, and once the mark is
  set it moves only when the previous urgent data has been read, and I had not
  read the first urgent data when the second and third bytes of urgent data
  arrived?  And why did select() indicate an exception condition was pending
  when I was unable to read any further urgent data?  Is this expected? }

null read...normal termination

{ I see this after a one second pause (due to the sleep on the end of sending
  half)  As expected, select signals for a read on the socket but read returns
  0 when it was closed by the remote side.  Q: what happened to my other two
  bytes of urgent data?  They have been lost!  Where did they go?  How do fix
  this program?  Or does it work as it should and it is my expectations which
  are in error? }

%
[1]   Exit 1               ./example ignoredargument



If anyone can help me understand why this acts as it does, or better yet, fix
it so it performs as "expected" (i.e. how *I* expected it to behave, where no
data, urgent or otherwise, is lost) please come to my rescue!  Thank you.

-- 
/-----------------------------------------------------------------------------\
| Doug Siebert                             | "I don't have to take this abuse |
| Internet:  dsiebert@isca.uiowa.edu       |  from you - I've got hundreds of |
| NeXTMail:  dsiebert@chop.isca.uiowa.edu  |  people waiting in line to abuse |
|     ICBM:  41d 39m 55s N, 91d 30m 43s W  |  me!"  Bill Murray, Ghostbusters |
\-----------------------------------------------------------------------------/
Newsgroups: comp.unix.programmer,comp.windows.x