96 Chapter 4: Beyond the Basics
■
an application needs the ability to do I/O on multiple channels simultaneously. For exam-
ple, we might want to provide echo service on several ports at once. The problem with
this becomes clear as soon as you consider what happens after the server creates and
binds a socket to each port. It is ready to Accept() connections, but which socket to
choose? A call to Accept() or Receive() on one socket may block, causing established
connections to another socket to wait unnecessarily. This problem can be solved using
nonblocking sockets, but in that case the server ends up continually polling the sockets,
which is wasteful. We would like to let the server block until some socket is ready for I/O.
Fortunately the socket API provides a way to do this. With the static Socket Select()
method, a program can specify a list of sockets to check for pending I/O; Select() sus-
pends the program until one or more of the sockets in the list becomes ready to perform
I/O. The list is modified to only include those Socket instances that are ready.
Select() takes four arguments, the first three of which are lists of Sockets, and the
fourth of which is a time in microseconds (not milliseconds) indicating how long to wait.
A negative value on the wait time indicates an indefinite wait period. The socket lists can be
any class that implements the IList interface (this includes ArrayList, used in our exam-
ple). The lists represent what event you are waiting for; in order, they represent checking
read readiness, write readiness, and error existence. The lists should be populated with
references to the Socket instances prior to call. When the call completes, the lists will
contain only the Socket references that meet that list’s criteria (readability, writability, or
error existence). If you don’t want to check for all these conditions in a single Select()
call, you can pass null for up to two of the lists.
Let’s reconsider the problem of running the echo service on multiple ports. If we
create a socket for each port, we could list those SocketsinanArrayList. A call to
Select(), given such a list, would suspend the program until an echo request arrives for
at least one of our sockets. We could then handle the connection setup and echo for that
particular socket. Our next example, TcpEchoServerSelect.cs, implements this model.
The server runs on three ports: 8080, 8081, and 8082.
TcpEchoServerSelectSocket.cs
0 using System; // For Console, Int32, ArgumentException, Environment
1 using System.Net; // For IPAddress
2 using System.Collections; // For ArrayList
3 using System.Net.Sockets; // For Socket, SocketException
4
5 class TcpEchoServerSelectSocket {
6
7 private const int BUFSIZE = 32; // Size of receive buffer
8 private const int BACKLOG = 5; // Outstanding conn queue max size
9 private const int SERVER1_PORT = 8080; // Port for second echo server
10 private const int SERVER2_PORT = 8081; // Port for second echo server