992 lines
43 KiB
HTML
992 lines
43 KiB
HTML
|
|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
|
<HTML><HEAD><TITLE>Man page of SELECT_TUT</TITLE>
|
|
</HEAD><BODY>
|
|
<H1>SELECT_TUT</H1>
|
|
Section: Linux Programmer's Manual (2)<BR>Updated: 2019-03-06<BR><A HREF="#index">Index</A>
|
|
<A HREF="/cgi-bin/man/man2html">Return to Main Contents</A><HR>
|
|
|
|
<A NAME="lbAB"> </A>
|
|
<H2>NAME</H2>
|
|
|
|
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO -
|
|
synchronous I/O multiplexing
|
|
<A NAME="lbAC"> </A>
|
|
<H2>SYNOPSIS</H2>
|
|
|
|
<PRE>
|
|
/* According to POSIX.1-2001, POSIX.1-2008 */
|
|
<B>#include <<A HREF="file:///usr/include/sys/select.h">sys/select.h</A>></B>
|
|
|
|
/* According to earlier standards */
|
|
<B>#include <<A HREF="file:///usr/include/sys/time.h">sys/time.h</A>></B>
|
|
<B>#include <<A HREF="file:///usr/include/sys/types.h">sys/types.h</A>></B>
|
|
<B>#include <<A HREF="file:///usr/include/unistd.h">unistd.h</A>></B>
|
|
|
|
<B>int select(int </B><I>nfds</I><B>, fd_set *</B><I>readfds</I><B>, fd_set *</B><I>writefds</I><B>,</B>
|
|
<B> fd_set *</B><I>exceptfds</I><B>, struct timeval *</B><I>utimeout</I><B>);</B>
|
|
|
|
<B>void FD_CLR(int </B><I>fd</I><B>, fd_set *</B><I>set</I><B>);</B>
|
|
<B>int FD_ISSET(int </B><I>fd</I><B>, fd_set *</B><I>set</I><B>);</B>
|
|
<B>void FD_SET(int </B><I>fd</I><B>, fd_set *</B><I>set</I><B>);</B>
|
|
<B>void FD_ZERO(fd_set *</B><I>set</I><B>);</B>
|
|
|
|
<B>#include <<A HREF="file:///usr/include/sys/select.h">sys/select.h</A>></B>
|
|
|
|
<B>int pselect(int </B><I>nfds</I><B>, fd_set *</B><I>readfds</I><B>, fd_set *</B><I>writefds</I><B>,</B>
|
|
<B> fd_set *</B><I>exceptfds</I><B>, const struct timespec *</B><I>ntimeout</I><B>,</B>
|
|
<B> const sigset_t *</B><I>sigmask</I><B>);</B>
|
|
</PRE>
|
|
|
|
<P>
|
|
|
|
|
|
Feature Test Macro Requirements for glibc (see
|
|
<B><A HREF="/cgi-bin/man/man2html?7+feature_test_macros">feature_test_macros</A></B>(7)):
|
|
|
|
|
|
<P>
|
|
|
|
<B>pselect</B>():
|
|
|
|
_POSIX_C_SOURCE >= 200112L
|
|
<A NAME="lbAD"> </A>
|
|
<H2>DESCRIPTION</H2>
|
|
|
|
<B>select</B>()
|
|
|
|
(or
|
|
<B>pselect</B>())
|
|
|
|
is used to efficiently monitor multiple file descriptors,
|
|
to see if any of them is, or becomes, "ready";
|
|
that is, to see whether I/O becomes possible,
|
|
or an "exceptional condition" has occurred on any of the file descriptors.
|
|
<P>
|
|
|
|
Its principal arguments are three "sets" of file descriptors:
|
|
<I>readfds</I>, <I>writefds</I>, and <I>exceptfds</I>.
|
|
Each set is declared as type
|
|
<I>fd_set</I>,
|
|
|
|
and its contents can be manipulated with the macros
|
|
<B>FD_CLR</B>(),
|
|
|
|
<B>FD_ISSET</B>(),
|
|
|
|
<B>FD_SET</B>(),
|
|
|
|
and
|
|
<B>FD_ZERO</B>().
|
|
|
|
A newly declared set should first be cleared using
|
|
<B>FD_ZERO</B>().
|
|
|
|
<B>select</B>()
|
|
|
|
modifies the contents of the sets according to the rules
|
|
described below; after calling
|
|
<B>select</B>()
|
|
|
|
you can test if a file descriptor is still present in a set with the
|
|
<B>FD_ISSET</B>()
|
|
|
|
macro.
|
|
<B>FD_ISSET</B>()
|
|
|
|
returns nonzero if a specified file descriptor is present in a set
|
|
and zero if it is not.
|
|
<B>FD_CLR</B>()
|
|
|
|
removes a file descriptor from a set.
|
|
<A NAME="lbAE"> </A>
|
|
<H3>Arguments</H3>
|
|
|
|
<DL COMPACT>
|
|
<DT id="1"><I>readfds</I><DD>
|
|
This set is watched to see if data is available for reading from any of
|
|
its file descriptors.
|
|
After
|
|
<B>select</B>()
|
|
|
|
has returned, <I>readfds</I> will be
|
|
cleared of all file descriptors except for those that
|
|
are immediately available for reading.
|
|
<DT id="2"><I>writefds</I><DD>
|
|
This set is watched to see if there is space to write data to any of
|
|
its file descriptors.
|
|
After
|
|
<B>select</B>()
|
|
|
|
has returned, <I>writefds</I> will be
|
|
cleared of all file descriptors except for those that
|
|
are immediately available for writing.
|
|
<DT id="3"><I>exceptfds</I><DD>
|
|
This set is watched for "exceptional conditions".
|
|
In practice, only one such exceptional condition is common:
|
|
the availability of <I>out-of-band</I> (OOB) data for reading
|
|
from a TCP socket.
|
|
See
|
|
<B><A HREF="/cgi-bin/man/man2html?2+recv">recv</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+send">send</A></B>(2),
|
|
|
|
and
|
|
<B><A HREF="/cgi-bin/man/man2html?7+tcp">tcp</A></B>(7)
|
|
|
|
for more details about OOB data.
|
|
(One other less common case where
|
|
<B><A HREF="/cgi-bin/man/man2html?2+select">select</A></B>(2)
|
|
|
|
indicates an exceptional condition occurs with pseudoterminals
|
|
in packet mode; see
|
|
<B><A HREF="/cgi-bin/man/man2html?2+ioctl_tty">ioctl_tty</A></B>(2).)
|
|
|
|
After
|
|
<B>select</B>()
|
|
|
|
has returned,
|
|
<I>exceptfds</I> will be cleared of all file descriptors except for those
|
|
for which an exceptional condition has occurred.
|
|
<DT id="4"><I>nfds</I><DD>
|
|
This is an integer one more than the maximum of any file descriptor in
|
|
any of the sets.
|
|
In other words, while adding file descriptors to each of the sets,
|
|
you must calculate the maximum integer value of all of them,
|
|
then increment this value by one, and then pass this as <I>nfds</I>.
|
|
<DT id="5"><I>utimeout</I><DD>
|
|
This is the longest time
|
|
<B>select</B>()
|
|
|
|
may wait before returning, even if nothing interesting happened.
|
|
If this value is passed as NULL, then
|
|
<B>select</B>()
|
|
|
|
blocks indefinitely waiting for a file descriptor to become ready.
|
|
<I>utimeout</I> can be set to zero seconds, which causes
|
|
<B>select</B>()
|
|
|
|
to return immediately, with information about the readiness
|
|
of file descriptors at the time of the call.
|
|
The structure <I>struct timeval</I> is defined as:
|
|
<DT id="6"><DD>
|
|
|
|
|
|
struct timeval {
|
|
<BR> time_t tv_sec; /* seconds */
|
|
<BR> long tv_usec; /* microseconds */
|
|
};
|
|
|
|
|
|
<DT id="7"><I>ntimeout</I><DD>
|
|
This argument for
|
|
<B>pselect</B>()
|
|
|
|
has the same meaning as
|
|
<I>utimeout</I>,
|
|
|
|
but
|
|
<I>struct timespec</I>
|
|
|
|
has nanosecond precision as follows:
|
|
<DT id="8"><DD>
|
|
|
|
|
|
struct timespec {
|
|
<BR> long tv_sec; /* seconds */
|
|
<BR> long tv_nsec; /* nanoseconds */
|
|
};
|
|
|
|
|
|
<DT id="9"><I>sigmask</I><DD>
|
|
This argument holds a set of signals that the kernel should unblock
|
|
(i.e., remove from the signal mask of the calling thread),
|
|
while the caller is blocked inside the
|
|
<B>pselect</B>()
|
|
|
|
call (see
|
|
<B><A HREF="/cgi-bin/man/man2html?3+sigaddset">sigaddset</A></B>(3)
|
|
|
|
and
|
|
<B><A HREF="/cgi-bin/man/man2html?2+sigprocmask">sigprocmask</A></B>(2)).
|
|
|
|
It may be NULL,
|
|
in which case the call does not modify the signal mask on
|
|
entry and exit to the function.
|
|
In this case,
|
|
<B>pselect</B>()
|
|
|
|
will then behave just like
|
|
<B>select</B>().
|
|
|
|
</DL>
|
|
<A NAME="lbAF"> </A>
|
|
<H3>Combining signal and data events</H3>
|
|
|
|
<B>pselect</B>()
|
|
|
|
is useful if you are waiting for a signal as well as
|
|
for file descriptor(s) to become ready for I/O.
|
|
Programs that receive signals
|
|
normally use the signal handler only to raise a global flag.
|
|
The global flag will indicate that the event must be processed
|
|
in the main loop of the program.
|
|
A signal will cause the
|
|
<B>select</B>()
|
|
|
|
(or
|
|
<B>pselect</B>())
|
|
|
|
call to return with <I>errno</I> set to <B>EINTR</B>.
|
|
This behavior is essential so that signals can be processed
|
|
in the main loop of the program, otherwise
|
|
<B>select</B>()
|
|
|
|
would block indefinitely.
|
|
Now, somewhere
|
|
in the main loop will be a conditional to check the global flag.
|
|
So we must ask:
|
|
what if a signal arrives after the conditional, but before the
|
|
<B>select</B>()
|
|
|
|
call?
|
|
The answer is that
|
|
<B>select</B>()
|
|
|
|
would block indefinitely, even though an event is actually pending.
|
|
This race condition is solved by the
|
|
<B>pselect</B>()
|
|
|
|
call.
|
|
This call can be used to set the signal mask to a set of signals
|
|
that are to be received only within the
|
|
<B>pselect</B>()
|
|
|
|
call.
|
|
For instance, let us say that the event in question
|
|
was the exit of a child process.
|
|
Before the start of the main loop, we
|
|
would block <B>SIGCHLD</B> using
|
|
<B><A HREF="/cgi-bin/man/man2html?2+sigprocmask">sigprocmask</A></B>(2).
|
|
|
|
Our
|
|
<B>pselect</B>()
|
|
|
|
call would enable
|
|
<B>SIGCHLD</B>
|
|
|
|
by using an empty signal mask.
|
|
Our program would look like:
|
|
<P>
|
|
|
|
|
|
static volatile sig_atomic_t got_SIGCHLD = 0;
|
|
<P>
|
|
static void
|
|
child_sig_handler(int sig)
|
|
{
|
|
<BR> got_SIGCHLD = 1;
|
|
}
|
|
<P>
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
<BR> sigset_t sigmask, empty_mask;
|
|
<BR> struct sigaction sa;
|
|
<BR> fd_set readfds, writefds, exceptfds;
|
|
<BR> int r;
|
|
<P>
|
|
<BR> sigemptyset(&sigmask);
|
|
<BR> sigaddset(&sigmask, SIGCHLD);
|
|
<BR> if (sigprocmask(SIG_BLOCK, &sigmask, NULL) == -1) {
|
|
<BR> perror("sigprocmask");
|
|
<BR> exit(EXIT_FAILURE);
|
|
<BR> }
|
|
<P>
|
|
<BR> sa.sa_flags = 0;
|
|
<BR> sa.sa_handler = child_sig_handler;
|
|
<BR> sigemptyset(&sa.sa_mask);
|
|
<BR> if (sigaction(SIGCHLD, &sa, NULL) == -1) {
|
|
<BR> perror("sigaction");
|
|
<BR> exit(EXIT_FAILURE);
|
|
<BR> }
|
|
<P>
|
|
<BR> sigemptyset(&empty_mask);
|
|
<P>
|
|
<BR> for (;;) { /* main loop */
|
|
<BR> /* Initialize readfds, writefds, and exceptfds
|
|
<BR> before the pselect() call. (Code omitted.) */
|
|
<P>
|
|
<BR> r = pselect(nfds, &readfds, &writefds, &exceptfds,
|
|
<BR> NULL, &empty_mask);
|
|
<BR> if (r == -1 && errno != EINTR) {
|
|
<BR> /* Handle error */
|
|
<BR> }
|
|
<P>
|
|
<BR> if (got_SIGCHLD) {
|
|
<BR> got_SIGCHLD = 0;
|
|
<P>
|
|
<BR> /* Handle signalled event here; e.g., wait() for all
|
|
<BR> terminated children. (Code omitted.) */
|
|
<BR> }
|
|
<P>
|
|
<BR> /* main body of program */
|
|
<BR> }
|
|
}
|
|
|
|
<A NAME="lbAG"> </A>
|
|
<H3>Practical</H3>
|
|
|
|
So what is the point of
|
|
<B>select</B>()?
|
|
|
|
Can't I just read and write to my file descriptors whenever I want?
|
|
The point of
|
|
<B>select</B>()
|
|
|
|
is that it watches
|
|
multiple descriptors at the same time and properly puts the process to
|
|
sleep if there is no activity.
|
|
UNIX programmers often find
|
|
themselves in a position where they have to handle I/O from more than one
|
|
file descriptor where the data flow may be intermittent.
|
|
If you were to merely create a sequence of
|
|
<B><A HREF="/cgi-bin/man/man2html?2+read">read</A></B>(2)
|
|
|
|
and
|
|
<B><A HREF="/cgi-bin/man/man2html?2+write">write</A></B>(2)
|
|
|
|
calls, you would
|
|
find that one of your calls may block waiting for data from/to a file
|
|
descriptor, while another file descriptor is unused though ready for I/O.
|
|
<B>select</B>()
|
|
|
|
efficiently copes with this situation.
|
|
<A NAME="lbAH"> </A>
|
|
<H3>Select law</H3>
|
|
|
|
Many people who try to use
|
|
<B>select</B>()
|
|
|
|
come across behavior that is
|
|
difficult to understand and produces nonportable or borderline results.
|
|
For instance, the above program is carefully written not to
|
|
block at any point, even though it does not set its file descriptors to
|
|
nonblocking mode.
|
|
It is easy to introduce
|
|
subtle errors that will remove the advantage of using
|
|
<B>select</B>(),
|
|
|
|
so here is a list of essentials to watch for when using
|
|
<B>select</B>().
|
|
|
|
<DL COMPACT>
|
|
<DT id="10">1.<DD>
|
|
You should always try to use
|
|
<B>select</B>()
|
|
|
|
without a timeout.
|
|
Your program
|
|
should have nothing to do if there is no data available.
|
|
Code that
|
|
depends on timeouts is not usually portable and is difficult to debug.
|
|
<DT id="11">2.<DD>
|
|
The value <I>nfds</I> must be properly calculated for efficiency as
|
|
explained above.
|
|
<DT id="12">3.<DD>
|
|
No file descriptor must be added to any set if you do not intend
|
|
to check its result after the
|
|
<B>select</B>()
|
|
|
|
call, and respond appropriately.
|
|
See next rule.
|
|
<DT id="13">4.<DD>
|
|
After
|
|
<B>select</B>()
|
|
|
|
returns, all file descriptors in all sets
|
|
should be checked to see if they are ready.
|
|
<DT id="14">5.<DD>
|
|
The functions
|
|
<B><A HREF="/cgi-bin/man/man2html?2+read">read</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+recv">recv</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+write">write</A></B>(2),
|
|
|
|
and
|
|
<B><A HREF="/cgi-bin/man/man2html?2+send">send</A></B>(2)
|
|
|
|
do <I>not</I> necessarily read/write the full amount of data
|
|
that you have requested.
|
|
If they do read/write the full amount, it's
|
|
because you have a low traffic load and a fast stream.
|
|
This is not always going to be the case.
|
|
You should cope with the case of your
|
|
functions managing to send or receive only a single byte.
|
|
<DT id="15">6.<DD>
|
|
Never read/write only in single bytes at a time unless you are really
|
|
sure that you have a small amount of data to process.
|
|
It is extremely
|
|
inefficient not to read/write as much data as you can buffer each time.
|
|
The buffers in the example below are 1024 bytes although they could
|
|
easily be made larger.
|
|
<DT id="16">7.<DD>
|
|
Calls to
|
|
<B><A HREF="/cgi-bin/man/man2html?2+read">read</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+recv">recv</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+write">write</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+send">send</A></B>(2),
|
|
|
|
and
|
|
<B>select</B>()
|
|
|
|
can fail with the error
|
|
<B>EINTR</B>,
|
|
and calls to
|
|
<B><A HREF="/cgi-bin/man/man2html?2+read">read</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+recv">recv</A></B>(2)
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+write">write</A></B>(2),
|
|
|
|
and
|
|
<B><A HREF="/cgi-bin/man/man2html?2+send">send</A></B>(2)
|
|
|
|
can fail with
|
|
<I>errno</I>
|
|
|
|
set to <B>EAGAIN</B> (<B>EWOULDBLOCK</B>).
|
|
These results must be properly managed (not done properly above).
|
|
If your program is not going to receive any signals, then
|
|
it is unlikely you will get <B>EINTR</B>.
|
|
If your program does not set nonblocking I/O,
|
|
you will not get <B>EAGAIN</B>.
|
|
|
|
<DT id="17">8.<DD>
|
|
Never call
|
|
<B><A HREF="/cgi-bin/man/man2html?2+read">read</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+recv">recv</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+write">write</A></B>(2),
|
|
|
|
or
|
|
<B><A HREF="/cgi-bin/man/man2html?2+send">send</A></B>(2)
|
|
|
|
with a buffer length of zero.
|
|
<DT id="18">9.<DD>
|
|
If the functions
|
|
<B><A HREF="/cgi-bin/man/man2html?2+read">read</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+recv">recv</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+write">write</A></B>(2),
|
|
|
|
and
|
|
<B><A HREF="/cgi-bin/man/man2html?2+send">send</A></B>(2)
|
|
|
|
fail with errors other than those listed in <B>7.</B>,
|
|
or one of the input functions returns 0, indicating end of file,
|
|
then you should <I>not</I> pass that file descriptor to
|
|
<B>select</B>()
|
|
|
|
again.
|
|
In the example below,
|
|
I close the file descriptor immediately, and then set it to -1
|
|
to prevent it being included in a set.
|
|
<DT id="19">10.<DD>
|
|
The timeout value must be initialized with each new call to
|
|
<B>select</B>(),
|
|
|
|
since some operating systems modify the structure.
|
|
<B>pselect</B>()
|
|
|
|
however does not modify its timeout structure.
|
|
<DT id="20">11.<DD>
|
|
Since
|
|
<B>select</B>()
|
|
|
|
modifies its file descriptor sets,
|
|
if the call is being used in a loop,
|
|
then the sets must be reinitialized before each call.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</DL>
|
|
<A NAME="lbAI"> </A>
|
|
<H3>Usleep emulation</H3>
|
|
|
|
On systems that do not have a
|
|
<B><A HREF="/cgi-bin/man/man2html?3+usleep">usleep</A></B>(3)
|
|
|
|
function, you can call
|
|
<B>select</B>()
|
|
|
|
with a finite timeout and no file descriptors as
|
|
follows:
|
|
<P>
|
|
|
|
|
|
|
|
struct timeval tv;
|
|
tv.tv_sec = 0;
|
|
tv.tv_usec = 200000; /* 0.2 seconds */
|
|
select(0, NULL, NULL, NULL, &tv);
|
|
|
|
|
|
<P>
|
|
|
|
This is guaranteed to work only on UNIX systems, however.
|
|
<A NAME="lbAJ"> </A>
|
|
<H2>RETURN VALUE</H2>
|
|
|
|
On success,
|
|
<B>select</B>()
|
|
|
|
returns the total number of file descriptors
|
|
still present in the file descriptor sets.
|
|
<P>
|
|
|
|
If
|
|
<B>select</B>()
|
|
|
|
timed out, then the return value will be zero.
|
|
The file descriptors set should be all
|
|
empty (but may not be on some systems).
|
|
<P>
|
|
|
|
A return value of -1 indicates an error, with <I>errno</I> being
|
|
set appropriately.
|
|
In the case of an error, the contents of the returned sets and
|
|
the <I>struct timeout</I> contents are undefined and should not be used.
|
|
<B>pselect</B>()
|
|
|
|
however never modifies <I>ntimeout</I>.
|
|
<A NAME="lbAK"> </A>
|
|
<H2>NOTES</H2>
|
|
|
|
Generally speaking,
|
|
all operating systems that support sockets also support
|
|
<B>select</B>().
|
|
|
|
<B>select</B>()
|
|
|
|
can be used to solve
|
|
many problems in a portable and efficient way that naive programmers try
|
|
to solve in a more complicated manner using
|
|
threads, forking, IPCs, signals, memory sharing, and so on.
|
|
<P>
|
|
|
|
The
|
|
<B><A HREF="/cgi-bin/man/man2html?2+poll">poll</A></B>(2)
|
|
|
|
system call has the same functionality as
|
|
<B>select</B>(),
|
|
|
|
and is somewhat more efficient when monitoring sparse
|
|
file descriptor sets.
|
|
It is nowadays widely available, but historically was less portable than
|
|
<B>select</B>().
|
|
|
|
<P>
|
|
|
|
The Linux-specific
|
|
<B><A HREF="/cgi-bin/man/man2html?7+epoll">epoll</A></B>(7)
|
|
|
|
API provides an interface that is more efficient than
|
|
<B><A HREF="/cgi-bin/man/man2html?2+select">select</A></B>(2)
|
|
|
|
and
|
|
<B><A HREF="/cgi-bin/man/man2html?2+poll">poll</A></B>(2)
|
|
|
|
when monitoring large numbers of file descriptors.
|
|
<A NAME="lbAL"> </A>
|
|
<H2>EXAMPLE</H2>
|
|
|
|
Here is an example that better demonstrates the true utility of
|
|
<B>select</B>().
|
|
|
|
The listing below is a TCP forwarding program that forwards
|
|
from one TCP port to another.
|
|
<P>
|
|
|
|
|
|
#include <<A HREF="file:///usr/include/stdlib.h">stdlib.h</A>>
|
|
#include <<A HREF="file:///usr/include/stdio.h">stdio.h</A>>
|
|
#include <<A HREF="file:///usr/include/unistd.h">unistd.h</A>>
|
|
#include <<A HREF="file:///usr/include/sys/time.h">sys/time.h</A>>
|
|
#include <<A HREF="file:///usr/include/sys/types.h">sys/types.h</A>>
|
|
#include <<A HREF="file:///usr/include/string.h">string.h</A>>
|
|
#include <<A HREF="file:///usr/include/signal.h">signal.h</A>>
|
|
#include <<A HREF="file:///usr/include/sys/socket.h">sys/socket.h</A>>
|
|
#include <<A HREF="file:///usr/include/netinet/in.h">netinet/in.h</A>>
|
|
#include <<A HREF="file:///usr/include/arpa/inet.h">arpa/inet.h</A>>
|
|
#include <<A HREF="file:///usr/include/errno.h">errno.h</A>>
|
|
<P>
|
|
static int forward_port;
|
|
<P>
|
|
#undef max
|
|
#define max(x,y) ((x) > (y) ? (x) : (y))
|
|
<P>
|
|
static int
|
|
listen_socket(int listen_port)
|
|
{
|
|
<BR> struct sockaddr_in addr;
|
|
<BR> int lfd;
|
|
<BR> int yes;
|
|
<P>
|
|
<BR> lfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
<BR> if (lfd == -1) {
|
|
<BR> perror("socket");
|
|
<BR> return -1;
|
|
<BR> }
|
|
<P>
|
|
<BR> yes = 1;
|
|
<BR> if (setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR,
|
|
<BR> &yes, sizeof(yes)) == -1) {
|
|
<BR> perror("setsockopt");
|
|
<BR> close(lfd);
|
|
<BR> return -1;
|
|
<BR> }
|
|
<P>
|
|
<BR> memset(&addr, 0, sizeof(addr));
|
|
<BR> addr.sin_port = htons(listen_port);
|
|
<BR> addr.sin_family = AF_INET;
|
|
<BR> if (bind(lfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
|
|
<BR> perror("bind");
|
|
<BR> close(lfd);
|
|
<BR> return -1;
|
|
<BR> }
|
|
<P>
|
|
<BR> printf("accepting connections on port %d\n", listen_port);
|
|
<BR> listen(lfd, 10);
|
|
<BR> return lfd;
|
|
}
|
|
<P>
|
|
static int
|
|
connect_socket(int connect_port, char *address)
|
|
{
|
|
<BR> struct sockaddr_in addr;
|
|
<BR> int cfd;
|
|
<P>
|
|
<BR> cfd = socket(AF_INET, SOCK_STREAM, 0);
|
|
<BR> if (cfd == -1) {
|
|
<BR> perror("socket");
|
|
<BR> return -1;
|
|
<BR> }
|
|
<P>
|
|
<BR> memset(&addr, 0, sizeof(addr));
|
|
<BR> addr.sin_port = htons(connect_port);
|
|
<BR> addr.sin_family = AF_INET;
|
|
<P>
|
|
<BR> if (!inet_aton(address, (struct in_addr *) &addr.sin_addr.s_addr)) {
|
|
<BR> fprintf(stderr, "inet_aton(): bad IP address format\n");
|
|
<BR> close(cfd);
|
|
<BR> return -1;
|
|
<BR> }
|
|
<P>
|
|
<BR> if (connect(cfd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
|
|
<BR> perror("connect()");
|
|
<BR> shutdown(cfd, SHUT_RDWR);
|
|
<BR> close(cfd);
|
|
<BR> return -1;
|
|
<BR> }
|
|
<BR> return cfd;
|
|
}
|
|
<P>
|
|
#define SHUT_FD1 do { \
|
|
<BR> if (fd1 >= 0) { \
|
|
<BR> shutdown(fd1, SHUT_RDWR); \
|
|
<BR> close(fd1); \
|
|
<BR> fd1 = -1; \
|
|
<BR> } \
|
|
<BR> } while (0)
|
|
<P>
|
|
#define SHUT_FD2 do { \
|
|
<BR> if (fd2 >= 0) { \
|
|
<BR> shutdown(fd2, SHUT_RDWR); \
|
|
<BR> close(fd2); \
|
|
<BR> fd2 = -1; \
|
|
<BR> } \
|
|
<BR> } while (0)
|
|
<P>
|
|
#define BUF_SIZE 1024
|
|
<P>
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
<BR> int h;
|
|
<BR> int fd1 = -1, fd2 = -1;
|
|
<BR> char buf1[BUF_SIZE], buf2[BUF_SIZE];
|
|
<BR> int buf1_avail = 0, buf1_written = 0;
|
|
<BR> int buf2_avail = 0, buf2_written = 0;
|
|
<P>
|
|
<BR> if (argc != 4) {
|
|
<BR> fprintf(stderr, "Usage\n\tfwd <listen-port> "
|
|
<BR> "<forward-to-port> <forward-to-ip-address>\n");
|
|
<BR> exit(EXIT_FAILURE);
|
|
<BR> }
|
|
<P>
|
|
<BR> signal(SIGPIPE, SIG_IGN);
|
|
<P>
|
|
<BR> forward_port = atoi(argv[2]);
|
|
<P>
|
|
<BR> h = listen_socket(atoi(argv[1]));
|
|
<BR> if (h == -1)
|
|
<BR> exit(EXIT_FAILURE);
|
|
<P>
|
|
<BR> for (;;) {
|
|
<BR> int ready, nfds = 0;
|
|
<BR> ssize_t nbytes;
|
|
<BR> fd_set readfds, writefds, exceptfds;
|
|
<P>
|
|
<BR> FD_ZERO(&readfds);
|
|
<BR> FD_ZERO(&writefds);
|
|
<BR> FD_ZERO(&exceptfds);
|
|
<BR> FD_SET(h, &readfds);
|
|
<BR> nfds = max(nfds, h);
|
|
<P>
|
|
<BR> if (fd1 > 0 && buf1_avail < BUF_SIZE)
|
|
<BR> FD_SET(fd1, &readfds);
|
|
<BR> /* Note: nfds is updated below, when fd1 is added to
|
|
<BR> exceptfds. */
|
|
<BR> if (fd2 > 0 && buf2_avail < BUF_SIZE)
|
|
<BR> FD_SET(fd2, &readfds);
|
|
<P>
|
|
<BR> if (fd1 > 0 && buf2_avail - buf2_written > 0)
|
|
<BR> FD_SET(fd1, &writefds);
|
|
<BR> if (fd2 > 0 && buf1_avail - buf1_written > 0)
|
|
<BR> FD_SET(fd2, &writefds);
|
|
<P>
|
|
<BR> if (fd1 > 0) {
|
|
<BR> FD_SET(fd1, &exceptfds);
|
|
<BR> nfds = max(nfds, fd1);
|
|
<BR> }
|
|
<BR> if (fd2 > 0) {
|
|
<BR> FD_SET(fd2, &exceptfds);
|
|
<BR> nfds = max(nfds, fd2);
|
|
<BR> }
|
|
<P>
|
|
<BR> ready = select(nfds + 1, &readfds, &writefds, &exceptfds, NULL);
|
|
<P>
|
|
<BR> if (ready == -1 && errno == EINTR)
|
|
<BR> continue;
|
|
<P>
|
|
<BR> if (ready == -1) {
|
|
<BR> perror("select()");
|
|
<BR> exit(EXIT_FAILURE);
|
|
<BR> }
|
|
<P>
|
|
<BR> if (FD_ISSET(h, &readfds)) {
|
|
<BR> socklen_t addrlen;
|
|
<BR> struct sockaddr_in client_addr;
|
|
<BR> int fd;
|
|
<P>
|
|
<BR> addrlen = sizeof(client_addr);
|
|
<BR> memset(&client_addr, 0, addrlen);
|
|
<BR> fd = accept(h, (struct sockaddr *) &client_addr, &addrlen);
|
|
<BR> if (fd == -1) {
|
|
<BR> perror("accept()");
|
|
<BR> } else {
|
|
<BR> SHUT_FD1;
|
|
<BR> SHUT_FD2;
|
|
<BR> buf1_avail = buf1_written = 0;
|
|
<BR> buf2_avail = buf2_written = 0;
|
|
<BR> fd1 = fd;
|
|
<BR> fd2 = connect_socket(forward_port, argv[3]);
|
|
<BR> if (fd2 == -1)
|
|
<BR> SHUT_FD1;
|
|
<BR> else
|
|
<BR> printf("connect from %s\n",
|
|
<BR> inet_ntoa(client_addr.sin_addr));
|
|
<P>
|
|
<BR> /* Skip any events on the old, closed file descriptors. */
|
|
<BR> continue;
|
|
<BR> }
|
|
<BR> }
|
|
<P>
|
|
<BR> /* NB: read OOB data before normal reads */
|
|
<P>
|
|
<BR> if (fd1 > 0 && FD_ISSET(fd1, &exceptfds)) {
|
|
<BR> char c;
|
|
<P>
|
|
<BR> nbytes = recv(fd1, &c, 1, MSG_OOB);
|
|
<BR> if (nbytes < 1)
|
|
<BR> SHUT_FD1;
|
|
<BR> else
|
|
<BR> send(fd2, &c, 1, MSG_OOB);
|
|
<BR> }
|
|
<BR> if (fd2 > 0 && FD_ISSET(fd2, &exceptfds)) {
|
|
<BR> char c;
|
|
<P>
|
|
<BR> nbytes = recv(fd2, &c, 1, MSG_OOB);
|
|
<BR> if (nbytes < 1)
|
|
<BR> SHUT_FD2;
|
|
<BR> else
|
|
<BR> send(fd1, &c, 1, MSG_OOB);
|
|
<BR> }
|
|
<BR> if (fd1 > 0 && FD_ISSET(fd1, &readfds)) {
|
|
<BR> nbytes = read(fd1, buf1 + buf1_avail,
|
|
<BR> BUF_SIZE - buf1_avail);
|
|
<BR> if (nbytes < 1)
|
|
<BR> SHUT_FD1;
|
|
<BR> else
|
|
<BR> buf1_avail += nbytes;
|
|
<BR> }
|
|
<BR> if (fd2 > 0 && FD_ISSET(fd2, &readfds)) {
|
|
<BR> nbytes = read(fd2, buf2 + buf2_avail,
|
|
<BR> BUF_SIZE - buf2_avail);
|
|
<BR> if (nbytes < 1)
|
|
<BR> SHUT_FD2;
|
|
<BR> else
|
|
<BR> buf2_avail += nbytes;
|
|
<BR> }
|
|
<BR> if (fd1 > 0 && FD_ISSET(fd1, &writefds) && buf2_avail > 0) {
|
|
<BR> nbytes = write(fd1, buf2 + buf2_written,
|
|
<BR> buf2_avail - buf2_written);
|
|
<BR> if (nbytes < 1)
|
|
<BR> SHUT_FD1;
|
|
<BR> else
|
|
<BR> buf2_written += nbytes;
|
|
<BR> }
|
|
<BR> if (fd2 > 0 && FD_ISSET(fd2, &writefds) && buf1_avail > 0) {
|
|
<BR> nbytes = write(fd2, buf1 + buf1_written,
|
|
<BR> buf1_avail - buf1_written);
|
|
<BR> if (nbytes < 1)
|
|
<BR> SHUT_FD2;
|
|
<BR> else
|
|
<BR> buf1_written += nbytes;
|
|
<BR> }
|
|
<P>
|
|
<BR> /* Check if write data has caught read data */
|
|
<P>
|
|
<BR> if (buf1_written == buf1_avail)
|
|
<BR> buf1_written = buf1_avail = 0;
|
|
<BR> if (buf2_written == buf2_avail)
|
|
<BR> buf2_written = buf2_avail = 0;
|
|
<P>
|
|
<BR> /* One side has closed the connection, keep
|
|
<BR> writing to the other side until empty */
|
|
<P>
|
|
<BR> if (fd1 < 0 && buf1_avail - buf1_written == 0)
|
|
<BR> SHUT_FD2;
|
|
<BR> if (fd2 < 0 && buf2_avail - buf2_written == 0)
|
|
<BR> SHUT_FD1;
|
|
<BR> }
|
|
<BR> exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
<P>
|
|
|
|
The above program properly forwards most kinds of TCP connections
|
|
including OOB signal data transmitted by <B>telnet</B> servers.
|
|
It handles the tricky problem of having data flow in both directions
|
|
simultaneously.
|
|
You might think it more efficient to use a
|
|
<B><A HREF="/cgi-bin/man/man2html?2+fork">fork</A></B>(2)
|
|
|
|
call and devote a thread to each stream.
|
|
This becomes more tricky than you might suspect.
|
|
Another idea is to set nonblocking I/O using
|
|
<B><A HREF="/cgi-bin/man/man2html?2+fcntl">fcntl</A></B>(2).
|
|
|
|
This also has its problems because you end up using
|
|
inefficient timeouts.
|
|
<P>
|
|
|
|
The program does not handle more than one simultaneous connection at a
|
|
time, although it could easily be extended to do this with a linked list
|
|
of buffers---one for each connection.
|
|
At the moment, new
|
|
connections cause the current connection to be dropped.
|
|
<A NAME="lbAM"> </A>
|
|
<H2>SEE ALSO</H2>
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+accept">accept</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+connect">connect</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+ioctl">ioctl</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+poll">poll</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+read">read</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+recv">recv</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+select">select</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+send">send</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+sigprocmask">sigprocmask</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?2+write">write</A></B>(2),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?3+sigaddset">sigaddset</A></B>(3),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?3+sigdelset">sigdelset</A></B>(3),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?3+sigemptyset">sigemptyset</A></B>(3),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?3+sigfillset">sigfillset</A></B>(3),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?3+sigismember">sigismember</A></B>(3),
|
|
|
|
<B><A HREF="/cgi-bin/man/man2html?7+epoll">epoll</A></B>(7)
|
|
|
|
|
|
|
|
<A NAME="lbAN"> </A>
|
|
<H2>COLOPHON</H2>
|
|
|
|
This page is part of release 5.05 of the Linux
|
|
<I>man-pages</I>
|
|
|
|
project.
|
|
A description of the project,
|
|
information about reporting bugs,
|
|
and the latest version of this page,
|
|
can be found at
|
|
<A HREF="https://www.kernel.org/doc/man-pages/.">https://www.kernel.org/doc/man-pages/.</A>
|
|
<P>
|
|
|
|
<HR>
|
|
<A NAME="index"> </A><H2>Index</H2>
|
|
<DL>
|
|
<DT id="21"><A HREF="#lbAB">NAME</A><DD>
|
|
<DT id="22"><A HREF="#lbAC">SYNOPSIS</A><DD>
|
|
<DT id="23"><A HREF="#lbAD">DESCRIPTION</A><DD>
|
|
<DL>
|
|
<DT id="24"><A HREF="#lbAE">Arguments</A><DD>
|
|
<DT id="25"><A HREF="#lbAF">Combining signal and data events</A><DD>
|
|
<DT id="26"><A HREF="#lbAG">Practical</A><DD>
|
|
<DT id="27"><A HREF="#lbAH">Select law</A><DD>
|
|
<DT id="28"><A HREF="#lbAI">Usleep emulation</A><DD>
|
|
</DL>
|
|
<DT id="29"><A HREF="#lbAJ">RETURN VALUE</A><DD>
|
|
<DT id="30"><A HREF="#lbAK">NOTES</A><DD>
|
|
<DT id="31"><A HREF="#lbAL">EXAMPLE</A><DD>
|
|
<DT id="32"><A HREF="#lbAM">SEE ALSO</A><DD>
|
|
<DT id="33"><A HREF="#lbAN">COLOPHON</A><DD>
|
|
</DL>
|
|
<HR>
|
|
This document was created by
|
|
<A HREF="/cgi-bin/man/man2html">man2html</A>,
|
|
using the manual pages.<BR>
|
|
Time: 00:05:34 GMT, March 31, 2021
|
|
</BODY>
|
|
</HTML>
|