Establishing a Simple TCP Server (Using Stream Sockets)

Typically, with TCP, a server "waits" for a connection request from a client. Often, the server program contains a control statement or block of code that executes continuously until the server receives a request. On receiving a request, the server establishes a connection to the client. The server then uses this connectionmanaged by an object of class Socketto handle future requests from that client and to send data to the client. Since programs that communicate via TCP process the data they send and receive as streams of bytes, programmers sometimes refer to Sockets as "stream Sockets."

Establishing a simple server with TCP and stream sockets requires five steps. First, create an object of class TcpListener of namespace System.Net.Sockets. This class represents a TCP stream socket through which a server can listen for requests. Creating a new TcpListener, as in

TcpListener server = new TcpListener( ipAddress, port );

binds (assigns) the server application to the specified port number. A port number is a numeric identifier that an application uses to identify itself at a given network address, also known as an Internet Protocol Address (IP Address). IP addresses identify computers on the Internet. In fact, Web-site names, such as www.deitel.com, are aliases for IP addresses. An IP address is represented by an object of class IPAddress of namespace System.Net. Any application that performs networking identifies itself via an IP address/port number pairno two applications can have the same port number at a given IP address. Explicitly binding a socket to a connection port (using method Bind of class Socket) is usually unnecessary, because class TcpListener and other classes discussed in this chapter do it automatically, along with other socket-initialization operations.

Software Engineering Observation 23 1

Port numbers can have values between 0 and 65535. Many operating systems reserve port numbers below 1024 for system services (such as e-mail and Web servers). Applications must be granted special privileges to use these reserved port numbers.

To receive requests, TcpListener first must listen for them. The second step in the connection process is to call TcpListener's Start method, which causes the TcpListener object to begin listening for connection requests. The server listens indefinitely for a requesti.e., the execution of the server-side application waits until some client attempts to connect with it. The server creates a connection to the client when it receives a connection request. An object of class Socket (namespace System.Net.Sockets) manages a connection to a client. Method AcceptSocket of class TcpListener accepts a connection request. This method returns a Socket object upon connection, as in the statement

Socket connection = server.AcceptSocket();

When the server receives a request, AcceptSocket calls method Accept of the TcpListener's underlying Socket to make the connection. This is an example of how networking complexity is hidden from the programmer. You simply place the preceding statement in a server-side programthe classes of namespace System.Net.Sockets handle the details of accepting requests and establishing connections.

The third step establishes the streams used for communication with the client. In this step, we create a NetworkStream object that uses the Socket object representing the connection to perform the actual sending and receiving of data. In our forthcoming example, we use this NetworkStream object to create a BinaryWriter and a BinaryReader that will be used to send information to and receive information from the client, respectively.

Step four is the processing phase, in which the server and client communicate using the connection established in the third step. In this phase, the client uses BinaryWriter method Write and BinaryReader method ReadString to perform the appropriate communications.

The fifth step is the connection-termination phase. When the client and server have finished communicating, the server calls method Close of the BinaryReader, BinaryWriter, NetworkStream and Socket to terminate the connection. The server can then return to step two to wait for the next connection request. Note that the documentation for class Socket recommends that you call method Shutdown before method Close to ensure that all data is sent and received before the Socket closes.

One problem associated with the server scheme described in this section is that step four blocks other requests while processing the connected client's request, so no other client can connect with the server while the code that defines the processing phase is executing. The most common technique for addressing this problem is to use multithreaded servers, which place the processing-phase code in a separate thread. For each connection request the server receives, it creates a THRead to process the connection, leaving its TcpListener (or Socket) free to receive other connections. We demonstrate a multithreaded server in Section 23.8.

Software Engineering Observation 23 2

Multithreaded servers can efficiently manage simultaneous connections with multiple clients. This architecture is precisely what popular UNIX and Windows network servers use.

Software Engineering Observation 23 3

A multithreaded server can be implemented to create a thread that manages network I/O across a Socket object returned by method AcceptSocket. A multithreaded server also can be implemented to maintain a pool of threads that manage network I/O across newly created Sockets.

Performance Tip 23 1

In high-performance systems with abundant memory, a multithreaded server can be implemented to create a pool of threads. These threads can be assigned quickly to handle network I/O across multiple Sockets. Thus, when a connection is received, the server does not incur the overhead of thread creation.

Категории