Taking a tour of the System.Net
namespace
Using built-in tools to access the network
Making the network tools work for you
In my opinion, the reason that Microsoft had to create the .NET Framework in the first place was the lack of Internet interoperability within the existing infrastructure. COM just couldn't handle the Internet. The Internet works differently than most platforms, such as PCs. The Internet is based on protocols — carefully defined and agreed upon ways to get things like mail and file transfers working. Microsoft's environment before 2002 distinctly didn't handle those as well.
As you can see throughout this book, the .NET Framework is designed from the ground up to take the Internet and networking in general into consideration. Not surprisingly, that is nowhere more clear than it is in the System.Net
namespace. The Internet takes first chair here, with Web tools taking up nine of the classes in the namespace.
In this fourth version of the framework, even more Internet functionality is baked in. Although in version one the focus was on tools used to build other tools (low-level functions), now it contains features that are useful to you, such as Web, mail, and File Transfer Protocol (FTP). Secure Sockets Layer — the Internet's transport security — is much easier to use in this version, as are FTP and mail, which previously required other, harder-to-use classes.
System.Net
is a big, meaty namespace, and finding your way around it can be difficult. My goal for this chapter is to take things that you do often and show the basics, and then give you the tools to research the more complex features of the classes.
Networking is a big part of the .NET Framework, and all the functionality is in this namespace — a whole book can be (and has been) written on the subject. For the purposes of this introduction to networking with C#, I show you these features:
Keep in mind that I am not saying that sockets and IPv6 and other advanced Internet protocols aren't important. This chapter talks about the parts of the namespace that you will use every day. As always, there is more to learn about System.Net
.
The System.Net
namespace is full of classes that are confusing if viewed in the documentation but make a lot of sense when used in an application. The namespace removes all the complexity of dealing with the various protocols used on the Internet.
There are more than 2,000 RFCs for Internet protocols (an RFC is a Request For Comments, a document that is sent to a standards body for review by peers before it becomes a standard), and if you have to learn all of them separately, you will never complete your project. The System.Net
namespace is about making it less painful.
System.Net
is not just for Web projects. Like everything else in the base class library, you can use System.Net
with all kinds of projects. You can
Get information from Web pages on the Internet and use them on your programs.
Move files via the Internet using FTPs.
Send e-mail easily.
Use more advanced network structures.
Secure communications over the Internet using the SSL protocol.
If you need to check on the connectivity of a computer from a Windows application, you can use System.Net
. If you need to build a class that will download a file from a Web site, System.Net
is the namespace you need. Just because most classes relate to the Internet doesn't mean that only Web applications can use it. That's the magic of System.Net
. Any application can be a connected application. While some parts of the namespace function to make the development of Web applications easier, the namespace in general is designed to make any application work with the Web.
The System.Net
namespace contains 62 classes and six smaller namespaces. Even as I write this, I am overwhelmed. However, if you look closely, you can see patterns.
If you need help using classes, you can find more information in Book II.
The classes are well named, and you will note that a few protocols get a number of classes each. After you translate, you can narrow down what you need based on the way the protocol is named:
Authentication
and Authorization
: These classes provide security.
Cookie
: This class manages cookies from Web browsers and usually is used in ASP.NET pages.
DNS
(Domain Name Services): These classes help to resolve domain names into IP addresses.
Download
: This class is used to get files from servers.
EndPoint
: This class helps to define a network node.
FileWeb
: This brilliant set of classes describes network file servers as local classes.
FtpWeb
: This class is a simple File Transfer Protocol implementation.
Http
(HyperText Transfer Protocol): This class is the Web protocol.
IP
(Internet Protocol): This class helps to define network endpoints that are specifically Internet related.
IrDA
: This class is an infrared endpoint. Infrared ports are networks too!
NetworkCredential
: This class is another security implementation.
Service
: This class helps manage network connections.
Socket
: This class deals with the most primitive of network connections.
Upload
: This set of classes helps you upload information to the Internet.
Web
: These classes help with the World Wide Web — largely implementations of the http classes that are more task oriented.
This list is extensive because the classes build on each other. The EndPoint
classes are used by the socket
classes to define certain network specifics, and the IP
classes make them specific to the Internet. The Web
classes are specific to the World Wide Web. You will rarely use highest-level classes, but it's often tough to see what is needed when.
Most of the functions that you use every day, though, are encapsulated within seven mostly new namespaces under the System.Net
namespace:
Cache
: This function has a lot of enumerators that manage the browser and network caching functions built into the namespace.
Configuration
: This function grants access to the properties that you need to set to make many of the other System.Net
classes work.
Mail
: This function takes over for System.Web.Mail
to facilitate the sending of Internet e-mail.
Mime
: This function bundles file attachments with the Mail
namespace.
NetworkInformation
: This function gets details about the network around your application.
Security
: This function implements the network security managed by many classes of System.Net
.
Sockets
: This function utilizes the most basic network connections available to Windows.
The System.Net
namespace is code-oriented, which means that few implementations are specifically for user interfaces. Most everything that you do with these classes is behind the scenes. You have few drag-and-drop user controls — the System.Net
namespace is used in the Code View.
To demonstrate this, in the rest of this chapter, I go over building a Windows Forms application that has the following requirements:
Check the network status.
Get a specific file from the Internet.
E-mail it to a specific e-mail address.
Log the whole transaction.
This is not an insignificant set of requirements. In fact, even in the 1.0 and 1.1 versions of C#, this would be difficult. One of the main goals of the System.Net
namespace in this version is to make common tasks much easier. You can start by loading the sample code or by starting a new project and following the steps in the following sections.
First, you need to inform the user about network connectivity by following these steps:
Create a new Windows Forms Application project in Visual Studio.
I called mine NetworkTools
.
Add a StatusStrip
control to the form by dragging it from the Toolbox.
Select the SmartTag that appears and add a StatusLabel
.
Double-click the form to get the Form_Load
event handler and move to Code View.
Reference the System.Net
namespace by adding the line using System.NET.NetworkInformation;
to the top of the code.
Add the code in bold from the following listing to test whether the network is available and display it on the status bar:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Net.NetworkInformation; namespace NetworkTools { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) {if (NetworkInterface.GetIsNetworkAvailable())
{
toolStripStatusLabel1.Text = "Connected";
}
else
{
toolStripStatusLabel1.Text = "Disconnected";
}
} } }
That's all there is to it. The NetworkInformation
class contains a bunch of information about the status of the network, current IP addresses, the gateway being used by the current machine, and more.
Keep in mind that the NetworkInformation
class will work only on a local machine. If you use this class in an ASP.NET Web Forms application, you will get information about the server.
You can get a file from the Internet in one of several ways, and one of the most common is by using FTP. The lightweight FTP protocol is favored because it's secure and supported on many systems.
To build an application that uses FTP, follow these steps:
Drag a button onto the form from the Toolbox.
Double-click the button to get the Click
event handler.
Add the required imports, System.Net, System.Net.Mail
, and System.IO
to the top of the code.
Create a new subroutine called Download File
that accepts a remote filename and a local filename as strings.
In the new subroutine, create a new FileStream
(called localFileStream
) and FTPWebRequest
(called ftpRequest
), as shown in Listing 4-1.
The FileStream
references a local file and accepts the local file that is passed into the subroutine. The FtpWebRequest
is the same thing for the remote file.
Set the Method
parameter of the FtpWebRequest
to WebRequestMethods.Ftp.Downloadfile
.
Set the Credentials
property of the FtpWebRequest
to a new NetworkCredential
with anonymous information, like I did in Listing 4-1.
Create a new WebResponse
object from the ftpRequest
method. This gets the statement back from the FTP server about how your request will be handled.
Get the Stream
from the Response
object.
Read the file into a 1024-byte buffer, one block at a time, using a While
loop, as shown at the end of Listing 4-1.
Call the DownloadFile
method from the Button1_Click
event handler, like I show in this chunk of code:
private void button1_Click(object sender, EventArgs e) DownloadFile(@"ftp://ftp.csharpfordummies.net/sampleFile.bmp", @"c: sampleFile.bmp"); End Sub
Example 4-1. The DownloadFile Method
private void DownLoadFile(string remoteFile, string localFile) { FileStream localFileStream = new FileStream(localFile, FileMode. OpenOrCreate); FtpWebRequest ftpRequest = (FtpWebRequest)WebRequest. Create(remoteFile); ftpRequest.Method = WebRequestMethods.Ftp.DownloadFile; ftpRequest.Credentials = new NetworkCredential("Anonymous", "bill@ sempf.net"); WebResponse ftpResponse = ftpRequest.GetResponse(); Stream ftpResponseStream = ftpResponse.GetResponseStream(); byte[] buffer = new byte[1024]; int bytesRead = ftpResponseStream.Read(buffer, 0, 1024); while (bytesRead > 0) { localFileStream.Write(buffer, 0, bytesRead); bytesRead = ftpResponseStream.Read(buffer, 0, 1024); } localFileStream.Close(); ftpResponseStream.Close(); }
This FTP example is watered down, but it makes my point. The WebRequest
and WebResponse
classes in the System.Net
namespace are fully utilized to create the more complete FtpWebRequest
, for instance. Properties like the Method
of download and Credentials
make it an easy call.
In fact, the toughest part of this process is dealing with a FileStream
object, which is still the best way to move files and not specific to the System.Net
namespace. Streams are discussed in Chapter 3 of this mini-book, which covers the System.IO
namespace, but they have significance to the network classes too. Streams represent a flow of data of some kind, and a flow of information from the Internet qualifies.
That's what you are doing when you get a Web page or a file from the Internet — gathering a flow of data. If you think about it, it makes sense that this is a flow, because the status bar in an application shows a percentage of completion. Just like pouring water into a glass, the flow of data is a stream, so the concept is named Stream
.
This concept holds true for getting a file from the World Wide Web, as well. HTTP, the Web protocol, is just another protocol that defines how a document is moved from a server on the Internet to your local machine. In fact, the code looks strikingly similar to the FTP example, as you can see in the following bit of code. The same stream is recovered; only the formatting is different.
private void DownLoadWebFile(string remoteFile, string localFile) { FileStream localFileStream = new FileStream(localFile, FileMode. OpenOrCreate);WebRequest webRequest = WebRequest.Create(remoteFile);
webRequest.Method = WebRequestMethods.Http.Get;
WebResponse webResponse = webRequest.GetResponse();
Stream webResponseStream = webResponse.GetResponseStream(); byte[] buffer = new byte[1024]; int bytesRead = webResponseStream.Read(buffer, 0, 1024); while (bytesRead > 0) { localFileStream.Write(buffer, 0, bytesRead); bytesRead = webResponseStream.Read(buffer, 0, 1024); } localFileStream.Close(); webResponseStream.Close(); }
You need to pass in a Web address, so your subroutine call looks like this:
DownloadWebFile(@"http://www.csharpfordummies.net/sampleFile.bmp", @"c: sampleFile.bmp");
Note the changes, marked as bold. webRequest
is now a WebRequest
rather than an FtpWebRequest
. Also, the Method
property of webRequest
has been changed to WebRequestMethods.Http.Get
. Finally, the Credentials
property has been removed because the credentials are no longer required.
E-mail is a common requirement of networked systems. If you are working in an enterprise environment, you are going to write a larger scale application to handle all e-mail requirements, rather than make each individual application e-mail-aware.
However, if you are writing a standalone product, it might require e-mail support. Because I happen to be writing a standalone application, that is exactly what I'm going to do.
E-mail is a server-based operation, so if you don't have an e-mail server that you can use to send from, this might be hard. Many ISPs no longer allow relaying, which is sending an outgoing message without first having an account and logging in. Therefore, you might have trouble running this part of the sample.
If you are in a corporate environment, however, you can usually talk to your e-mail administrator and get permission to use the e-mail server. Because outgoing requests are usually only harnessed inside the firewall, relaying is often available. To build your e-mail function, follow these steps:
Add a text box to the default form in Design View, and then change to Code View.
At the top of Code View, make sure that you have referenced the System.Net.Mail
namespace.
Create a new subroutine called SendEmail
.
It should accept the from e-mail address, the to e-mail address, the subject of the e-mail, and the body of the e-mail.
Declare a new MailMessage
and pass in the fromAddress, toAddress, subject
, and body
parameters, like this:
MailMessage message = New MailMessage(fromAddress, toAddress, subject, body);
Declare a new SmtpClient
and pass in the address of your mail server.
This can be an IP address, machine name, or URL.
Use the Send
method of the SmtpClient
object you created to send the MailMessage
, which is passed in as a parameter.
When you're finished, make sure that you set the values of the MailMessage
and SmtpClient
to Nothing
, because they do take up resources.
Listing 4-2 shows the completed subroutine.
Example 4-2. The SendEmail Subroutine
private void SendEmail(string fromAddress, string toAddress, string subject, string body) { MailMessage message = new MailMessage(fromAddress, toAddress, subject, body); SmtpClient mailClient = new SmtpClient("localhost"); mailClient.Send(message); message = null; mailClient = null; }
Notice that I used localhost
as the e-mail server name. If you have an e-mail server software installed locally, even IIS 6.0 with SMTP, this will work. Most of the time, you will have to put another e-mail server name in the SmtpClient
constructor. The e-mail server name can often be found in your Outlook preferences.
After you have written your method, you need to call it after the file is downloaded in the Button1_Click
event handler. Change the code of that subroutine to the following to call that method:
private void button1_Click(object sender, EventArgs e) { DownloadFile(@"ftp://ftp.csharpfordummies.net/sampleFile.bmp", @"c: sampleFile.bmp"); SendEmail(textBox1.Text, textBox1.Text, "FTP Successful", "FTP Successfully downloaded"); }
Notice that I sent in the value of the text box twice: once for the to address, and once for the from address. This isn't always necessary, because you may have a situation where you want the e-mail to come only from a Webmaster address or to go only to your address.
You should have enough code in place to run the application now. Press F5 to launch the application in debug mode and give it a try.
When you click the button, the application should download the file to the local drive and then e-mail you to inform you that the download is complete. A host of things can go wrong with network applications, though, and you should be aware of them. Here are a few:
For most network activity, the machine running the software must be connected to a network. This isn't a problem for you as the developer, but you need to be conscious of the end users, who may need connectivity to have access to the features they want to use. Use of the network status code can help inform users about the availability of those features.
Firewalls and other network appliances sometimes block network traffic from legitimate applications. Some examples of this include:
FTP is often blocked from corporate networks.
Network analysis features of .NET are often blocked on corporate servers. If the server is available to the public, these openings can cause holes for hackers to crawl through.
Speaking of hackers, make sure that if you use incoming network features in your application, you have adequately secured your application. More on this can be found in the excellent book Writing Secure Code, Second Edition, by Michael Howard and David C. LeBlanc (published by Microsoft Press).
E-mail is especially fragile. Often, Internet service providers will block e-mail from an address that is not registered on a mail server. This means that if you are using your localhost server (like in the example in Listing 4-2), your ISP might block the e-mail.
Network traffic is notoriously hard to debug. For instance, if the sample application works, but you never receive an e-mail from the SmtpServer
you coded, what went wrong? You may never know. XML Web services (covered in Book VII) have a similar problem — it's spectacularly tough to see the actual code in the SOAP envelope (markup added around requests for Web services) to tell what went wrong.
This brings you to the next topic, which is network logging. Because network activity problems are so hard to debug and reproduce, Microsoft has built in several tools for the management of tracing network activity.
What's more, like the ASP.NET tracing available, the System.Net
namespace tracing is completely managed using the configuration files. To be able to use the functions, therefore, you don't need to change and recompile your code. In fact, with a little management, you can even show debug information to the user by managing the config
files your application uses.
Each kind of application has a different kind of configuration file. For Windows Forms applications, which you are using here, the file is called app.config
and is stored in the development project directory. When you compile, the name of the file is changed to the name of the application, and it's copied into the bin
directory for running.
If you open your app.config
file now, you see that it already contains some diagnostic information, as shown in Listing 4-3. You will add some information to it.
Example 4-3. The Default app.config File
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.diagnostics> <sources> <!-- This section defines the logging configuration for My.Application.Log in Windows Forms projects.--> <source name="Microsoft.VisualBasic.Logging.Log.WindowsFormsSource" switchName="DefaultSwitch"> <listeners> <add name="FileLog"/> <!-- Uncomment the below section to write to the Application Event Log --> <!--<add name="EventLog"/>--> </listeners> </source> </sources> <switches> <add name="DefaultSwitch" value="Information" /> </switches>
<sharedListeners> <add name="FileLog" type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f 5f7f11d50a3a, processorArchitecture=MSIL" initializeData="FileLogWriter"/> <!-- Uncomment the below section and replace APPLICATION_NAME with the name of your application to write to the Application Event Log --> <!--<add name="EventLog" type="System.Diagnostics. EventLogTraceListener" initializeData="APPLICATION_NAME"/> --> </sharedListeners> </system.diagnostics> </configuration>
First, you need to add a new source for the System.Net
namespace. Next, you add a switch to the Switches
section for the source you added. Finally, you add a SharedListener
to that section and set the file to flush the tracing information automatically.
The finished app.config
file, with the adds in bold, is shown in Listing 4-4. It's also in the sample code on this book's companion Web site.
Example 4-4. The Finished app.config File
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.diagnostics> <sources> <source name="Microsoft.VisualBasic.Logging.Log.WindowsFormsSource" switchName="DefaultSwitch"> <listeners> <add name="FileLog"/> </listeners> </source><source name="System.Net">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
</sources> <switches> <add name="DefaultSwitch" value="Information" /><add name="System.Net" value="Verbose" />
</switches> <sharedListeners> <add name="FileLog" type="Microsoft.VisualBasic.Logging.FileLogTraceListener, Microsoft.VisualBasic, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f 5f7f11d50a3a, processorArchitecture=MSIL" initializeData="FileLogWriter"/><add name="System.Net"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="my.log"/>
</sharedListeners><trace autoflush="true" />
</system.diagnostics> </configuration>
Run the application again and watch the Output window. Advanced logging information is shown there because of your changes to the configuration file. Additionally, a log file was written. In the development environment, this is in the bin/debug directory of your project. You might have to click the Show All Files button at the top of the Solution Explorer to see it.
In that folder, you should see the file named my.log
, where the SharedListener
you added to the app.config
file directed the logging information. My copy of that file is shown in Listing 4-5 — your mileage may vary.
Example 4-5. The Log Information
System.Net Information: 0 : WebRequest::Create(ftp://ftp.csharpfordummies.net/ sample.bmp) System.Net Information: 0 : Exiting WebRequest::Create() -> FtpWebRequest#37460558 System.Net Information: 0 : FtpWebRequest#37460558::GetResponse() System.Net Information: 0 : Exiting FtpWebRequest#37460558::GetResponse() System.Net Information: 0 : Associating Message#59487907 with HeaderCollection#23085090 System.Net Information: 0 : HeaderCollection#23085090::Set(mime-version=1.0) System.Net Information: 0 : Associating MailMessage#6964596 with Message#59487907 System.Net Information: 0 : SmtpClient::.ctor(host=24.123.157.3) System.Net Information: 0 : Associating SmtpClient#17113003 with SmtpTransport#30544512 System.Net Information: 0 : Exiting SmtpClient::.ctor() -> SmtpClient#17113003 System.Net Information: 0 : SmtpClient#17113003::Send(MailMessage#6964596) System.Net Information: 0 : SmtpClient#17113003::Send(DeliveryMethod=Network) System.Net Information: 0 : Associating SmtpClient#17113003 with MailMessage#6964596 System.Net Information: 0 : Associating SmtpTransport#30544512 with SmtpConnection#44365459 System.Net Information: 0 : Associating SmtpConnection#44365459 with ServicePoint#7044526 System.Net Information: 0 : Associating SmtpConnection#44365459 with SmtpPooledStream#20390146 System.Net Information: 0 : HeaderCollection#30689639::Set(content-transfer- encoding=base64) System.Net Information: 0 : HeaderCollection#30689639::Set(content-transfer- encoding=quoted-printable) System.Net Information: 0 : HeaderCollection#23085090::Remove(x-receiver) System.Net Information: 0 : HeaderCollection#23085090::Set([email protected]) System.Net Information: 0 : HeaderCollection#23085090::Set([email protected]) System.Net Information: 0 : HeaderCollection#23085090::Set(date=1 Apr 2010 16:32:32 −0500) System.Net Information: 0 : HeaderCollection#23085090::Set(subject=FTP Successful) System.Net Information: 0 : HeaderCollection#23085090::Get(mime-version) System.Net Information: 0 : HeaderCollection#23085090::Get(from) System.Net Information: 0 : HeaderCollection#23085090::Get(to) System.Net Information: 0 : HeaderCollection#23085090::Get(date) System.Net Information: 0 : HeaderCollection#23085090::Get(subject) System.Net Information: 0 : HeaderCollection#30689639::Get(content-type) System.Net Information: 0 : HeaderCollection#30689639::Get(content-transfer- encoding) System.Net Information: 0 : Exiting SmtpClient#17113003::Send()
Reading this file, you can see that the reference numbers that match the requests on the server all appear, dramatically improving the ease of debugging. Also, because everything is in order of action, finding out exactly where the error occurred in the process is much easier.