TCP

TCP
In the previous chapter, we looked at low-level sockets programming for performing network-related tasks. In this chapter, we will look in detail at the higher-level network classes provided in the .NET Framework. We'll start with a general introduction to TCP, and its architecture and data structures. Next, we'll explore the TcpClient and TcpListener classes provided in the .NET Framework for working with TCP. Finally, we'll discuss using the TCP channel with .NET Remoting.

Overview of TCP
TCP, or Transmission Control Protocol, is used as a reliable protocol to communicate across an interconnected network of computers. TCP verifies that the data is properly delivered to the destination in the correct sequence. We'll look briefly at how TCP achieves this in a moment.

TCP is a connection-oriented protocol designed to provide reliable transmission of data from one process to another process running on the same or different computers. The term 'connection-oriented' means that the two processes or applications must establish a TCP connection prior to exchanging any data. This is in contrast to UDP (which we'll look at in the next chapter), which is a 'connection-less' protocol, allowing data to be broadcast to an unknown number of clients.

Encapsulation
When an application sends data using TCP, it travels down the protocol stack. The data is passed through each of the layers and finally transferred across the network as stream of bits.

Each layer in the TCP/IP protocol suite adds some information in the form of headers and/or trailers:


When the packet arrives on the other side of the network, it is again passed through each layer from bottom to top. Each layer strips out its header/trailer information to verify the data and finally it reaches the server application in the same form as it left the client application.

TCP Terminology
Before looking at how TCP establishes a connection with another TCP host, there are a number of terms that we need to define:

Segment
The unit of data that TCP sends to IP is called a TCP segment.

Datagram
The unit of data that IP sends to the Network Interface Layer is called an IP datagram.

Sequence Number
Every TCP segment sent over a connection has a number assigned to it, which is called the 'Sequence Number'. This is used to ensure that the data arrives in the correct order.

TCP Headers
To understand how TCP works, we also need to look quickly at the structure of a TCP header:


The sequence and acknowledgement numbers are used by TCP to ensure that all the data arrives in the correct order, and the control bits contain various flags to indicate the status of the data. There are six of these control bits (usually represented by three-letter abbreviations):

URG-indicates that the segment contains urgent data

ACK-indicates that the segment contains an acknowledgement number

PSH-indicates the data is to be pushed through to the receiving user

RST-resets the connection

SYN-used to synchronize sequence numbers

FIN-indicates the end of data

TCP Connections
TCP uses a process called a 'Three-Phase Handshake' to establish a connection. As the name suggests, this process consists of three steps:

The client initiates communication with the server by sending a segment to the server with the SYN control bit set. This segment contains the client's initial sequence number.

The server responds by sending a segment with both the SYN and ACK bits set. This segment contains the server's initial sequence number (unrelated to that of the client), and the acknowledgement number, which will be equal to the client's sequence number plus one (that is, it is the next sequence number expected from the client).

The client must acknowledge this segment by sending back a segment with the ACK bit set. The acknowledgement number will be the server's sequence number plus one, and the sequence number will be the same as the server's acknowledgement number (that is, the client's original sequence number plus one).


TCP Operations
Now that we've seen the basics of how TCP establishes connections, let's look in a bit more detail at a few TCP operations, to see how TCP transfers data.

Basic Stream Data Transfer
As we've seen, TCP transfers data in byte chunks known as segments. In order to ensure that segments are received correctly and in the correct order, a sequence number is assigned to each segment. The receiver sends an acknowledgement that the segment has been received. If the acknowledgement is not received before the timeout interval expires, the data is resent.

Each octet (eight bits) of data is assigned a sequence number. The sequence number of the segment is the sequence number of the first octet of data within the segment, and this number is sent in the TCP header for the segment. Segments can also have an acknowledgement number, which is the sequence number for the next expected data segment:


TCP uses the sequence numbers to ensure that duplicate data isn't passed on to the receiving application, and that the data is delivered in the correct order. The TCP header contains a checksum which is used to ensure that the data hasn't been corrupted in transit. If a segment with an invalid checksum is received, it is simply discarded, and no acknowledgement is sent. This means that the sender will resend the segment when the timeout value expires.

Flow Control
TCP governs the amount of data sent to it by returning a 'window size' with every acknowledgement. A 'window' is the amount of data that the receiver can accept. A data buffer is placed between the application program and the network data flow. The 'window size' is actually the difference between the size of the buffer and the amount of data stored in it. This number is sent to inform the remote host about the current window size. This is called a 'Sliding Window'. The Sliding Window algorithms control the flow for network data transfers:


The data received is stored in this buffer, and the application can access and read the data from the buffer at its own speed. As the application reads data, the buffer empties itself to accept more input from the network.

If the application is too slow reading the data from the buffer, the window size will drop to zero and the remote host will be told to stop sending the data. As soon as the local application processes the data in the buffer, the window size increases and can start receiving data from the network again.

If the size of the window is greater than the packet size, the sender knows that the receiver can hold multiple packets at once, improving the performance.

Multiplexing
TCP allows many processes on a single machine to use a TCP socket simultaneously. A TCP socket consists of a host address and a unique port number, and a TCP connection comprises two sockets on different ends of a network. A port may be used for simultaneous multiple connections-a single socket on one end may be used for several connections with different sockets on the other end. An example of this is a web server listening on port 80 answering requests from more than one computer.

Introduction to TCP in .NET
.NET support for TCP sockets is a great improvement on the previous programming model. Previously, most developers using Visual C++ have either employed the CSocket and CAsyncSocket classes for manipulating all types of socket communication, or used third-party programming libraries. There was almost no built-in support for higher-level TCP programming. In .NET, there is a separate namespace provided for working with sockets-the System.Net.Sockets namespace (as discussed in the previous chapter). This namespace contains low-level classes such as Socket as well as higher-level classes such as TcpClient and TcpListener to offer simple interfaces to TCP communication.

The TcpClient and TcpListener classes follow the stream model for sending and receiving data, as opposed to the Socket class, which employs the byte-level approach. In these classes, all communication between the client and the socket is based on a stream, using the NetworkStream class. However, we can also work with bytes where necessary.

The TcpClient Class
The TcpClient class provides client-side connections for TCP services. It is built upon the Socket class to provide TCP Services at a higher level-TcpClient has a private data member called m_ClientSocket, which is used to communicate with the TCP server. The TcpClient class provides simple methods for connecting to another socket application over the network, and for sending and receiving data to it. The important class members of TcpClient class are listed below:

Public Properties
Name
Type
Description

LingerState
LingerOption
Sets or returns a LingerOption object that holds information about whether the connection will remain open after the socket is closed, and if so for how long.

NoDelay
bool
Specifies whether the socket will delay sending or receiving data if the send or receive buffer isn't full. If set to false, TCP will delay sending the packet until there is sufficient data in order to avoid the inefficiency of sending very small packets over the network.

ReceiveBufferSize
int
Specifies the size of the buffer for incoming data (in bytes). This property is used when reading data from a socket.

ReceiveTimeout
int
Specifies the length of time in milliseconds that the TcpClient will wait to receive data once initiated. A SocketException will be thrown after this time has elapsed if no data is received.

SendBufferSize
int
Specifies the size of the buffer for outgoing data.

SendTimeout
int
Specifies the length of time in milliseconds that the TcpClient will wait to receive confirmation of the number of bytes sent to the remote host from the underlying Socket after a send is initiated. A SocketException will be thrown if the SendTimeOut expires.


Public Methods
Name
Description

Close()
Closes the TCP connection.

Connect()
Connects to a remote TCP host.

GetStream()
Returns the NetworkStream used for transferring data between the client and the remote host.


Protected Properties
Name
Type
Description

Active
bool
Specifies whether there is an active connection to a remote host.

Client
Socket
Specifies the underlying Socket used by the TcpClient. Because this property is protected, the underlying socket can only be accessed if you derive your own class from TcpClient.


Instantiating a TcpClient
The constructor for the TcpClient class has three overloads:

public TcpClient();
public TcpClient(IPEndPoint ipEnd);
public TcpClient(string hostname, int port);
The default constructor initializes a TcpClient instance:


TcpClient newClient = new TcpClient();

If a TcpClient is instantiated in this way, we must call the Connect() method with an IPEndPoint to establish a connection to a remote host.

The second overload takes a single parameter of type IPEndPoint. This initializes a new instance of the TcpClient class bound to the specified endpoint. Notice that this IPEndPoint is the local endpoint, not the remote endpoint. If we try to pass a remote endpoint to the constructor, an exception will be thrown, stating that the IP address isn't valid in this context.

If we use this constructor, we still have to call the Connect() method with the remote endpoint after creating the TcpClient object:


// Create a local end point
IPAddress ipAddr = IPAddress.Parse("192.168.1.51");
IPEndPoint endPoint = new IPEndPoint(ipAddr, 11100);

TcpClient newClient = new TcpClient(endPoint);

// You must call connect() to make a connection with the server
newClient.Connect("192.168.1.52", 11000);

The parameter passed to TcpClient constructor is the local endpoint, whereas the Connect() method actually connects the client with the server, so takes the remote endpoint as a parameter.

The last overload creates a new instance of the TcpClient class and establishes a remote connection using a DNS name and port number parameters passed in as arguments:


TcpClient newClient = new TcpClient("localhost", 80);

This is the most convenient method, as it allows us to initialize the TcpClient, resolve the DNS name, and connect with the host in one easy step. However, notice that we can't specify the local port to which we want to bind using this overload.

Establishing a Connection with the Host
Once we've instantiated a TcpClient, the next step is to establish a connection with the remote host. The Connect() method is provided to connect the client with the TCP host. We only need to call this method if we used the default constructor or a local endpoint to instantiate the TcpClient; otherwise, if we pass a hostname and port number into the constructor, attempting to call Connect() will cause an exception.

There are three overloads for the Connect() method:

public void Connect(IPEndPoint endPoint);
public void Connect(IPAddress ipAddr, int port);
public void Connect(string hostname, int port);
These are fairly self-explanatory, but we'll quickly demonstrate examples of using each overload:

Passing in an IPEndPoint object representing the remote endpoint we want to connect to:


// Create a new instance of the TcpClient class
TcpClient newClient = new TcpClient();

// Establish a connection with, the IPEndPoint
IPAddress ipAddr = IPAddress.Parse("127.0.0.1");
IPEndPoint endPoint = new IPEndPoint(ipAddr, 80);

// Connect with the host using IPEndPoint
newClient.Connect(endPoint);


Passing in an IPAddress object and a port number:


// Create a new instance of the TcpClient class
TcpClient newClient = new TcpClient();

// Establish a connection with the IPEndPoint
IPAddress ipAddr = IPAddress.Parse("127.0.0.1");

// Connect with the host using IPAddress and port number
newClient.Connect(ipAddr, 80);


Passing in a hostname and port number:


// Create a new instance of the TcpClient class
TcpClient newClient = new TcpClient();

// Connect with the host using hostname as string and port
newClient.Connect("127.0.0.1", 80);


If there is a connection failure or other problem, a SocketException will be thrown:


try
{
TcpClient newClient = new TcpClient();

// Connect to the server
newClient.Connect("192.168.0.1", 80); // Socket raises exception here
// if there is some problem with the
// connection
}
catch(SocketException se)
{
Console.WriteLine("Exception: " + se);
}

Sending and Receiving Messages
The NetworkStream class is used for stream-level processing as a communication channel between two connected applications. This class has already been discussed in Chapter 2, so here we'll just look at using it with a TcpClient.

Before sending and receiving any data, we need to get the underlying stream. TcpClient provides a GetStream() method exclusively for this purpose. GetStream() creates an instance of the NetworkStream class using the underlying socket and returns it to the caller. The following code sample demonstrates how to get a network stream using the GetStream() method.

We assume that newClient is an instance of the TcpClient, and that a connection with the host has already been established. Otherwise, an InvalidOperation exception would be thrown.


NetworkStream tcpStream = newClient,GetStream();

After getting the stream, we can use the Read() and Write() methods of NetworkStream to actually read from the host application and write to it.

The Write() method takes three parameters-a byte array containing the data we want to send to the host, the position in the stream where we want to start writing, and the length of the data:


byte[] sendBytes = Encoding.ASCII.GetBytes("This is a Test");
tcpStream.Write(sendBytes, 0, sendBytes.Length);

The Read() method has exactly the same set of parameters-a byte array to store the data that we read from the stream, the position to start reading, and the number of bytes to read:


byte[] bytes = new byte[newClient.ReceiveBufferSize];
int bytesRead = tcpStream.Read(bytes, 0, newClient.ReceiveBufferSize);

// Convert from bytes to string
// returnData will contain the incoming data from socket
string returnData = Encoding.ASCII.GetString(bytes);

The TcpClient's ReceiveBufferSize property allows us to get or set the size of the receive buffer (in bytes), so we use it as the size of the byte array. Note that setting this property doesn't restrict the number of bytes we can read in each operation, as the buffer will be dynamically resized if necessary, but it does reduce overhead if we specify a buffer size.

Closing a TCP Socket
After communicating with the client, the Close() method should be called to free all resources:


// Close client socket
newClient.Close();

That is all that is required to use the TcpClient class to communicate with the server.

Apart from this basic functionality, if we need to access the socket instance underlying the TcpClient object, for example to set options by calling SetSocketOption(), we can call the Client property to access the members of the underlying Socket. We can also use the Client property to set the TcpClient's underlying socket to an existing Socket object. But since this is a protected member of the TcpClient class, we must inherit from TcpClient before using it. The Client property allows protected access to the private m_ClientSocket member we mentioned earlier. The TcpClient class passes calls made on it to the Socket class's parallel method after checking the validity of parameters and initializing the socket instance. The m_ClientSocket object is instantiated in the constructor. The constructor calls the private initialize() method that constructs the new Socket object and then calls the set_Client() method to assign it to the Client property. The method also sets the m_Active Boolean value, which is used to track the state of the Socket instance. It also checks for redundant Socket connections and for any operation that requires the connection. A separate protected property, Active, is provided for setting and getting the value of the private m_Active member.

There are a lot of socket options that the TcpClient class does not cover. If we want to set or get any of these properties that are not exposed by the TcpClient (such as Broadcast or KeepAlive), we need to derive a class from TcpClient and use its Client member.

The following code demonstrates how to use these protected properties, and specifically the Active member of the TcpClient class:


using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;

public class PSocket : TcpClient
{
public PSocket() : base()
{
}

public PSocket(string ipaddress, int port) : base(ipaddress, port)
{
}

public static void Main()
{
// Creating a TcpClient object but not connecting
PSocket ps2 = new PSocket();

// Showing the state of the socket
Console.WriteLine("trackActive: " + ps2.Active);

// Connecting with the client
ps2.Connect("127.0.0.1", 11000);

// Checking the state of the socket
Console.WriteLine("trackActive: " + ps2.Active);

// Creating another TcpClient class, this time connecting
// within the constructor
PSocket newClient = new PSocket("127.0.0.1", 11000);

// Checking the state of the other socket
Console.WriteLine("trackActive: " + newClient.Active);

// Getting the internal protected socket member
Socket s = newClient.Client;

// Use the socket to set an option
s.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
}
}

Building a Real World Socket Application
To demonstrate the use of the TcpClient class, we'll build a simple e-mail client implementing two of the most common protocols in the Internet world. These are SMTP and POP3 protocols. We won't show the POP3 implementation in this chapter, as another implementation is provided in Chapter 9), but it's important to see how to implement application-level protocols such as this, and SMTP provides a useful example because of its simplicity.

Simple Mail Transfer Protocol (SMTP)
The following figure shows the basic model behind the SMTP protocol involved in completing a mail transaction:


The process starts when the user sends the mail. The sending system contacts the receiving system on TCP port 25 to establish a communication link. The receiving system, which may be the final destination or an intermediate system, replies back with status messages to inform the sender that it's ready to receive the message. These responses consist of a three-digit status code and a human-readable message. The receiving system continues by sending commands followed by the original e-mail. The receiving system responds appropriately with status messages and commands. The link is terminated when the sender has finished sending the message and disconnects itself from the receiving system.

SMTP Commands
The command syntax for the SMTP protocol isn't very difficult, and contains very few commands:

HELO. The HELO command is used to initiate communications between the sending and receiving hosts. This command is accompanied by a parameter identifying the sending host. The receiving host then identifies itself back to the sending host; this places both the machines into the ready state to begin communications. If successful, this command should be met with the reply code 250, which indicates that the command completed without error.

An example HELO command and the host's response would be:

Sending System: HELO wrox.com
Receiving System: 250 gg.mail.com

MAIL. This command lets the receiving system know who is sending the mail message so that any error in delivering the mail message can be directed back to the original e-mail sender. Since mail can be intercepted en route by more than one host, it is used to identify the final destination of the e-mail. Most e-mail servers put some restriction on this parameter, such as ensuring that the user's domain name is the same as the e-mail server's domain, to prevent anonymous e-mails and spamming.

An example MAIL command and response would be:

Sending System: MAIL FROM:
Receiving System: 250 OK

RCPT. This command is used to send the mailbox names of the users to whom the mail is being sent. It's possible to have more than one recipient for a particular mail message.

Identifying the Recipient of the Mail Message

Sending System: RCPT T0:
Receiving System: 250 OK

DATA. This command indicates that the information that follows contains the body of the message file. The message end is indicated by the sending system's transmission of a line containing only a period (.). The following listing shows the transaction between the sending and the receiving system. Note that the subject of the mail is specified within this data by adding a SUBJECT: line just after sending the DATA command. The text between SUBJECT: and the carriage return/line feed characters construct the subject of the message:

Sending System: DATA
Receiving System: 354 Ready to receive data...
Sending System: SUBJECT:Subject of the messageThis is a test
message. .
Receiving System: 250 OK

After the receiving system gets the end of message line, it replies to the sender about the status of the message.

QUIT. This command indicates that the sending system is ready to close down the communication link between the sender and the receiver:

Sending System: QUIT
Receiving System: 221 web.akros.net closing connection.

SMTP Communication Sample
Below you can find an example of a successful SMTP session between a sending and the receiving system. For each command and response, CLIENT represents the system that will be initiating and sending the mail, and SERVER represents the system to which the mail is sent:

[Establish Connection with SMTP Server]
Connection Established with xyz.com
SERVER: 220 web1.xyz.com ESMTP SendMail
CLIENT: HELD xyz.com
SERVER: 250 OK
CLIENT: MAIL FROM:
SERVER: 250 ... Sender OK
CLIENT: RCPT TO:
SERVER: 250 ... Recepient OK
CLIENT: DATA
SERVER: 354 Enter mail, end with "." on a line by itself
CLIENT: SUBJECT: test
test
.
SERVER: 250 asdkauy83 Message accepted for delivery
CLIENT: QUIT
SERVER: 221 web.xyz.com Closing Connection
To test this, you can use a Telnet application and connect to your local SMTP server on port 25. Then you can manually type the client portion of the example into the Telnet application to get a more 'hands on' feel for the process. We look at a sample SMTP Telnet session in Chapter 1.

SMTP Client in .NET
Our e-mail client application will be a Windows Application, but we'll keep the user interface as simple as possible. There will be single-line textboxes to specify the SMTP server we want to connect to, the From and To fields for the e-mail, and the subject of the e-mail. We'll also have a multi-line textbox for the body of the e-mail and a listbox where we'll display status messages:


There are two tabs on the form, one for sending e-mails via SMTP, and the other for retrieving them from an inbox via POP. The SMTP tab contains the following controls (we show the text of the labels associated with the controls, so that they can be identified in the screenshot above):

Name
Type
Associated Label Text

txtSmtpServer
text box
Smtp Server:

txtFrom
text box
From:

txtTo
text box
To:

txtSubject
text box
Subject:

lstLog
list box
Status Messages:

txtMessage
text box
Message to Send:

btnSend
button
-


All the code for connecting to the server and sending the message is implemented in the Click event handler for the Send button. We start by making a connection to the SMTP server:

private void btnSend_Click(object sender, System.EventArgs e)
{
// Create an instance of the TcpClient class
TcpClient smtpServer = new TcpClient(txtSmtpServer.Text, 25);
lstLog.Items.Add("Connection Established with smtpserver.com");
The next step is to build the Stream classes to communicate with the SMTP server. We have used a NetworkStream for writing a stream to the server, but a StreamReader for reading from the server. This provides a ReadLine() method that is much easier to use than calling the NetworkStream's Read() method, which reads the stream into bytes that we then have to convert into a string using the Encoding class. The StreamReader's ReadLine() method returns a string.


// Create the stream classes for communication
NetworkStream writeStream = smtpServer.GetStream();
StreamReader readStream = new StreamReader(smtpServer.GetStream());

Once we've connected to the server, it will send us a message back to tell us that the connection has been made. We read this message using the StreamReader.ReadLine() method, and display it in the list box:


// Retrieve connection success message
receiveData = readStream.ReadLine();

// Add it to the listbox
lstLog.Items.Add(receiveData);

Next, we send the user's e-mail address to the server by writing an SMTP MAIL FROM command to our NetworkStream object. Again, the server will send a response message, which we retrieve from the StreamReader and add to the listbox:


// Send 'From' E-mail Address
sendString = "MAIL FROM: " + "<" + txtFrom.Text + ">\r\n";
dataToSend = Encoding.ASCII.GetBytes(sendString);
writeStream.Write(dataToSend, 0, dataToSend.Length);

// Display response message
receiveData = readStream.ReadLine();
lstLog.Items.Add(receiveData);

Then we send the destination e-mail address in a RCPT TO command, and again display the server's response:


// Sending 'To' E-mail Address
sendString = "RCPT TO: " + "<" + txtTo.Text + ">\r\n";
dataToSend = Encoding.ASCII.GetBytes(sendString);
writeStream.Write(dataToSend, 0, dataToSend.Length);

// Display response message
receiveData = readStream.ReadLine();
lstLog.Items.Add(receiveData);

Now, after both the e-mail addresses have been authenticated, the actual data (including the e-mail's subject) is sent following the DATA SMTP command:


// Send data
sendString = "DATA " + "\r\n";
dataToSend = Encoding,ASCII.GetBytes(sendString);
writeStream.Write(dataToSend, 0, dataToSend.Length);

// Display response message
receiveData = readStream.ReadLine();
1stLog.Items.Add(receiveData);

// Sending Message Subject and Text
sendString = "SUBJECT: " + txtSubject.Text + "\r\n" + txtMessage.Text +
"\r\n" + "." + "\r\n";
dataToSend = Encoding.ASCII.GetBytes(sendString);
writeStream.Write(dataToSend, 0, dataToSend.Length);

receiveData = readStream.ReadLine();
1stLog.Items.Add(receiveData);

The last step is to send the QUIT command to the server and free any resources used by the application:


// Send Disconnect Message to Server
sendString = "QUIT " + "\r\n";
dataToSend = Encoding.ASCII.GetBytes(sendString);
writeStream.Write(dataToSend,0,dataToSend.Length);
receiveData = readStream.ReadLine();
1stLog.Items.Add(receiveData);

// Close all open resources
writeStream.Close();
readstream.Close();
smtpServer.Close();
}

Post Office Protocol (POP)
The second tab of our e-mail client application allows us to read e-mails from an inbox using the POP3 protocol:


The complete code for this application is available in the code download for the book, but since we present a very similar example in Chapter 9, we won't show the code here.

Implementing an FtpWebRequest Class
The second example we'll look at is a bit more complicated-we'll implement FtpWebRequest and FtpWebResponse classes that will allow us to download files from or upload them to an FTP server in much the same way that we can access files using the FileWebRequest and FileWebResponse classes that we saw in Chapter 3.

Overview of FTP
File Transfer Protocol (FTP) is, as we saw in Chapter 1, an application-level protocol built on top of a transport-level protocol, usually TCP. It is used for uploading and downloading files on a remote server. In many ways, implementing an FTP client is very similar to our implementation of the SMTP client in the previous section-we open up a TCP connection to the server, send text commands to perform actions such as retrieving a file from the server, and the server returns a three-digit code (along with a human-readable message) to indicate the status of the requested action.

Where FTP differs from SMTP, however, is that two different connections are used-the control connection, on which we send the commands and receive the server's responses; and the data connection, which is used for the actual transfer of the files to be downloaded or uploaded. By default, the server listens on port 21 for commands from the client, and when it needs to send data will open a second connection to port 20 of the client.

Active and Passive Modes
The data connection is opened only when a command has been sent to upload or download a file. In active mode (the default), the client must listen for connections. When data needs to be sent, the FTP server will open up a connection to this socket, and transfer the data over to the client.

The problem with this approach is that most firewall configurations won't permit connections from outside to machines behind the firewall-they will only allow connections that were initialized from behind the firewall. FTP's answer to this is passive mode. In this case, the client sends a command to indicate that passive mode is to be used, and the server will respond with the port number that the server is listening on. When the data needs to be sent, the client can now open up a connection to the specified port, instead of having to listen for the server's request. Our implementation will use passive mode.

FTP Commands
The FTP specification defines a number of commands for authentication, uploading and downloading files, and changing the directory on the server. We won't use all of these commands in our code, so we'll only look at those that we do use:

Command
Description

USER
The username to be authenticated on the server

PASS
The password associated with the username

RETR
Download the specified file

STOR
Upload a file and store it in the specified location

TYPE
The format for the data. This can be one of:

A-ASCII

E-EBCDIC

I-Image (binary data)

L -Local byte size

PASV
Use passive mode

STAT
Cause the server to send a status message to the client. This can be used while data is being transferred, to indicate the status of the operation

QUIT
Close the connection to the server


FTP Status Codes
As with SMTP reply codes, the three digit FTP codes are ordered according to the granularity of detail provided by the digit-the first digit gives a general indication of the status of the command; the second digit indicates the general type of error that occurred; and the third digit gives more specific information. The possible first digits are:

Digit
Description

1
Positive preliminary response-the requested action is being initiated, and another response will be sent before the client should send a new command

2
Positive completion response-the requested action has been completed

3
Positive intermediate response-the command has been accepted, but the server requires more information before proceeding

4
Temporary negative response-the command was rejected, but the error is temporary, and the command can be resent

5
Permanent negative response-the command was rejected


Some of the specific responses that we will handle are:

Code
Description

125
Data connection open-starting transfer

150
About to open data connection

200
Command accepted

220
Service ready for new user

227
Entering passive mode

230
User logged in

331
Username accepted-send password


Coding an FTP Client
Now that we've had a quick overview of the FTP protocol, we can start to implement a client in .NET. As we mentioned above, to make this class as intuitive as possible for developers using it, we'll do this by implementing an FtpWebRequest class that inherits from WebRequest and can be used in the same way. This is implemented as a Class Library project, and consists of five classes:

FtpRequestCreator-Used when we register the "ftp" prefix with WebRequest

FtpWebRequest-Represents a request to download or upload a file on an FTP server

FtpWebResponse-Represents the response from the server

FtpWebStream-Represents the stream between the client and server

FtpClient-The utility class which we use for connecting to the server and executing the FTP commands

The FtpRequestCreator Class
The first thing we need to do is create an implementation of the IWebRequestCreate interface. This interface has one method, Create(), which is called by the WebRequest.Create() static method. In this method, we simply return a new instance of our FtpWebRequest class:


using System;
using System.Net;

namespace Wrox.Networking.TCP.FtpUtil
{
public class FtpRequestCreator : IWebRequestCreate
{
public FtpRequestCreator()
{
}

public System.Net.WebRequest Create(System.Uri uri)
{
return new FtpWebRequest(uri);
}
}
}

When we want to create an FtpWebRequest object, we need to register the "ftp" prefix, and pass in an FtpRequestCreator object, so that the WebRequest class knows that it must use this class to handle any web requests beginning with "ftp" (we'll see how to do this later on).

The FtpWebRequest Class
Next, we can define the FtpWebRequest class itself. This will have five data members, to store the username and password of the user we will connect as, the URI we want to connect to, a Boolean value indicating whether the data is to be in binary rather than ASCII format, and a string representing the 'method' for the request. This last field represents the command we want to execute against the server; to make this more accessible to users who aren't familiar with the FTP protocol, we will allow this to be set to "GET" instead of "RETR", and "PUT" instead of "STOR". These values are also exposed as public properties of the class. The constructor of the class simply sets the uri field to the Uri passed in. The only other method in the class is the GetResponse() method, which simply instantiates and returns a new FtpWebResponse object, passing the current FtpWebRequest as a parameter:


using System;
using System.Net;

namespace Wrox.Networking.TCP.FtpUti1
{
public class FtpWebRequest : WebRequest
{
private string username = "anonymous";
internal string password = "someuser@somemail.com";
private Uri uri;
private bool binaryMode = true;
private string method = "GET";

internal FtpWebRequest(Uri uri)
{
this.uri = uri;
}

public string Username
{
get { return username; }
set { username = value; }
}

public string Password
{
set { password = value; }
}

public bool BinaryMode
{
get { return binaryMode; }
set { binaryMode = value; }
}

public override System.Uri RequestUri
{
get { return uri; }
}

public override string Method
{
get { return method; }
set { method = value; }
}

public override System.Net.WebResponse GetResponse()
{
FtpWebResponse response = new FtpWebResponse(this);
return response;
}
}
}

The FtpWebResponse Class
Next, we define the FtpWebResponse class. This has two private data members-the FtpWebRequest object with which it is associated, and an instance of a class called FtpClient, where we will implement most of the actual code for communicating with the FTP server. The class's constructor simply sets the request field to the FtpWebRequest object that is passed in:


using System;
using System.IO;
using System.Net;
using System.Net.Sockets;

namespace Wrox.Networking.TCP.FtpUtil
{
public class FtpWebResponse : WebResponse
{
private FtpWebRequest request;
private FtpClient client;

internal FtpWebResponse(FtpWebRequest request)
{
this.request = request;
}

The GetResponseStream() method is where we actually connect to the server and download/upload the data. This method first separates the URI we want to connect to into its component parts-the name of the server, and the path and name of the file we want to download or save to the server. Next, we create an instance of the FtpClient class, passing in the username and password from the FtpWebRequest object, and we use this object to connect to the server. Next, we execute the command represented by the FtpWebRequest object. This could be either GET/RETR to download a file, or PUT/STOR to upload a file. In either case, we retrieve a NetworkStream object that is used to represent the stream between the client and the server. If the method isn't one of these, we throw an exception to state that the method isn't supported. Finally, we create a new FtpWebStream object from this NetworkStream, and return it to the caller:


public override System.IO.Stream GetResponseStream()
{
// Split up URI to get hostname and filename
string hostname;
string filename;
GetUriComponents(request.RequestUri.ToString(), out hostname,
out filename);

// Connect to the FTP server and get a stream
client = new FtpClient(request.Username, request.password);
client.Connect(hostname);

NetworkStream dataStream = null;
switch(request.Method)
{
case "GET":
case "RETR":
dataStream = client.GetReadStream(filename,
request.BinaryMode);
break;
case "PUT":
case "STQR":
dataStream = client.GetWriteStream(filename,
request.BinaryMode);
break;
default:
throw new WebException("Method " + request.Method +
" not supported");
}

// Create and return an FtpWebStream
// (to close the underlying objects)
FtpWebStream ftpStream = new FtpWebStream(dataStream, this);
return ftpStream;
}

The GetUriComponents() method parses a URI in string format and populates two output parameters with the hostname and the filename components from this:


private void GetUriComponents(string uri, out string hostname,
out string fileName)
{
// Check that URI has at least 7 characters, or we'll get an error
uri = uri.ToLower();
if (uri.Length < 7)
throw new UriFormatException("Invalid URI");

// Check that URI starts "ftp://", and remove that from the start
if (uri.Substring(0, 6) != "ftp://")
throw new NotSupportedException(
"Only FTP requests are supported");
else
uri = uri.Substring(6, uri.Length - 6);

// Divide the rest of the URI into the hostname and the filename
string[] uriParts = uri.Split(new char[] { '/' }, 2);
if (uriParts.Length != 2)
throw new UriFormatException("Invalid URI");

hostname = uriParts[0];
fileName = uriParts[1];
}

Finally, the Close() method simply calls Close() on the FtpClient object:


public override void Close()
{
client.Close();
}
}
}

The FtpWebStream Class
The FtpWebStream class inherits from Stream. It has two private fields-the FtpWebResponse object that returns it to the client application, and the underlying NetworkStream between the client and the FTP server. Again, the constructor just populates these fields:


using System;
using System.IO;
using System.Net.Sockets;

namespace Wrox.Networking.TCP.FtpUtil
{
internal class FtpWebStream : Stream
{
private FtpWebResponse response;
private NetworkStream dataStream;

public FtpWebStream(NetworkStream dataStream, FtpWebResponse response)
{
this.dataStream = dataStream;
this.response = response;
}

The remaining methods and properties simply pass on the call to the underlying NetworkStream, or (if the functionality isn't supported), throw a NotSupportedException:


public override void Close()
{
response.Close();
base.Close();
}

public override void Flush()
{
dataStream.Flush();
}

public override int Read(byte[] buffer, int offset, int count)
{
return dataStream.Read(buffer, offset, count);
}

public override long Seek(long offset, System.IO.SeekOrigin origin)
{
throw new NotSupportedException("Seek not supported");
}

public override void SetLength(long value)
{
throw new NotSupportedException("SetLength not supported");
}

public override voidWrite (byte[] buffer, int offset, int count)
{
dataStream.Write(buffer, offset, count);
}

public override bool CanRead
{
get { return dataStream.CanRead; }
}

public override bool CanSeek
{
get { return false; }
}

public override bool CanWrite
{
get { return dataStream.CanWrite; }
}

public override long Length
{
get { throw new Not SupportedExcept ion ("Length not supported"); }
}

public override long Position
{
get { throw new NotSupportedException("Position not supported"); }
set { throw new NotSupportedException("Position not supported"); }
}
}
}

The FtpClient Class
The final class in our project, FtpClient, is where most of the actual work is done. This class is only intended to be called from within our application, so we've set its accessibility to internal, and also made the class sealed. The class has seven private fields:

bufferSize-A constant representing the size of the buffer we'll use for reading from and writing to the NetworkStream

controlStream-The NetworkStream that is used to send commands and receive responses from the FTP server

dataStream-The NetworkStream that is used to send data to and receive it from the FTP server

username-A string representing the name of the user, for authenticating against the FTP server

password-The password associated with the username

client-A TcpClient object used to make the control connection to the FTP server

dataClient-A TcpClient object used to make the data connection to the FTP server


using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Text;

namespace Wrox.Networking.TCP.FtpUtil
{
internal sealed class FtpClient
{
private const int bufferSize = 65536;
private NetworkStream controlStream;
private NetworkStream dataStream;
private TcpClient client;
private string username;
private string password;
private TcpClient dataClient = null;

The constructor takes the username and password as parameters, and stores these in the data members:


public FtpClient(string username, string password)
{
this.username = username;
this.password = password;
}

The Connect() method makes the initial connection with the FTP server. It opens a connection to port 21 (the default port for the control connection to an FTP server) using the TcpClient class, and retrieves the NetworkStream for this connection. We then call the GetResponse() method, which retrieves the status code and message from the stream. If all is well, this should be a 220 'service ready for new user' response, so we log on using the username and password. If any other status code is returned, something has gone wrong, so we throw an exception:


public void Connect(string hostname)
{
// Set the private fields representing the TCP control connection to
// the server and the NetworkStream used to communicate with the
// server
client = new TcpClient(hostname, 21);
controlStream = client.GetStream();

string responseMessage;
if (GetResponse(out responseMessage) != 220)
{
throw new WebException(responseMessage);
}

Logon(username, password);
}

The GetResponse() method is used to read a response from the FTP server on the control connection. It returns the three-digit status code as an integer and populates an output parameter with the response message:


public int GetResponse(out string responseMessage)
{
// Read the response from the server, trim any nulls, and return it.
byte[] response = new byte[client.ReceiveBufferSize];
control Stream.Read(response, 0, response.Length);
responseMessage = Encoding.ASCII.GetString(response) .Replace(
"\0", "");

return int.Parse(responseMessage.Substring(0, 3));
}

The Logon() method sends a USER command to the server. If the server requires a password, it will respond with a 331 response, in which case we send a PASS command with the user's password. Otherwise, it should return a 230 response to say that the user has logged in successfully. If any other response is returned, we throw an UnauthorizedAccessException.

Note that FTP has no built-in security features for protecting passwords, so passwords must be sent in plain text format. For this reason, sensitive passwords should not be sent.


private void Logon(string username, string password)
{
// Send a USER FTP command. The server should respond with a 331
// message to ask for the user's password.
string respMessage;
int resp = SendCommand("USER" + username, out respMessage);

if (resp != 331 && resp != 230)
throw new UnauthorizedAccessException(
"Unable to login to the FTP server");

if (resp != 230)
{
// Send a PASS FTP command. The server should respond with a 230
// message to say that the user is now logged in.
resp = SendCommand("PASS " + password, out respMessage);
if (resp != 230)
throw new UnauthorizedAccessException(
"FTP server can't authenticate username");
}
}

The next two methods simply return a NetworkStream for downloading or uploading a file via the data connection, by calling the DownloadFile() or UploadFile() method (we'll look at these next):


public NetworkStream GetReadStream(string filename, bool binaryMode)
{
return DownloadFile(filename, binaryMode);
}

public NetworkStream GetWriteStream(string filename, bool binaryMode)
{
return UploadFile(filename, binaryMode);
}

The DownloadFile() method opens the data connection to the FTP server, and then sets the binaryMode to specify whether we're expecting binary or ASCII data. Next, we send a RETR command to tell the server we want to download a file. This should be met with a 125 or 150 response to signal that the data connection either is already open or is about to be opened. If it isn't, we throw a WebException; otherwise, we return the NetworkStream from our dataClient TcpClient:


private NetworkStream DownloadFile(string filename, bool binaryMode)
{
if (dataClient == null)
dataClient = CreateDataSocket();

SetBinaryMode(binaryMode);

string respMessage;
int resp = SendCommand("RETR " + filename, out respMessage);

if(resp ! = 150 && resp ! = 125)
throw new WebException(respMessage);

dataStream = dataClient.GetStream();

return dataStream;
}

The UploadFile() method is almost identical, except that a STOR command is sent instead of a RETR command:


private NetworkStream UploadFile(string filename, bool binaryMode)
{
if (dataClient == null)
dataClient = this.CreateDataSocket();

// Set binary or ASCII mode
SetBinaryMode(binaryMode);

// Send a STOR command to say we want to upload a file.
string respMessage;
int resp = SendCommand("STOR " + filename, out respMessage);

// We should get a 150 response to say that the server is
// opening the data connection,
if (resp != 150 && resp != 125)
throw new WebException("Cannot upload files to the server");

dataStream = dataClient.GetStream();
return dataStream;
}

The SetBinaryMode() method allows us to specify whether we want to retrieve binary or text (ASCII) data. We do this by sending a TYPE command to the FTP server with the parameter A for ASCII data or I for image (binary) data. We should receive a 200 response code if all went well:


private void SetBinaryMode(bool binaryMode)
{
int resp;
string respMessage;
if(binaryMode)
resp = SendCommand("TYPE I", out respMessage);
else
resp = SendCommand("TYPE A", out respMessage);

if (resp != 200)
throw new WebException(respMessage);
}

The CreateDataSocket() method is the most complicated of our methods. First, we send a PASV command to the server to say that we want to use passive mode; the server responds with a message similar to the following:

227 Entering Passive Mode (192,168,0,1,177,147).
The first four values in the parentheses represent the IP address, and the last two the port number the server is listening on. We use these values to retrieve the IP address and port number used by the server for data connections. These are returned as 8-bit values, so we calculate the IP address by concatenating the values (separated by period signs) into a string in the normal quad format (for example, "192.168.0.1"), and the port number by shifting the fifth value left by eight bits, and adding the sixth value. Once we've done this, we can create and return a TcpClient object to open up the data connection to the specified IP address and port number:


private TcpClient CreateDataSocket()
{
// request server to listen on a data port (not the default data
// port) and wait for a connection
string respMessage;
int resp = SendCommand("PASV", out respMessage);
if (resp != 227)
throw new WebException(respMessage);

// The response includes the host address and port number
// IP address and port number separated with ','
// Create the IP address and port number
int[] parts = new int[6];
try
{
int index1 = respMessage.IndexOf('(');
int index2 = respMessage.IndexOf(')');
string endPointData = respMessage.Substring(index1 + 1,
index2 − index1 − 1);
string[] endPointParts = endPointData.Split(',');
for (int i = 0; i < 6; i++)
{
parts[i] = int.Parse(endPointParts[i]);
}
}
catch
{
throw new WebException("Malformed PASV reply: " + respMessage);
}

string ipAddress = parts[0] + "." + parts[1] + "." + parts[2] +
"." + parts[3];
int port = (parts[4] << 8) + parts[5];

// Create a client socket
TcpClient dataClient = new TcpClient();

// Connect to the data port of the server
try
{
IPEndPoint remoteEP = new IPEndPoint(IPAddress.Parse(ipAddress),
port);
dataClient.Connect(remoteEP);
}
catch(Exception)
{
throw new WebException("Can't connect to remote server");
}
return dataClient;
}

The Close() method simply reads any outstanding responses from the server, logs off, and cleans up the resources we've opened:


public void Close()
{
if (dataStream ! = null)
{
dataStream.Close();
dataStream = null;
}

string respMessage;
GetResponse(out respMessage);

Logoff();

// Close the control TcpClient and NetworkStream
controlStream.Close();
client.Close();
}

The Logoff() method simply sends a STAT command to the server for debugging purposes, and then sends a QUIT command. There will be two responses to the STAT command, so we need to call GetResponse() to read the second:


public void Logoff()
{
// Send the QUIT command to log off from the server
string respMessage;
SendCommand("STAT", out respMessage); // Test only
Get Response(out respMessage); // STAT has 2 response lines !

SendCommand("QUIT", out respMessage);
}

The last method, SendCommand(), is where we actually write the command to the control stream. We add the final CRLF combination to the command before sending it, so that we don't need to do this every time we call the method. Once we've sent the command, we call GetResponse() to read the server's response:


internal int SendCommand(string command, out string respMessage)
{
// Convert the command string (terminated with a CRLF) into a
// byte array, and write it to the control stream.
byte[] request = Encoding.ASCII.Get Bytes(command + "\r\n");
controlStream.Write(request, 0, request.Length);

return GetResponse(out respMessage);
}
}
}

That completes the code for the class library, so now let's write a simple console application to test it out.

A Client Console Application
The first thing we need to do in our Main() method is register the "ftp" prefix with WebRequest. We do this by calling the WebRequest.RegisterPrefix() static method, and passing in the prefix associated with our new WebRequest extension, and an IWebRequestCreate implementation that will create an instance of our FtpWebRequest. After this, we call two methods to demonstrate uploading and downloading files respectively:


using System;
using System.IO;
using System.Net;
using Wrox.Networking.TCP.FtpUtil;

namespace TestClient
{
class Class1
{
const int bufferSize = 65536;

static void Main(string[] args)
{
// Register the ftp schema.
// Alternatively, a config file could be used
WebRequest.RegisterPrefix("ftp", new FtpRequestCreator());
UploadDemo();
DownloadDemo();
}

The UploadDemo() method creates a new instance of our FtpWebRequest class, and sets its properties to specify the username and password used to make the connection, to specify the data format for the file (here, we want to use binary format, so we set the BinaryMode property to true), and the method to execute against the server. As we want to upload a file, this could be either "PUT" or "STOR". Next, we call the GetResponseStream() method of the associated FtpWebResponse object to retrieve an FtpWebStream that we can use to write the file content to the FTP server. In order to do this, we open up a FileStream object pointing to the file we want to upload, and copy it in 65,536-byte chunks into the FtpWebStream. Finally, we close the FtpWebStream (which will close the associated response and log off from the server), and the FileStream:


// Upload a file using FtpWebRequest
public static void UploadDemo()
{
FtpWebRequest req = (FtpWebRequest)WebRequest.Create(
"ftp://192.168.0.1/demofile.bmp");
req.Username = "Administrator";
req.Password = "secret";
req.Method = "PUT"; // STOR or PUT
req.BinaryMode = true;
Stream writeStream = req.GetResponse().GetResponseStream();

FileStream fs = new FileStream (@"c: \temp\cool.bmp", FileMode.Open);

byte[] buffer = new byte [buffer Size];
int read;
while ((read = fs.Read(buffer, 0, bufferSize)) > 0)
{
writeStream.Write(buffer, 0, bufferSize);
}
writeStream.Close();
fs.Close();
}

Important Note that you will need the appropriate permissions on the FTP server to upload files. By default, uploading files to the server is not permitted.


The DownloadDemo() method is similar, but the way we use it depends on whether we're downloading binary or text data. If it's binary data, we copy the file data into a FileStream object in 65,536-byte chunks as in UploadDemo(). If it's a text file, we can use the far simpler StreamReader:


// Download a file using FtpWebRequest
public static void DownloadDemo()
{
FtpWebRequest req = (FtpWebRequest)WebRequest.Create(
"ftp://192.168.0.1/sample.bmp");

// defaults:
/* reg.Username = "anonymous";
reg.Password = "someuser@somemail.com";
reg.BinaryMode = true;
reg.Method = "GET"; */

FtpWebResponse resp = (FtpWebResponse)reg.GetResponse();
Stream stream = resp.GetResponseStream();

// Read a binary file
FileStream fs = new FileStream(@"c:\temp\sample.bmp",
FileMode.Create);
byte[] buffer = new byte[bufferSize];
int count;
do
{
Array.Clear(buffer, 0, bufferSize);
count = stream.Read(buffer, 0, bufferSize);
fs.Write(buffer, 0, count);
} while (count > 0);

stream.Close();
fs.Close();

/* read a text file
StreamReader reader = new StreamReader(stream);
string line;
while ((line = reader.ReadLine()) != null)
{
Console.WriteLine(line);
}
reader.Close(); */
}
}
}

And that completes the project! This isn't the most robust possible implementation, and we haven't implemented every possible command, but it should give you a clear idea of what's involved in implementing an FTP client, and it provides an example of implementing an application-level protocol that is a bit more complex than SMTP. It also shows how we can integrate our classes with the existing WebRequest framework, so that it's very intuitive to call.

The TcpListener Class
Typically, a server-side application starts by binding to the local endpoint and listening to incoming requests from clients. As soon as a client is found knocking on the port, the application activates by accepting the request and then creating a channel that is then responsible for communicating with the client. The application continues to listen for more incoming client requests on the main thread. The TcpListener class does exactly that-it listens to the client's request, accepts it, and then creates a new instance of the Socket class or the TcpClient class that we can use to communicate with the client. Just like the TcpClient, the TcpListener also encapsulates a private Socket object, m_ServerSocket, available only to derived classes.

The following tables show the important properties and methods:

Public Properties
Name
Type
Description

LocalEndpoint
IPEndpoint
This property returns an IPEndpoint object that contains information about the local network interface and the port number which is being used to listen for incoming client requests


Public Methods
Name
Description

AcceptSocket()
Accepts a pending connection request and returns a Socket object to use to communicate with the client

AcceptTcpClient()
Accepts a pending connection request and returns a TcpClient object to use to communicate with the client

Pending()
Indicates whether there are any connection requests pending

Start()
Causes the TcpListener to start listening for connection requests

Stop()
Closes the listener


Protected Properties
Name
Type
Description

Active
bool
Indicates whether the TcpListener is currently listening for connection requests

Server
Socket
Returns the underlying Socket object used by the listener to listen for connection requests


Instantiating of TcpListener Class
The TcpListener constructor has three overloads:

public TcpListener(int port);
public TcpListener(IPEndPoint endPoint);
public TcpListener(IPAddress ipAddr, int port);
The first simply specifies which port we want to listen on. The IP address in this case is IPAddress.Any, which provides an address that the server should listen to for a client's activity on all network interfaces. This field is equivalent to 0.0.0.0:


int port = 11000;
TcpListener newListener = new TcpListener(port);

The second takes an IPEndPoint object that specifies the IP address and port we want to listen on:


IPAddress ipAddr = IPAddress.Parse("127.0.0.1");
IPEndPoint endPoint = new IPEndPoint(ipAddr, 11000);
TcpListener newListener = new TcpListener(endPoint);

The last overload takes an IPAddress object and a port number:


IPAddress ipAddr = IPAddress.Parse("127.0.0.1");
int port = 11000;
TcpListener newListener = new TcpListener(ipAddr, port);

Listening for Clients
The next step after creating a socket is to start listening for client requests. The TcpListener class has a Start() method that does two things. First, it actually binds a socket using the IP address and port provided as arguments in the TcpListener constructor. Next, it begins listening for client connections by calling the Listen() method on the underlying Socket:


TcpListener newListener = new TcpListener(ipAddr, port);
newListener.Start();

Once we've started listening to the socket, we can check whether there already are connections in the queue by calling the Pending() method. This allows us to check for any awaiting clients before the accept method is called, which will then block the running thread:


...
if (newListener.Pending())
{
Console.WriteLine("There are connections pending in queue");
}

Accepting Connections from a Client
A typical server program uses two sockets, one that is used by the TcpListener class and the other that is returned when the listener accepts a client's connection and is used to communicate individually with that client. The AcceptSocket() or the simpler AcceptTcpClient() method can be used to accept any request that is currently pending in the queue. These methods return a Socket or TcpClient respectively to accept client requests.

Socket sAccepted = newListener.AcceptSocket();

TcpClient sAccepted = newListener.AcceptTcpClient();
Sending and Receiving Messages
The actual communication between the client and the server socket can be performed using the Socket's Send() and Receive() methods or by writing to/reading from the NetworkStream, depending on the type of socket created when accepting the connection. This topic was covered in detail earlier in the section on the TcpClient class, so we will skip the code here.

Stopping the Server
After communicating with the client, the last step is to stop the listening socket. Calling the Stop() method of the TcpListener class performs this step:


newListener.Stop();

A Multithreaded Client-Server Application
In the following section, we'll build a simple multithreaded echo server that can serve multiple echo clients using threads. Before going into the code, we will have a short introduction to multithreading by presenting a small example on how to create and run a thread. As the topic is not directly connected with network programming, we won't attempt to cover multithreaded programming in detail, and we will assume familiarity with the basic concepts of threading.

The multithreading support in .NET is provided through the System.Threading namespace. The Thread class in this namespace represents an individual thread. The thread needs an entry point for running an alternative flow of code. This entry point is the method where the thread will begin execution. Naturally enough, this method is represented by a delegate, and specifically the Threadstart delegate. Therefore, before creating an instance of the Thread class, we need to create an instance of the ThreadStart delegate, specifying the method name in the constructor.

After specifying the method using the delegate in the Thread constructor, the next step is to inform the operating system about the change in the thread state. The Start() method of the Thread class notifies the operating system that the thread is changing its state to running:


// Threading.cs
using System;
using System.Threading;

public class MultiThread
{
public static void runThread()
{
Console.WriteLine("Thread is running");
}

public static void Main(string [] arg)
{
// The ThreadStart specifying the delegate function
ThreadStart threadMethod = new ThreadStart(runThread);

// Create a thread instance with the ThreadStart delegate
Thread newThread = new Thread (threadMethod);
Console.WriteLine("Starting Thread");

// Start the thread, which calls the runThread method in a separate thread
newThread.Start();
}
}

This is all that we need to know about using threads in a basic multithreaded server, although you will need a more intensive usage of the Thread class to build a more robust server application, with support for synchronization techniques such as locking, and advanced thread manipulation such as thread pooling.

The client is a simple application, much as we saw in the previous section on the TcpClient class, so we don't need to look at it in too much detail here:


using System;
using System.Net;
using System.IO;
using System.Net.Sockets;
using System.Text;

public class EchoClient
{
const int ECHO_PORT = 8080;

public static void Main(string [] arg)
{
Console.Write("Your UserName:");
string userName = Console.ReadLine();
Console.WriteLine("-----Logged In----->");

try
{
// Create a connection with the ChatServer
TcpClient eClient = new TcpClient("127.0.0.1", ECHO_PORT);

// Create the stream classes
StreamReader readerStream = new StreamReader(eClient.GetStream());
NetworkStream writerStream = eClient.GetStream();

string dataToSend;

dataToSend = userName;
dataToSend += "\r\n";

// Send username to the server
byte[] data = Encoding.ASCII.GetBytes(dataToSend);

writerStream.Write(data,0,data.Length);

while(true)
{
Console.Write(userName + ":");

// Read line from server
dataToSend = Console.ReadLine();
dataToSend += "\r\n";

data = Encoding.ASCII.GetBytes(dataToSend);
writerStream.Write(data, 0, data.Length);

// If QUIT is sent, then quit application
if (dataToSend.IndexOf("QUIT") > −1)
break;

string returnData;

// Receive response from server
returnData = readerStream.ReadLine();

Console.WriteLine("Server: " + returnData);
}

// Close TcpClient
eClient.Close();
}
catch(Exception exp)
{
Console.WriteLine("Exception: " + exp);
}
}
}

The server application obviously has a change from the one we saw in the last chapter-it uses multiple threads for serving more than one client simultaneously. The following diagram shows the flow of the server application:


We start as usual by creating an instance of the TcpListener class and specifying the port number to bind the socket to. The listener socket then starts listening and accepting client requests. The change in the code appears when the listener accepts a client's request. In a single-user server model, which only serves a single client at a time, a Stream object is built to communicate directly with the client. After wrapping up the existing client, the server would be able to start listening for a new client. All the tasks performed in this type of application are executed on a single thread. In a multithreaded application, as soon as the listener accepts a client's socket, it starts a new thread, which is responsible for negotiating with the client. The main server thread continues to listen for further requests.

The method that executes on the secondary thread is implemented in a different class, called ClientHandler. We use a member variable to pass data between the main thread and this secondary thread. When the listener accepts a TcpClient, we instantiate a new object of the ClientHandler class, and assign the TcpClient object to its public field. We then execute the RunClient() method on a new thread. Since this is a member of the ClientHandler class, it can access the TcpClient object that we set from the main thread.

The RunClient() method in the ClientHandler class is completely responsible for negotiating with a single client. It performs the procedure of creating streams and then reading and writing messages to the socket.

This is the complete code listing for the server application:


// MEchoServer.cs

using System;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Collections;
using System.Text;

public class ClientHandler
{
public TcpClient clientSocket;

public void RunClient()
{
// Create the stream classes
StreamReader readerStream = new StreamReader(clientSocket.GetStream());
NetworkStream writerStream = clientSocket.GetStream();

string returnData = readerStream.ReadLine();
string userName = returnData;

Console.WriteLine("Welcome " + userName + " to the Server");

while (true)
{
returnData = readerStream.ReadLine();

if (returnData.IndexOf("QUIT") > −1)
{
Console.WriteLine("Bye Bye " + userName);
break;
}

Console.WriteLine(userName + ": " + returnData);
returnData += "\r\n";

byte[] dataWrite = Encoding.ASCII.GetBytes(returnData)
writerStream.Write(dataWrite,0,dataWrite.Length);
}

clientSocket.Close();
}
}

public class EchoServer
{
const int ECHO_PORT = 8080;
public static int nClients = 0;

public static void Main(string [] arg)
{
try
{
// Bind the server to the local port
TcpListener clientListener = new TcpListener(ECHO_PORT);

// Start to listen
clientListener.Start();

Console.WriteLine("Waiting for connections…");

while (true)
{
// Accept the connection
TcpClient client = clientListener.AcceptTcpClient();

ClientHandler cHandler = new ClientHandler();

// Pass value to the ClientHandler object
cHandler.clientSocket = client;

// Create a new thread for the client
Thread clientThread = new Thread(new ThreadStart(cHandler.RunClient));
clientThread.Start();
}

clientListener.Stop();
}
catch(Exception exp)
{
Console.WriteLine("Exception: " + exp);
}
}
}

You can test the application by running the server first and then running multiple instances of the client and logging on with different names. Although this is the simplest of servers, just built for demonstration purposes, it can be used as a framework for building a more robust server application.





.NET Remoting
Operating systems typically provide some level of protection for each application from the effects of other applications. These are necessary to save one application from the effects of failure of another application. Microsoft Windows followed this approach by implementing processes for each application. Each application is loaded in a separate process space. The code and memory running in a process could not access another code running in a different process, although there are well-defined methods regulated by the Operating System to achieve this. In the .NET Framework, Microsoft introduced the concept of application domains.

Application Domains
Application domains (AppDomains) are the units that the .NET Framework uses to provide isolation between two different applications. It is possible to run multiple application domains in a single process. This AppDomain-based isolation provides several overall advantages, specifically for a server-side application. The existence of different AppDomains within a single process means that if any piece of code in an AppDomain malfunctions for any reason, it won't harm the other AppDomains in the same process. We can also stop a piece of code included in an AppDomain. However, the most important factor is that the code running in an AppDomain cannot access another code in a different AppDomain. The process that is used to provide communication between different objects in different AppDomains is called .NET Remoting.

The Microsoft .NET Remoting framework can be used to enable communications between two different applications. These applications can be on the same computer, on a single LAN, on the Internet, or in any other geographically located area connected with some protocol.

One of the advantages of .NET Remoting comes from the fact that, unlike the proprietary protocols employed by Microsoft DCOM and Java RMI, Remoting is built on accepted industry standards, such as Simple Object Access Protocol (SOAP), HTTP, and TCP. This makes it possible for different applications on the Internet to communicate in the same way as if they were making the connection over a private network.

However, while Remoting can use the SOAP and HTTP protocols, it doesn't have to. In fact, it's probably worth thinking twice if you're using SOAP with Remoting, as you lose some of the performance benefits of Remoting over ASP.NET web services. If you're using Remoting rather than web services, efficiency is probably a significant factor, and for best performance use binary encoding with the TCP channel.

A complete, in-depth description of Microsoft .NET Remoting is outside the scope of this book. We'll cover the important concepts in building a Remoting application with a sample which shows you how to build your own application based on the Microsoft .NET Remoting Framework.

How Remoting Works
In order to use the Remoting framework, the hosting application must first be loaded, and we must register a channel and port to listen to connections. The client also needs to register the same channel. We'll discuss channels in more detail in the next section, but, for the time being, think of a channel as a transport medium between the server and the client. Channels use network protocols like TCP or HTTP to send data from one object to another. After the client has registered the channel, it creates a new instance of the remote class. If the client succeeds in instantiating a remote object, it then receives a reference to the server object, through which it can call methods on the remote object, as if it were part of the client process. The remoting system uses a proxy to implement this. When the client creates an instance of the remote object, the Remoting framework intercepts the call and creates a proxy object with the same public interface as the real object, and returns this proxy object to the client. The client then makes method calls on this proxy. These calls are intercepted by the Remoting framework, and routed to the server process. The server process then instantiates the object, calls the method, and sends the return value to the client application via the client proxy:


Channels
Channels are used to transport messages to and from the remote objects. When a client calls a method on the server object, the parameters and other details for the method are packed inside a message object and transported through the channel to the remote object. A client can select any channel type that is registered on the server to be used as the transport medium. This allows developers to choose the best channel for their needs. Although there are a couple of built-in transport channel types, it's possible to extend those, or even to create an altogether new channel type to be used in specialized environments or scenarios. The results are returned from the server in a similar way.

Channel Sinks
Channels route each message through a series of channel sinks. Each sink changes or filters the message, and then passes it on to the next sink in the chain, to the receiving application, or to the channel itself. There can be any number of sinks that process the messages sent over the channel, such as a security sink (for encrypting the message), or a logging sink (for tracking the remoting process).

On the client side, the first sink is the formatter sink; this passes the message onto any custom sinks implemented in the sink chain. Finally, the message reaches the transport sink, which writes it to the transport channel.

On the server side, the whole process is reversed. The transport sink retrieves the message from the channel, and forwards it to the channel sink chain. The last sink in the chain is the formatter sink, which routes the message to the Remoting infrastructure, so that it can be passed to the receiving application:


There are two types of channel provided by the .NET Framework:

TcpChannel

HttpChannel

The TcpChannel is discussed in the next section, while Chapter 8 provides a section on the HttpChannel.

Formatters
The formatter sinks are used for encoding and decoding of messages before the channel transports them. There are two formatters provided with the .NET Remoting framework:

The Binary Formatter. When binary formatting is used for messages, both the client and server require the .NET Framework to process messages. The type system maintains its state when transmission is performed using the binary formatter. Because the binary formatter depends on the .NET Framework, it can preserve the type system information by serializing or deserializing the object.

The binary formatter has better performance than the SOAP formatter because less data is sent over the network, and because it requires less time for serialization and deserialization.

The SOAP XML-based Formatter. The SOAP formatter follows the SOAP specifications outlined by the W3C (http://www.w3.org/TR/2002/WD-soap12-part120020626/). This allows interoperability with other clients or servers. As the SOAP formatter can include different implementations from different languages, it is not possible for it to preserve the type system information.

Because the SOAP formatter is designed for interoperability rather than performance, it generally has worse performance than the binary formatter.

The TCP Transport Channel
The TCP transport channel by default uses the binary formatter to serialize all messages to the binary stream and transport them to the target using the TCP protocol. The .NET Framework provides the System.Runtime.Remoting.Channels.Tcp namespace to be used in applications that use the TCP transport channel. There are three classes in the namespace:

Class
Description

TcpChannel
Provides an implementation for a sender-receiver channel that uses the TCP protocol to transmit messages.

TcpClientChannel
Provides an implementation for a client channel that uses the TCP protocol to transmit messages.

TcpServerChannel
Provides an implementation for a server channel that uses the TCP protocol to transmit messages.


The following example shows how to write a simple Remoting application. The client passes a string to the remote object, which echoes back the same string to the client.

There are three steps required to build a remoting application:

Create the remote object

Create the hosting application for the remote object

Create the client application

Creating the Remote Object
The remote object is a simple class instance. The only extension that it needs is that the class must inherit from the MarshalByRefObject class. We can convert all our existing classes to remote classes just by deriving them from MarshalByRefObject. There can be any number of methods and properties in the class. Here we define a simple class that can be used to instantiate a remote object.

The first step before starting any remoting application is to include all the namespaces required. The following namespaces are used for a TCP transport-based remoting application:


// ServerObj.cs

using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

The next step is to define the class for the remote object:


// Notice that the class is derived from MarshalByRefObject
public class ServerObj : MarshalByRefObject
{
public string ServerMethod(string argument)
{
Console.WriteLine(argument);
return argument;
}
}

We must build this class as a DLL, so we use the following command to compile the remote object:

csc /target:library /out:ServerObj.dll ServerObj.cs
Creating the Hosting Application
After creating the remote object, the next thing to do is to create an application that hosts our remote object on the server. The application that we build here is a simple console-based application (although for production you might need to consider the idea of using Windows Services as a better alternative).

The first step is to include all the necessary using directives in the code as above. The next step is to create and register the TcpChannel. (You should close the server created in the previous section before doing this, because we use Remoting on the same port.)


TcpChannel tcpChannel = new TcpChannel(8080);

The TcpChannel constructor is used to specify the port number for listening for client connections.

Next, we register the TCP channel:


ChannelServices.RegisterChannel(tcpChannel);

The ChannelServices class provides static methods for registering channels, getting registered channels, unregistering channels, creating the channel sink chain, dispatching messages to the sink chain, and URL discovery. All the channels that are registered can be accessed through the RegisteredChannels property, which returns an array of IChannel objects. The most important member of this class is the RegisterChannel() method. It performs the registration of a channel with the channel services. It takes a single argument, which is the channel object we created earlier:

public static void RegisterChannel(IChannel channelObject);
It is important to note here that you cannot register two channels with the same name in the application domain. By default, the names of the HttpChannel and TcpChannel are "http" and "tcp" respectively. If you need to register two channels, then you must change the name of the respective channel by altering the ChannelName property of the channel object.

After creating and registering the channel, the RegisterWellKnownServiceType() method of the RemotingConfiguration class is called to register the class with the Remoting framework:


RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemoteSample.ServerObj),
"EchoMessage",
WellKnownObjectMode.SingleCall);

The RemotingConfiguration class provides static methods for configuring the remoting options.

The RegisterWellKnownServiceType() method takes three parameters that indicate the type of the remoting class, the string to identify the remoting object (that is, its URI), and the object activation mode.

There are two types of activation for remote objects:

Server Activation. Server-activated objects are created by the server only when they are needed, for instance when the client invokes the first method on that server. There are two activation modes for server-activated objects, Singleton and SingleCall. Singleton objects have only one instance, regardless of how many clients there are for that object. When an object is specified as SingleCall, a new instance is created every time for each client method invocation.

Client Activation. Client-activated objects are created on the server when the client calls new or Activator.CreateInstance().

In the code sample, we created a server-activated object with SingleCall mode.

Finally, we wait for the client to connect with the server by blocking the input stream through Console.ReadLine():


Console.ReadLine();

The complete sourcecode is provided below for a quick reference:


// ServerApp.cs

using System;
using Systern.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System,Runtime.Remoting.Channels.Tcp;

namespace RemoteSample
{
public class ServerApp
{
public static void Main(string [] arg)
{
TcpChannel tcpChannel = new TcpChannel(8080);
ChannelServices.RegisterChannel(tcpChannel);

RemotingConfiguration.RegisterWellKnownServiceType(
typeof(RemoteSample.ServerObj),
"EchoMessage",
WellKnownObjectMode.SingleCall);

Console.WriteLine("Hit to continue…");
Console.ReadLine();
}
}
}

Creating the Client Application
The last step in implementing the remoting sample is to create the client application. As with the server application, the client must also register the TCP channel with the channel services:


TcpChannel tcpChannel = new TcpChannel();
ChannelServices.RegisterChannel(tcpChannel);

Being a sharp reader, you may have noticed that the server IP address and port number have been omitted. Normally, a client requires two things to connect with the server-the server IP address and the port number. Here we don't specify either of those. You will see in the next step how the Remoting framework overcomes this.

The next step is the most important for the client; here the object is created on the server and a reference to that object is returned to the client:


ServerObj obj = (ServerObj)Activator.GetObject(typeof(RemoteSample.ServerObj),
"tcp://localhost:8080/EchoMessage");

The Activator class provides methods to create types of object remotely or locally, or to obtain references to existing remote objects. The GetObject() method creates a proxy for a currently running remote server-activated well-known object. The object type and the URL are passed as parameters. Notice the URL here, which is used to define the protocol, the port number, and the IP address for the remote object-all the things required to connect with a remote server that were missing from the TcpChannel constructor.

Here, we are trying out the sample on a single PC, so we specified localhost, but it can be replaced with any server IP address on which you want to host your remote object.

After creating the proxy and getting the reference to the remote object by means of the proxy, we can call any of the methods declared in our remote object:


obj.ServerMethod("Wrox Remoting Sample");

The complete client code is as follows:


// ClientApp.cs

using System;
using System.Runtime.Remoting;
using Systern.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace RemoteSample
{
public class ClientApp
{
public static void Main(string [] arg)
{
TcpChannel tcpChannel = new TcpChannel();

ChannelServices.RegisterChannel(tcpChannel);

ServerObj obj = (ServerObj)Activator.GetObject(
typeof(RemoteSample.ServerObj),
"tcp://localhost:8080/EchoMessage");

Console.WriteLine(obj.ServerMethod("Wrox Remoting Sample"));
}
}
}

When compiling both the client and server applications, we need to include a reference to the server object DLL, or we will receive an error stating that 'The type or namespace name ServerObj does not exist in the class". The server is compiled using:

csc /r:ServerObj.dll ServerApp.cs
And the client application using:

csc /r:ServerObj.dll ClientApp.cs
After compiling all the files, you must execute the server application first to start listening for the client request.

Creating a Client-Activated Remote Object
The above example demonstrates the remote instantiation of server-activated objects. For a client-activated object, although the remote object remains the same, there are some changes required in the server and client applications. In the server application, we call the RegisterActivatedServiceType() method instead of RegisterWellknownServiceType(). The Register ActivatedServiceType() takes a single argument that represents the type of the remote object. The URI in this case is assigned using the ApplicationName property of the RemotingConfiguration class:


using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace RemoteSample
{
public class ServerApp
{
public static void Main(string [] arg)
{
TcpChannel tcpChannel = new TcpChannel(8080);
ChannelServices.RegisterChannel(tcpChannel);

RemotingConfiguration.ApplicationName = "EchoMessage";
RemotingConfiguration.RegisterActivatedServiceType(
typeof(RemoteSample.ServerObj));

Console.WriteLine("Hit to continue…");
Console.ReadLine();
}
}
}

On the client side, we have two different methods to access the remote objects-Activator.CreateInstance() and RemotingConfiguration.RegisterActivatedClientType(). To create an instance of the remote object using the new keyword, you must first register the object type on the client by calling the RegisterActivatedClientType() method. Calling CreateInstance() gives us a new instance of the server object, and requires the URL of the remote application as a parameter. The UrlAttribute class is used to pass attributes to CreateInstance(). The CreateInstance() method returns an ObjectHandle object that acts as a wrapper for the remote object; we can call this object's UnWrap() method to get the remote object itself:


using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Activation;

namespace RemoteSample
{
public class ClientApp
{
public static void Main(string [] arg)
{
TcpChannel tcpChannel = new TcpChannel();
ChannelServices.RegisterChannel(tcpChannel);

// Method 1
object[] attrs = {new UrlAttribute("tcp://localhost:8080/EchoMessage")};
ObjectHandle handle = Activator.CreateInstance("ServerObj",
"RemoteSample.ServerObj", attrs);

ServerObj obj = (ServerObj)handle.Unwrap();
Console.WriteLine("Client:" + obj.ServerMethod("Wrox Remoting Sample"));

// Method 2
RemotingConfiguration.RegisterActivatedClientType(
typeof(RemoteSample.ServerObj), "tcp://localhost:8080/EchoMessage");

ServerObj obj2 = new ServerObj();
Console.WriteLine("Client2:" + obj2.ServerMethod(
"Wrox Remoting Sample"));
}
}
}

Although this sample application does perform all the basic steps involved in .NET Remoting, there is a lot to improve on this; you can see that there is no error checking performed in the code. There is an exception class called RemotingException provided specially for the Remoting framework, which can be used to help out with most of the hiccups involved in these types of application. As the TCP channel employs the Socket class to connect to the server, a SocketException will be thrown if the server is not listening to the port. If you forget to inherit the remote class from MarshalByRefObject, the client code will throw a RemotingException when calling GetObject() to create a proxy for the running remote object. A RemotingException is also thrown when you try to register a channel that is already registered. In this case, you can call the ChannelServices.GetChannel() method, which will return the channel interface if it is already registered; otherwise null is returned.