[comp.unix.wizards] My problem with fdopen and sockets........

arup@grad1.cis.upenn.edu (Arup Mukherjee) (02/14/89)

To all net.wizards [again...] :

   I had 3 responses to my earlier posting asking how to obtain a FILE *
that would allow bidirectional communication over an internet socket.
Well, I had 3 responses and they all suggested that I dup() the fd and
use one for reading and the other for writing. As I indicated in my
earlier posting, this does not work. 

Enclosed below are two program listings. writer.c waits for a
connection, writes some stuff to it, and then trys to read something.
reader.c connects to the write, reads some stuff and then writes some
stuff. These work fine using read() and write() calls. However, when I
substitute equivalent fprintf and fscanf calls (by using -DPRINTF or
-DSCANF from the command line while compiling), nothing works.

Again, any suggestions will be greatly appreciated....


						Arup Mukherjee
					[arup@grasp.cis.upenn.edu]

---writer.c---

# include <stdio.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/errno.h>
# include <netinet/in.h>
# include <netdb.h>


int sock, i, newsock, j, sock2;
char *ptr;
char buf[1024];
char *buf2;
extern int errno;

cleanup ()
{
	shutdown(sock, 2);
	shutdown(sock2, 2);
	shutdown (newsock, 2);
	printf ("writer : exiting\n");
	exit(0);
}

main (argc, argv)
char **argv;
{

	struct sockaddr_in sa;
	struct hostent *host;
	FILE *in, *out;


	host = (struct hostent *) gethostbyname("localhost");
	bzero (&sa, sizeof(struct sockaddr_in));

	sa.sin_family = AF_INET;
	bcopy (host->h_addr, &sa.sin_addr, host->h_length);
	sa.sin_port = 7000;
	

	sock = socket (AF_INET, SOCK_STREAM, 0);
	bind (sock, &sa, sizeof(struct sockaddr_in));
	perror ("bound socket...");
	listen (sock, 3);
	newsock = accept(sock, &buf2, &j);
	fprintf (stderr,"writer : Connected %s %d : %d\n", buf2, j, newsock);
	
	for (i = 0; i< 20; ++i) signal (i, cleanup);

	sock2 = dup(newsock);
	out = fdopen (newsock, "w");
	in = fdopen (sock2, "r");

	for (i=0;i < 3;i++){

		errno = 0;
		sprintf (buf, "Testing : %d", i);

# ifdef PRINTF
		fprintf(out, "%s\n", buf); 
# else
		write (newsock, buf, strlen(buf) + 1);
# endif
		if (errno) perror("Writer Error :");
	}


# ifdef SCANF
	fscanf(in, "%s", buf);
# else
	read (newsock, buf, 30);
# endif

	fprintf(stderr, "writer reads: %s\n", buf);

	cleanup();
}




--- reader.c ---

# include <stdio.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <sys/errno.h>
# include <netinet/in.h>
# include <netdb.h>


int sock, i, sock2;
char *ptr;
char buf[1024];
extern int errno;

cleanup ()
{
	shutdown(sock, 2);
	shutdown(sock2, 2);
	printf ("reader : exiting\n");
	exit(0);
}

main (argc, argv)
char **argv;
{
	struct sockaddr_in sa;
	struct hostent *host;
	FILE *in, *out;

	host = (struct hostent *) gethostbyname("localhost");
	bzero (&sa, sizeof(struct sockaddr_in));

	sa.sin_family = AF_INET;
	bcopy (host->h_addr, &sa.sin_addr, host->h_length);
	sa.sin_port = 7000;
	

	sock = socket (AF_INET, SOCK_STREAM, 0);
	connect (sock, &sa, sizeof(struct sockaddr_in));

	if (errno) perror ("Reader Error :");
	
	for (i = 0; i< 20; ++i) signal (i, cleanup);

	sock2 = dup(sock);

	in = fdopen(sock, "r");
	out = fdopen (sock2, "w");
	for (i=0; i < 3 ;i++){

# ifdef SCANF
		fscanf (in,  "%s", buf); 
# else 
		read (sock, buf, 12);
# endif
		
		fprintf (stderr,"reader reads :%s\n", buf);
	}

	sprintf (buf, "Hi there, this is the reader\n");

# ifdef PRINTF
	fprintf(out, "%s\n", buf);
# else
	write (sock2, buf, strlen(buf)+1);
# endif

	fprintf (stderr, "reader : done writing %d chars\n", strlen(buf) + 1);
	cleanup();
}

wen-king@cit-vax.Caltech.Edu (King Su) (02/14/89)

In article <7937@netnews.upenn.edu> arup@grad1.cis.upenn.edu.UUCP (Arup Mukherjee) writes:
>
>To all net.wizards [again...] :

First of all, you should do a 'fflush' on the FILE after you wrote to it.
Since a socket file is not a character speical device, the FILE writing
functions such as fprintf will cause characters to be held in an internal
buffer until a buffering limit is reached or until you do a fflush.

Secondly, you do not need two sockets, you just need two FILEs.  You can
apply fdopen on the same file twice.  You don't need to dup it either.
For example:

in  = fdopen(soc,"r");
out = fdopen(soc,"w");

Your observation, that the suggestions you got by E-mail doesn't work,
probably is due to that fact that you did not use fflush.

-- 
/*------------------------------------------------------------------------*\
| Wen-King Su  wen-king@vlsi.caltech.edu  Caltech Corp of Cosmic Engineers |
\*------------------------------------------------------------------------*/

chris@mimsy.UUCP (Chris Torek) (02/17/89)

In article <9556@cit-vax.Caltech.Edu> wen-king@cit-vax.Caltech.Edu
(King Su) writes:
>in  = fdopen(soc,"r");
>out = fdopen(soc,"w");

If you use this technique, be aware that fclose(in) also closes out's
file descriptor, without giving it a chance to flush any pending data.
Likewise, fclose(out) closes in's fd.  There is also no guarantee that
stdio will not get upset if fclose's implied close() fails.  This
might, for instance, lead to running out of FILE structures.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

smb@ulysses.homer.nj.att.com (Steven M. Bellovin) (02/19/89)

In article <9556@cit-vax.Caltech.Edu>, wen-king@cit-vax.Caltech.Edu (King Su) writes:
} Secondly, you do not need two sockets, you just need two FILEs.  You can
} apply fdopen on the same file twice.  You don't need to dup it either.
} For example:
} 
} in  = fdopen(soc,"r");
} out = fdopen(soc,"w");

That is not correct for System V; you do indeed need to do the dup().
Please note that I'm speaking empirically... (i.e., it broke on me
the other way.)