Chapter 4
IN THIS CHAPTER
Taking a tour of the System.Net
namespace
Using built-in tools to access the network
Making the network tools work for you
The .NET Framework is designed from the ground up to take the Internet and networking in general into consideration. Not surprisingly, the emphasis on connectivity in every form is nowhere more clear than it is in the System.Net
namespace (https://docs.microsoft.com/dotnet/api/system.net
). The Internet takes first chair here, with web tools taking up 13 of the classes in the namespace.
System.Net
is a big, meaty namespace, and finding your way around it can be difficult, so this chapter begins with an overview of the namespace. The chapter then discusses tasks that you perform often and shows the basics of performing them. Next, you find out about the tools to research complex features of the classes.
Networking is a big part of the .NET Framework, and all the functionality is in this namespace. An entire book can be (and has been) written on the subject. For the purposes of this introduction to networking with C#, this chapter shows you the following features:
Keep in mind that sockets, IPv6, and other advanced Internet protocols are important, but many developers don't currently use them every day. This chapter talks about the parts of the namespace that you will use every day. As always, there is more to discover 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 4,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). You can obtain copies of these RFCs in locations such as https://www.rfc-editor.org/standards
and http://www.faqs.org/rfcs/
. If you have to learn all the RFCs separately, you'll never complete your project. The System.Net
namespace is about making network coding less painful.
System.Net
isn't just for web projects. Like everything else in the base class library, you can use System.Net
with all kinds of projects. You can
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 website, 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 connected to another application through an Application Programming Interface (API) that you access using System.Net
functionality. 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 networks that adhere to web standards (including an intranet accessible within your organization).
The
namespace contains a large number of classes and smaller namespaces. The number of classes and namespaces increases with each version of the .NET Framework, so it pays to look after each update to see what new goodies are present. The number may seem overwhelming. However, if you look closely, you can see patterns. The following sections discuss the smaller namespaces and provide a listing of essential System.Net namespace classes.System.Net
When you view the System.Net documentation at https://docs.microsoft.com/dotnet/api/system.net
, you see a few subordinate namespaces listed in the “See Also” section of the page. However, this isn't a complete list, and there really are a number of additional subordinate namespaces you need to know about, as shown in Table 4-1. The Latest Full Implementation column tells you the latest version of .NET that contains the full set of classes for the namespace. Later versions may remove some functionality from the namespace.
TABLE 4-1 A Listing of Important System.Net-Associated Namespaces
Namespace | Purpose | Latest Full Implementation | Link |
---|---|---|---|
| A cache provides the means to improve application performance by storing commonly used resources. This namespace works with the | 6.0 | |
| Manages the configuration settings for web-based applications. These settings affect all the other System.Net namespaces and classes. | 4.8 |
|
| Provides the functionality needed to create HTTP applications. It's where you find the client, content, message, request, and response-oriented classes. The .NET Framework 5.0 provides some enhancements to this namespace. | 6.0 | |
| Contains the classes for managing the headers used with the | 6.0 |
|
| Allows sending (not receiving) of email using the Simple Mail Transfer Protocol (SMTP). The classes follow RFC 2821 ( | 6.0 | |
| Contains classes that provide Multipurpose Internet Mail Exchange (MIME) support for the | 6.0 | |
| Obtains network data that includes traffic data, network address information, and notification of address changes for the local computer. This is also where you find an implementation of the Ping utility ( | 6.0 |
|
| Provides access to peer-to-peer networking functionality. It provides an implementation of the Peer Name Resolution Protocol (PNRP) discussed at | 4.8 | |
| Enhances the | 4.8 |
|
| Allows secure communication using network streams. This namespace received significant updates in .NET 5.0 and .NET 6.0. | 6.0 | |
| Implements Windows Sockets (Winsock) functionality (see | 4.8 | |
System.Net.WebSockets | Implements Web Sockets (WebSocket) functionality (see | 6.0 |
The System.Net
classes are well named, and you will note that a few protocols get a number of classes each. After you translate the entry names, 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.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 the 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 the namespaces found in Table 4-1.
The System.Net
namespace is code oriented, which means that few implementations are specifically for user interfaces. Almost 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 fact, the examples in the remainder of the chapter build a Windows Forms application that has the following requirements:
First, you need to inform the user about network connectivity using the NetworkTools
example. To begin, create a Windows Forms application using the same steps as found in the “Creating the data access project” section of Chapter 2 of this minibook (making the requisite changes for example name and location), and then follow these steps when you see the form displayed:
Add a StatusStrip
control to the lower left of the form by dragging it from the Menus & Toolbars group of the Toolbox.
The StatusStrip
automatically takes up the entire bottom area of the form.
Select the SmartTag that appears on the left side of the StatusStrip
and add three StatusLabels
.
Figure 4-1 shows how the dropdown entries appear when you click the down arrow in the box.
Select each StatusLabel
in turn and configure it for displaying network and application status.
Configure your StatusLabel
controls like this:
toolStripStatusLabel1
: Set (Name)
to NetStatus
and Text
to Not Connected
.toolStripStatusLabel2
: Set (Name)
to PictureStatus
and Text
to Not Downloaded
.toolStripStatusLabel3
: Set (Name)
to EmailStatus
and Text
to No Email Sent
.0Figure 4-2 shows how your StatusStrip
should appear at this point.
Double-click the form.
Visual Studio creates the Form1_Load()
method and displays the Code Editor for you.
System.Net
namespace by adding the line using System.Net.NetworkInformation;
to the top of the code.Form1_Load()
to test whether the network is available and display it on the status bar:
private void Form1_Load(object sender, EventArgs e)
{
if (NetworkInterface.GetIsNetworkAvailable())
{
NetStatus.Text = "Connected";
}
else
{
NetStatus.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.
You can get a file from the Internet in one of several ways, and one of the most common is by using hypertext transfer protocol (HTTP). You can find something to download from HTTP sites across the web today, so it's an important protocol to know. To build an application that uses HTTP, start with the example from the previous section and follow these steps:
Button
control onto the form from the Toolbox.Text
property and btnDownload in the button's (Name)
property.Double-click the button.
Visual Studio creates the btnDownload_Click()
method and displays the Code Editor for you.
using System.Net;
using System.IO;
DownloadFile
that accepts a remoteFile
and a localFile
as type string
.DownloadFile()
method:
private void DownLoadFile(string remoteFile, string localFile)
{
// Create the stream and request objects.
FileStream localFileStream = File.Create(localFile);
HttpWebRequest httpRequest =
(HttpWebRequest)WebRequest.Create(remoteFile);
// Configure the request.
httpRequest.Method = WebRequestMethods.Http.Get;
// Configure the response to the request.
WebResponse httpResponse = httpRequest.GetResponse();
Stream httpResponseStream = httpResponse.GetResponseStream();
byte[] buffer = new byte[1024];
// Process the response by downloading data.
int bytesRead = httpResponseStream.Read(buffer, 0, 1024);
while (bytesRead > 0)
{
localFileStream.Write(buffer, 0, bytesRead);
bytesRead = httpResponseStream.Read(buffer, 0, 1024);
}
// Close the streams.
localFileStream.Close();
httpResponseStream.Close();
// Update the status strip.
PictureStatus.Text = "Picture Downloaded";
}
The code follows a process of establishing a connection, configuring the connection, configuring a response to that connection, and then performing a task. In this case, the task is to download a file from the HTTP site. You must always close the streams when you finish performing a task.
DownloadFile()
method from the btnDownload_Click()
event handler by using the following code:
private void btnDownload_Click(object sender, EventArgs e)
{
// Obtain the file.
DownLoadFile(@"http://blog.johnmuellerbooks.com/" +
"wp-content/uploads/2014/06/cropped-country01-1.jpg",
@"c: empCountry_Scene.jpg");
}
In fact, the toughest part of this process is dealing with a FileStream
object, which is still the best way to move files and is not specific to the System.Net
namespace. Streams are discussed in Chapter 3 of this minibook, 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
.
Email is a common requirement of networked systems. This example works fine with your personal email, but you need to know the SMTP server address (required when you set up your email application), your username, and your password to make it work. If you are working in an enterprise environment, you are going to write a larger-scale application to handle all email requirements, rather than make each individual application email-aware. However, if you are writing a stand-alone product, it might require email support.
Add four TextBox
controls named EmailAddress
, SMTPServer
, Username
, and Password
, and four Label
controls to the default form in Design View.
Your form should look similar to the one shown in Figure 4-3.
using System.Net.Mail;
Create a new method called SendEmail()
that accepts fromAddress
, toAddress
, subject
, and body
, all of type string
.
It should accept the from email address, the to email address, the subject of the email, and the body of the email.
SendEmail()
method:
private void SendEmail(string fromAddress, string toAddress,
string subject, string body)
{
// Define the message.
MailMessage message =
new MailMessage(fromAddress, toAddress, subject, body);
// Configure the credentials.
NetworkCredential Creds =
new NetworkCredential(Username.Text, Password.Text);
// Create the connection and send the message.
SmtpClient mailClient = new SmtpClient(SMTPServer.Text)
{
Credentials = Creds
};
mailClient.Send(message);
// Release the message and client.
message = null;
mailClient = null;
// Update the status strip.
EmailStatus.Text = "Email Sent";
}
The example follows a process that starts by creating a message. It then creates a client that provides the connection to the host and sends the message to the host. Notice that you must provide credentials (in most cases) to access the STMP server, and that you add these as part of the SmtpClient
configuration as Credentials
. The last step is to release the message and client so that the garbage collector can reclaim them.
After you have written your method, you need to call it after the file is downloaded in the btnDownload_Click()
event handler. Change the code of that subroutine to the following to call that method:
private void btnDownload_Click(object sender, EventArgs e)
{
// Obtain the file.
DownLoadFile(@"http://blog.johnmuellerbooks.com/" +
"wp-content/uploads/2014/06/cropped-country01-1.jpg",
@"c: empCountry_Scene.jpg");
// Send a success message.
SendEmail(EmailAddress.Text, EmailAddress.Text,
"HTTP Successful", "Picture Successfully Downloaded");
}
The example uses the value of the EmailAddress
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 in which you want the email to come only from a webmaster address or to go only to your address.
To run the application, provide your email address, SMTP server address, username, and password in the fields of the form. When you click Download, the application should download the file to the local drive and then email you to inform you that the download is complete. You will also see status information at the bottom of the application. A host of things can go wrong with network applications, though, and you should be aware of them. Here are a few:
SmtpServer
you coded, what went wrong? You may never know.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, as with 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 by double-clicking its entry in Solution Explorer, you see that it has practically nothing in it (as shown in Listing 4-1). This is fairly new for .NET, which used to have very involved configuration. You will add some content to the configuration to get tracing turned on. Note that your .NETFramework,Version
version number may vary from the one shown.
LISTING 4-1 The Default app.config File
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.7.2"/>
</startup>
</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 additions in bold, is shown in Listing 4-2.
LISTING 4-2 The Finished app.config File
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0"
sku=".NETFramework,Version=v4.5.2"/>
</startup>
<system.diagnostics>
<sources>
<source name="System.Net">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net" value="Verbose" />
</switches>
<sharedListeners>
<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. Listing 4-3 shows how the content of this file could appear. The specific URLs, reference numbers, and other object values will differ in your output, but you should see something similar to the output shown here (which has been edited for brevity):
System.Net Verbose: 0 : [15048] Entering WebRequest::Create(http://blog.johnmuellerbooks.com/wp-content/uploads/2014/06/cropped-country01-1.jpg)
System.Net Verbose: 0 : [15048] Entering HttpWebRequest#33675143::HttpWebRequest(http://blog.johnmuellerbooks.com/wp-content/uploads/2014/06/cropped-country01-1.jpg#-113598309)
System.Net Information: 0 : [15048] Current OS installation type is 'Client'.
System.Net Information: 0 : [15048] RAS supported: True
System.Net Verbose: 0 : [15048] Exiting HttpWebRequest#33675143::HttpWebRequest()
System.Net Verbose: 0 : [15048] Exiting WebRequest::Create() -> HttpWebRequest#33675143
System.Net Verbose: 0 : [15048] Entering HttpWebRequest#33675143::GetResponse()
…
System.Net Information: 0 : [15048] HeaderCollection#39451090::Get(MIME-Version)
System.Net Information: 0 : [15048] HeaderCollection#39451090::Get(From)
System.Net Information: 0 : [15048] HeaderCollection#39451090::Get(To)
System.Net Information: 0 : [15048] HeaderCollection#39451090::Get(Date)
System.Net Information: 0 : [15048] HeaderCollection#39451090::Get(Subject)
System.Net Information: 0 : [15048] HeaderCollection#26753075::Get(Content-Type)
System.Net Information: 0 : [15048] HeaderCollection#26753075::Get(Content-Transfer-Encoding)
System.Net Information: 0 : [15048] HeaderCollection#26753075::Get(Content-Transfer-Encoding)
System.Net Information: 0 : [15048] HeaderCollection#26753075::Get(Content-Transfer-Encoding)
System.Net Verbose: 0 : [15048] Exiting SmtpClient#13869071::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.
One of the more interesting entries is the series of three commands used to retrieve each block of data for the picture. The ConnectStream
exits the previous read, enters a new read, and then downloads the data like this (along with the actual binary data):
System.Net Verbose: 0 : [15048] Exiting ConnectStream#47891719::Read() -> Int32#1024
System.Net Verbose: 0 : [15048] Entering ConnectStream#47891719::Read()
System.Net Verbose: 0 : [15048] Data from ConnectStream#47891719::Read