schaefer@ogccse.ogc.edu (Barton E. Schaefer) (08/11/89)
Surely there must be an obvious way to do this, and I'm just missing it. I'm using DYNIX 3.0.12 on a Sequent Symmetry, but my question applies to 4.3 BSD as well, as far as I can tell. I've asked a guru here and come up empty. I have a small program which creates a socket [via socket(2)], binds a name to it [bind(2)], and prepares to receive connections [listen(2)]. I won't go into the details of these calls because they work as expected. (Miraculous, eh?) The program then needs to proceed with another task, checking the socket periodically for connections from a slave program. The master program cannot block when checking for these connections. According to the man page for accept(2): The call returns -1 on error. If it succeeds it returns a non-negative integer which is a descriptor for the accepted socket. The accept will fail if: [EWOULDBLOCK] The socket is marked non-blocking and no connections are present to be accepted. I therefore make the following call, where sd is the descriptor returned by socket(2): if (fcntl(sd, F_SETFL, FNDELAY) < 0) { perror("fcntl"); exit(1); } I note in passing that FNDELAY is defined only for ttys and sockets, but a socket is what I have, so all should be well. I don't get an error return from fcntl(), in any case. The program then goes about its business, periodically doing: if ((ad = accept(sd, sd_name, sd_size)) < 0) { if (errno != EWOULDBLOCK) { perror("accept"); exit(1); } } else { /* talk to slave via ad */ /* much detail omitted */ if (close(ad) < 0) { perror("close"); exit(1); } } This works fine and I am able to connect to the slave through the new descriptor ad. The problem begins when the slave sometimes cannot read data from the socket as quickly as the master tries to write it. The new descriptor ad inherits the FNDELAY attribute from sd, so the master gets an error from write(2) when the socket fills up. So, what I would like to do is turn *off* the FNDELAY attribute of the new descriptor ad and allow the master to do blocking writes. I don't care if this also affects sd, because there is never more than one master-slave connection at a time and I can reset FNDELAY on sd after closing the connection on ad. So the question is Is there any way to restore blocking read/write on a descriptor? I am aware that: It is possible to select(2) a socket for the purposes of doing an accept by selecting it for read. so it is not strictly necessary to do the fcntl() in the first place, and I will convert to use select(2) if necessary. However, it seems odd to me that FNDELAY is a permanent condition. My present interim solution is to note when the error from write() is EWOULDBLOCK, and in that case go to sleep for second and then try the write again. This seems to lose data on rare, nonrepeatable occasions, so a different solution is required. Thanks in advance. -- Bart Schaefer "And if you believe that, you'll believe anything." -- DangerMouse CSNET / Internet schaefer@cse.ogc.edu UUCP ...{sequent,tektronix,verdix}!ogccse!schaefer
schaefer@ogccse.ogc.edu (Barton E. Schaefer) (08/13/89)
In article <4180@ogccse.ogc.edu> I wrote: } Surely there must be an obvious way to do this, and I'm just missing it. } } if (fcntl(sd, F_SETFL, FNDELAY) < 0) { } perror("fcntl"); } exit(1); } } } } Is there any way to restore blocking read/write on a descriptor? As expected, I was indeed missing the obvious. The following people all pointed it out in basically the same way, with various degrees of "Hint! Hint!" :-) commentary leading up to the actual solution: Chris Torek <chris@mimsy.umd.edu> loverso@Xylogics.COM (John Robert LoVerso) guy@auspex.com (Guy Harris) The answer is that, as I should have realized, fcntl() works just like ioctl(), and with F_SETFL it sets/clears whatever bits are on/off in the third parameter. So for my particular problem, it suffices to do if (fcntl(ad, F_SETFL, 0) < 0) { perror("fcntl"); } and, in the more general case, if ((flags = fcntl(ad, F_GETFL, 0)) < 0) { perror("fcntl: can't get flags:"); } else if (fcntl(ad, F_SETFL, flags & ~FNDELAY) < 0) { perror("fcntl: can't set flags:"); } I got a different answer from Phil Hochstetler at Sequent, for you other DYNIX users. I haven't tried this one, because the fcntl() solution works fine. } From: sequent!phil (Phil Hochstetler) } } I believe the proper way to do this is: } } int on = 1, } off = 0; } } /* turn on non blocking I/O on a socket */ } if (ioctl(fd, FIONBIO, &on) < 0) { } perror("ioctl"); } exit(1); } } } } /* turn off non blocking I/O on a socket */ } if (ioctl(fd, FIONBIO, &off) < 0) { } perror("ioctl"); } exit(1); } } } } I think the problem is a basic BSD one .. 4.X BSD never did this } the way the doc states (or used to do it both ways). Give the above } a try. } -- } Phil Hochstetler UUCP: uunet!sequent!phil Thanks to everyone who responded. -- Bart Schaefer "And if you believe that, you'll believe anything." -- DangerMouse CSNET / Internet schaefer@cse.ogc.edu UUCP ...{sequent,tektronix,verdix}!ogccse!schaefer