From: SEB1525@MVS.draper.com (Steve Bacher)
Newsgroups: comp.unix.programmer
Subject: UDP bidirectional socket problem
Date: 10 Nov 92 20:33:00 GMT
Organization: Draper Laboratory


Please excuse me, those of you who saw this on comp.sys.sun.apps...

We have a program that sets up 2 UDP peer-to-peer sockets between two
Suns.  If both Suns are Sun-3's, they can read and write between 2
processes using 1 socket on each side.  If the Sun-3 talks to the Sparc,
then single sockets don't work - you end up needing 4 sockets, 2 on each
side, 1 read and 1 write.

Here is the program, along with 2 calling programs, to demonstrate the
problem.  Each program takes the name of remote machine to talk to.
You run sock1 on one host and subsequently sock2 on the other.
sock1 writes a message to the other host, where sock2 reads it.
Then sock2 reads a reply from the machine running sock1.

If I start sock1 on the sun4 and then start sock2 on the sun3,
sock1 hangs waiting for sock2 to send it something.  sock2 appears
never to return from the write to sock1.

If I start sock1 on the sun3 and then start sock2 on the sun4, sock1
hangs and sock2 complains on the read because it did not block after the
write.  No non-blocking has been specified.

We can get a message through if we disconnect the socket on 1 side.
Executing the connect() call on the sun4 somehow screws things up.

Note that read() normally blocks.  sock1 does read() and waits and
blocks just fine.  But if you have written to the socket, the first time
you read from it it is nonblocking, so you have to force a loop to
actually get it to block - the second one blocks.

I compared the output of the C preprocessor on the programs compiled on
the sun3 and sun4.  The only significant difference is in the definition
of the type physadr_t in /usr/include/sys/types.h, which is a struct
containing a short on the sun-3 but a struct containing an int on a
Sparc.  This doesn't seem to be used anywhere, though.

Here are the sample programs.

------------------------------------------------------------------------
makefile 
------------------------------------------------------------------------

sock1:          sock1.o sock.o
                cc -o sock1 sock1.o sock.o

sock2:          sock2.o sock.o
                cc -o sock2 sock2.o sock.o

sock.o:         recomp sock.c
                cc -c sock.c

sock1.o:        sock1.c
                cc -c sock1.c

sock2.o:        sock2.c
                cc -c sock2.c

recomp:
                -rm sock.o

------------------------------------------------------------------------
sock.c
------------------------------------------------------------------------

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdio.h>
#define MAX_MLEN 128
#define TRUE 1
#define FALSE 0

int sock;
char msg[MAX_MLEN];

set_socks(remote_host)
char *remote_host;
{
  struct hostent *remote_ent;
  unsigned long *remote_addr;
  struct sockaddr_in remote_name,my_name;
  int optval = TRUE;

  sock = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock < 0)
  {
    perror("Error opening socket ");
    return(FALSE);
  }

  my_name.sin_family = AF_INET;
  my_name.sin_addr.s_addr = htonl(INADDR_ANY);
  my_name.sin_port = htons(5002); /* write to me here */

  if (bind(sock, (struct sockaddr *) &my_name, sizeof(my_name)) < 0)
  {
    perror("Error binding my socket to my name");
    return(FALSE);
  }

  remote_ent = (struct hostent *) gethostbyname(remote_host);
  remote_addr = (unsigned long *) remote_ent->h_addr;
  remote_name.sin_family = AF_INET;
  remote_name.sin_addr.s_addr = *remote_addr;
  remote_name.sin_port = htons(5002);

  if(connect(sock, (struct sockaddr *) &remote_name,sizeof(remote_name)) < 0)
  {
    perror("Error connecting to remote port");
    return(FALSE);
  }
  return(TRUE);
}


host2(sendto)
char *sendto;
{
#define namelen 64
  int i,j;
  char me[namelen];
  gethostname(me,namelen);
  strcpy(msg,"Hello ");
  strcat(msg, sendto);
  strcat(msg, " Love, ");
  strcat(msg,me);
  strcat(msg,"\n");
  printf("about to set socks on %s\n",me);
  if(set_socks(sendto)){
    printf("about to write the following message to %s ::\n",sendto);
    printf("%s",msg);
    if(write(sock,msg,MAX_MLEN)<0)
    {
      perror("write error");
      return(FALSE);
    }
    j=0; 
    while((i=read(sock,msg,MAX_MLEN)) < 0)
    {
      j += 1;
      printf("read did not block after write\n");
    }
    printf(
     "Got %d byte message from %s after %d read attempts. Msg: %s\n",
            i,sendto,j,msg);
  }
  else exit(1);
}

host1(sendto)
char *sendto;
{
#define namelen 64
  int i; 
  char me[namelen];

  gethostname(me,namelen);
  printf("about to set socks on %s\n",me);
  if(set_socks(sendto)){
    printf("about to wait for a message from %s ::\n",sendto);
    i=read(sock,msg,MAX_MLEN);
    printf("%s",msg);
    strcpy(msg,"Bye ");
    strcat(msg, sendto);
    strcat(msg, " Love, ");
    strcat(msg,me);
    strcat(msg,"\n");
    printf("about to send reply to %s\n", sendto);
    sleep(2);
    if(write(sock,msg,MAX_MLEN)<0)
    {
      perror("write error");
    }
  }
  else exit(1);
  close(sock);
}

------------------------------------------------------------------------
sock1.c
------------------------------------------------------------------------

main(argc,argv)
int argc;
char **argv;
{
  host1(argv[1]);
}

------------------------------------------------------------------------
sock2.c
------------------------------------------------------------------------

main(argc,argv)
int argc;
char **argv;
{
  host2(argv[1]);
}


--
Steve Bacher (Batchman)                 Draper Laboratory
Internet: seb@draper.com                Cambridge, MA, USA