Chapter 13. Networking

Most interesting computer systems are distributed these days—it’s increasingly unusual for a program to run in isolation on a single machine. So .NET provides various ways to communicate across networks. The array of networking options looks a little bewildering at first: there are 10 namespaces whose names start with System.Net containing more than 250 classes, and that’s not even the complete set—there’s an even bigger API for producing and consuming web services.

Fortunately, it’s simpler than this makes it seem—despite the large API surface area most of the options fall into three categories. There’s WCF—the Windows Communication Foundation, a framework for building and using web services. There are lower-level APIs for working directly with web protocols. Or you can use sockets if you need very low-level control. We’ll start by discussing how to choose the most appropriate style of communication for your application, and then we’ll look at these three options in more detail.

Choosing a Networking Technology

The first step in choosing the right networking API is to decide on the nature of the communication your application requires. There are many different styles of distributed applications. Perhaps you are building a public-facing web service designed to be used by a diverse range of clients. Conversely, you might be writing client code that uses someone else’s web service. Or maybe you’re writing software that runs at both ends of the connection, but even then there are some important questions. Are you connecting a user interface to a service in a tightly controlled environment where you can easily deploy updates to the client and the server at the same time? Or perhaps you have very little control over client updates—maybe you’re selling software to thousands of customers whose own computers will connect back to your service, and you expect to have many different versions of the client program out there at any one time. Maybe it doesn’t even make sense to talk about clients and servers—you might be creating a peer-to-peer system. Or maybe your system is much simpler than that, and has just two computers talking to each other.

The variations are endless, so no single approach can work well for all systems. The next few sections will look at some common scenarios, and discuss the pros and cons of the various networking options .NET offers. Even within a specific scenario there will often be more than one way to make things work. There are no hard-and-fast rules, because each project has different requirements. So this section won’t tell you what to do—it’ll just describe the issues you’ll need to consider. Ultimately, only you can decide on the right solution for your system. We’ll start with a very common web-based scenario.

Web Application with Client-Side Code

Web user interfaces have been getting smarter lately. A few years ago, most of a web application’s logic would live on the server, with client-side code in the web browser typically doing little more than making buttons light up and menus fly out in response to the mouse. But now, we expect more from our web user interfaces. Whether you use AJAX (Asynchronous JavaScript and XML), or a RIA (Rich Internet Application) technology such as Silverlight or Flash, web applications often communicate constantly with the web server, and not just when navigating between pages.

If you’re writing the server-side parts of this sort of application in C#, you will typically use ASP.NET to provide a web user interface. But what should you use for programmatic communication—the messages that flow between the web UI and the server once a page is already loaded?

WCF is a flexible choice here, because as Figure 13-1 illustrates, you can make a single set of remote services accessible to many common browser-based user interface technologies. A WCF service can be configured to communicate in several different ways simultaneously. You could use JSON (JavaScript Object Notation), which is widely used in AJAX-based user interfaces because it’s is a convenient message format for JavaScript client code. Or you could use XML-based web services. Note that using WCF on the server does not require WCF on the client. These services could be used by clients written in other technologies such as Java, as long as they also support the same web service standards as WCF.

Looking specifically at the case where your web application uses C# code on the client side, this would mean using either Silverlight or WPF. (You can put WPF in a web page by writing an XBAP—a Xaml Browser Application. This will work only if the end user has WPF installed.) If you’re using C# on both the client and the server, the most straightforward choice is likely to be WCF on both ends.

What if your server isn’t running .NET, but you still want to use .NET on the web client? There are some restrictions on WCF in this scenario. Silverlight’s version of WCF is much more limited than the version in the full .NET Framework—whereas the full version can be configured to use all manner of different protocols, Silverlight’s WCF supports just two options. There’s the so-called basic profile for web services, in which only a narrow set of features is available, and there’s a binary protocol unique to WCF, which offers the same narrow set of features but makes slightly more efficient use of network bandwidth than the XML-based basic profile. So if you want a Silverlight client to use WCF to communicate with a non-.NET web service, as Figure 13-2 illustrates, this will work only if your service supports the basic profile.

Web application clients and a WCF service

Figure 13-1. Web application clients and a WCF service

Silverlight client and non-.NET web service

Figure 13-2. Silverlight client and non-.NET web service

More surprisingly, similar restrictions exist with a WPF XBAP. Even though XBAPs use the full version of the .NET Framework, certain features of WCF are disabled for security purposes—client code in web browsers shouldn’t have complete freedom to connect to anywhere on the Internet, because that would make life too easy for hackers. So WCF offers only a very limited version of its services to .NET applications running inside web browsers, meaning that XBAPs have similar WCF limitations to Silverlight.

If you’re writing a Silverlight client and you want to talk to a service that does not conform to the web services basic profile, that’s not necessarily a showstopper. It just rules out WCF—you will need to use the lower-level web-based APIs instead, or even the socket APIs, depending on the service.

Note that while WCF is usually a good default choice on the server side for web applications with client-side code, there are a few cases where you might not want to use it. ASP.NET provides its own mechanism for supporting AJAX clients, and while it’s considerably less flexible than WCF, you might not need the flexibility. The simplicity of using just one framework on the server instead of two might end up looking like a better option.

There’s a subtler reason why WCF might not always be the best fit: the style of communication. If you use WCF in a web application, the communication it supports will tend to involve the following steps:

  1. Some code in the client (browser script, C# code, or Flash ActionScript) decides to send a message to the server.

  2. The server receives the message and runs some code that does whatever it needs to do to process the message.

  3. Once the code has finished, the server sends a message back to the client containing any data returned by the code (or if there is no data to return, just a message to say the work is complete).

This is, in effect, a remote method invocation—it’s a way for the client to ask the server to run some code and optionally get a return value. (WCF is more flexible than this in general, but in the context of a web application, your communication patterns are constrained because clients will typically be behind a firewall.) That’s likely to be a perfectly good pattern for operations such as looking up a stock price or retrieving a weather forecast. However, if you are building a photograph browser application, this would not be a great way to retrieve pictures. You could make it work, but it’s easier to use the mechanisms already built into the web browser for downloading images—you’d almost certainly want to make the bitmaps available for download via HTTP rather than using WCF. HTML and Silverlight have UI elements that know how to render images downloaded with HTTP. Browsers are usually able to start rendering images without having to wait for the download to finish, and that’s difficult to achieve with a method invocation idiom. And by using normal HTTP image download, you’d also get to take advantage of standard HTTP caching in your web browser and any caching proxies you may be using. Plain old HTTP works better here than trying to fetch a bitmap using something resembling a method call.

More generally, if the information your client code works with looks like a set of resources that might be identified with URIs (Uniform Resource Identifiers; for instance, http://oreilly.com/) and accessed via HTTP you might want to stick with ordinary HTTP rather than using WCF. Not only do you get the benefits of normal HTTP caching when reading data, but it may also simplify security—you might be able to take whatever mechanism you use to log people into the website and secure access to web pages, and use it to secure the resources you fetch programmatically.

Note

A service that presents a set of resources identified by URIs to be accessed via standard HTTP mechanisms is sometimes described as a RESTful service. REST, short for Representational State Transfer, is an architectural style for distributed systems. More specifically, it’s the style used by the World Wide Web. The term comes from the PhD thesis of one of the authors of the HTTP specification (Roy Fielding). REST is a much misunderstood concept, and many people think that if they’re doing HTTP they must be doing REST, but it’s not quite that straightforward. It’s closer to the truth to say that REST means using HTTP in the spirit in which HTTP was meant to be used. For more information on the thinking behind REST, we recommend the book RESTful Web Services by Sam Ruby and Leonard Richardson, (O’Reilly).

Using WCF typically requires less effort than designing a RESTful service—you can get up and running with a good deal less thought and forward planning (although you might not consider a lack of thought and planning to be a good thing for your particular application). But if the communication you require with your server doesn’t sound like it fits well into a method-call-like style, you’ll probably want to consider alternatives to WCF.

Occasionally, neither WCF nor plain HTTP will be the best approach when connecting a web UI to a service. With Silverlight, you have the option to use TCP or UDP sockets from the web browser. (The UDP support is somewhat constrained. Silverlight 4, the current version at the time of writing this, only supports UDP for multicast client scenarios.) This is a lot more work, but it can support more flexible communication patterns—you’re not constrained to the request/response style offered by HTTP. Games and chat applications might need this flexibility, because it provides a way for the server to notify the client anytime something interesting happens. Sockets can also offer lower communication latency than HTTP, which can be important for games.

.NET Client and .NET Server

Fashionable though web applications are, they’re not the only kind of distributed system. Traditional Windows applications built with WPF or Windows Forms are still widely used, as they can offer some considerable advantages over web applications for both users and developers. Obviously, they’re an option only if all your end users are running Windows, but for many applications that’s a reasonable assumption. Assuming clients are running Windows, the main downside of this kind of application is that it’s hard to control deployment compared to a web application. With web applications, you only have to update an application on the server, and all your clients will be using the new version the next time they request a new page.

Note

Out-of-browser Internet applications could well blur this distinction. Both Silverlight and Flash make it possible for Internet applications to have parts that are installed on the user’s machine and run like normal applications outside the web browser. So the considerations in this section could apply if that’s the sort of web application you’re building.

To update a classic Windows application, you need to somehow get a new version of the program onto the end users’ machines. Since it’s rarely practical to install a new version on every single user’s machine simultaneously, you need to handle the possibility of having several different versions of the client software all trying to talk to your server. The extent to which this can cause problems will depend on how much control you have over the client computers.

Tightly controlled deployment

Some applications are deployed in tightly controlled environments. For example, suppose you’re writing a line-of-business application in WPF that will only ever be deployed to machines owned by your business. If your IT department has an iron grip on the company’s computers, you might be able to exert considerable control over what versions of your application are out there. Network administrators could forcibly upgrade users to the latest version. So new versions might overlap with old versions for only a day or so. You could even go further and arrange for your application to check for updates and refuse to continue running when a newer version is available.

This is a happy situation for a developer, because it makes it much easier to introduce changes to your server. Chances are that at some point you’ll want to add new services to support new features in your application. You might also want to modify existing services, which is usually more problematic than completely new features—if you’re using WCF, it’s not easy to modify the way an existing service works without breaking that service for older clients. It’s possible, but it’s hard, and it’s often easier to run multiple versions of the service simultaneously during the transition period. The nice thing about having sufficient control to remove old versions of the application is that you can know when you’ve reached the end of a transition period and can shut down the older version of the service. This won’t be the case if you can’t force that sort of change on the client.

Weakly controlled deployment

If your application’s customers don’t all work for your company, life becomes more complex, because it’s harder to force upgrades on your customers. It’s not impossible—for example, Microsoft’s Windows Live Messenger program occasionally tells you that if you don’t upgrade you won’t be able to carry on using the service. Mind you, it’s a free service, so it gets to dictate its terms of use; you might find that paying customers won’t put up with that, insisting that the product they’ve bought carries on working without needing to install regular upgrades.

The implication is that you might need to support old versions of your service indefinitely. At this point, WCF might not look like such a good choice. One of the attractive features of WCF is that it does a lot of work for you under the covers, but that’s a double-edged sword—it works really well when both ends of the connection evolve simultaneously, but it can become a burden over time if the two ends do not move forward in tandem. If you want a service to be able to evolve while the client does not, you end up needing to understand exactly how WCF presents your service, and how the changes you have in mind might affect its operation. For example, if you decide that a method in your service requires an extra argument, what happens when an old client invokes the operation without that new argument? In practice, it might actually be easier just to work directly with HTTP and XML, because that way you have complete control over what messages go across the network.

That’s not to say that WCF is definitely the wrong choice here. You could deal with the problem described by maintaining multiple versions of the service, or by dropping down to WCF’s lower-level messaging API, for example. But the trade-off between WCF and HTTP is altered by the nature of your deployment. In a tightly controlled deployment, WCF is likely to be a good choice, but when you have less control, the lower-level APIs can start to look like they’re worth the extra effort.

Regardless of how much control you have over deployment, as with the web application case there are some specialized scenarios in which neither WCF-based web services nor web APIs are the best fit. If you need communication patterns that don’t fit well with HTTP, be aware that with this style of application, you can use the full range of communication styles offered by WCF—as we’ll see, it supports more than just the typical web communication patterns. This means that sockets are an even more unusual choice in this scenario, and would typically be useful only if you need very precise control over the way in which messages are constructed and delivered.

.NET Client and External Party Web Service

You won’t necessarily write the code at both ends of a connection. You might build a .NET client which talks to a web service provided by someone else. For example, you could write a WPF frontend to an online social media site such as Twitter, or a Silverlight client that accesses an external site such as Digg.

In this case, your choice of communication technology will be determined largely by the service you’re connecting to. If it presents information in a way that WCF is able to consume, use WCF. How would you know that this is the case? You could try asking the service provider’s support staff if their service works with WCF, but if they’re not sure, it’ll be down to the nature of the service. If your service provider uses the so-called WS-* family of web service standards, there’s a good chance WCF will be able to talk to the service.

Warning

If you were hoping for something more definitive than “a good chance,” you’re out of luck. The mere fact that two systems have both opted to use the same set of standards is no guarantee that they’ll be able to communicate successfully, even if both ends conform strictly to the standards. If this information is news to you, welcome to the world of systems integration!

If WCF works in your scenario, that’s great, but when it is not an option, use .NET’s HTTP-based APIs. Unless, of course, the service in question is not HTTP-based, and requires you to work directly with TCP or UDP, in which case you would use sockets. In short, you’re at the mercy of the server, and you’ll just have to pick whichever option happens to work.

Note that because Silverlight’s version of WCF is considerably more limited than the full .NET Framework version, a Silverlight client is more likely to have to drop down to the HTTP APIs than a full .NET client.

External Client and .NET Web Service

If you are writing a web service in .NET that you would like to be accessible to client programs written by people other than you, the choice of technology will be determined by two things: the nature of the service and the demands of your clients.[26] If it’s something that fits very naturally with HTTP—for example, you are building a service for retrieving bitmaps—writing it as an ordinary ASP.NET application may be the best bet (in which case, refer to Chapter 21). But for services that feel more like a set of remotely invocable methods, WCF is likely to be the best bet. You can configure WCF to support a wide range of different network protocols even for a single service, thus supporting a wide range of clients.

As with the other application types, you would use sockets only if your application has unusual requirements that cannot easily be met using the communication patterns offered by HTTP.

So having looked at some common scenarios and seen which communication options are more or less likely to fit, let’s look at how to use those options.

WCF

WCF is a framework for building and using remotely accessible services. It’s particularly well suited to XML-based web standards, although it’s not limited to these. It provides a programming model that supports many different underlying communication mechanisms; as well as supporting numerous web service standards, WCF also offers high-performance proprietary protocols that you can use in end-to-end .NET systems, and it’s extensible, so support for other protocols can be added. WCF’s design makes many of these details a matter of configuration—you write services and clients in the same way no matter what communication mechanisms are in use.

To explore WCF, we’ll build a very simple instant messaging application to allow multiple users to chat with one another. So that we can focus on the communication code, the client will be a simple console application.

Creating a WCF Project

We’ll start with the server for our chat application. If you want to build your own copy of the project as you read, open Visual Studio’s New Project dialog (Ctrl-Shift-N) and in the template list on the left, select Visual C#WCF. Choose the WCF Service Library project template. Call the project ChatServerLibrary. Ensure that the “Create directory for solution” checkbox is checked, and call the solution WcfChat.

This project will produce a DLL as its output, because the WCF Service Library project template doesn’t commit to hosting the WCF service in any particular container application. WCF can run inside IIS, a Windows Service, a console application, or indeed pretty much any .NET application. If you want to use a particular kind of host, you can just create the relevant type of project—for example, instead of creating a WCF Service Library, you could create an ASP.NET web application project if you wanted to host your WCF service in there. (You can add a WCF service as a new item to an existing web project, so you don’t need a WCF-specific project type.) But there are a couple of benefits to this library-based template: as you’ll see shortly, it provides an easy way to do simple manual testing of the service. Also, it means you can host the service in multiple different host applications, which can be useful for automated testing—you can test the service without having to deploy it into its intended environment.

Visual Studio will have added a single service to the project, called Service1. This contains some example code that does things we don’t need in our chat application, so we’ll ignore that. (Feel free to delete them if you’re building your own version as you read this.) We’ll add a new WCF Service item to the project with the Add New Item dialog, called ChatService. Visual Studio adds two files to the project: ChatService.cs and IChatService.cs. This reflects the fact that WCF makes a distinction between the code that implements a service, and the contract for that service.

WCF Contracts

When two systems communicate over a network, they need to agree on what information is to be sent back and forth. WCF formalizes this with what it calls contracts. So the IChatService interface added by the wizard represents a service contract. The service contract defines the operations the service offers. As Example 13-1 shows, the interface is marked with a ServiceContract attribute to make it clear that it’s a contract definition.

Example 13-1. A service contract

[ServiceContract]
public interface IChatService
{
    [OperationContract]
    void DoWork();
}

Each method in the interface that defines an operation offered by the service must be marked with an OperationContract. You might have thought that it would be enough that the interface is marked as ServiceContract—why do we also need to annotate each method? WCF requires you to be explicit so that it’s always obvious when you’re defining some aspect of your system that will be visible across the network. A method call to a local object is a quite different kind of operation than using a remote service—the performance and reliability characteristics are poles apart—so it’s important for such boundaries to be clearly visible in the code.

Note

Although we’re defining a method for each operation, ultimately the contract defines what messages can go in and out of the service. To invoke an operation, a client will need to send a message to the server over the network. When you add a method marked with OperationContract to an interface marked with ServiceContract, you are really defining the logical structure of the message that will be sent to invoke that operation, and also of the message that will be sent back to the client when the operation is complete. WCF lets you represent these message formats as method signatures because it’s a convenient abstraction for developers.

WCF supports other ways of defining message formats—you can write a contract in WSDL, the Web Service Definition Language, and then generate types from that. This approach is beyond the scope of this book.

Our service is designed to let people chat, so it will need to provide clients with a way to send a short bit of text, which we’ll refer to as a note. (A more obvious name would be message, but that would introduce ambiguity—WCF sends messages to and from the server for every operation, so to call one of the pieces of information that crops up in certain messages a message would be confusing.) To keep things simple, we’ll just have one big chat room where everyone can see every note; we’re not going to support private conversations. To support sending notes, we’ll get rid of the DoWork method provided by Visual Studio, and replace it with the code in Example 13-2.

Example 13-2. Modifying the contract

[OperationContract]
void PostNote(string from, string note);

If you attempt to build your project in Visual Studio, you’ll get a compiler error:

error CS0535: 'ChatServerLibrary.ChatService' does not implement interface
 member 'ChatServerLibrary.IChatService.PostNote(string, string)'

Remember that Visual Studio added two files: IChatService.cs (the contract) and ChatService.cs (the service implementation). The compiler is pointing out to us that our service implementation no longer conforms to the contract for the service. So in ChatService.cs, we need to replace the DoWork method with this code:

public void PostNote(string from, string note)
{
    Debug.WriteLine("{0}: {1}", from, note);
}

For this to compile, you’ll need to add a using System.Diagnostics; directive to the top of your file.

Note

There’s an obvious security question with this service: how do we know that the note comes from the person it claims to come from? The answer is that we don’t—identification is a complex topic, with many possible solutions. The appropriate choice of solution would depend on the context in which the application will be used—on a corporate network, integrated Windows security might be best, but that wouldn’t work for a public-facing Internet application. The way to solve these problems is currently an area of debate, and could easily fill a chapter. Since this example just illustrates the basic mechanics of WCF, we are using the naïve trust model for identity: users can claim to be whoever they want to be, and our application will believe them.

WCF Test Client and Host

You can now build and run the application—either press F5 or choose DebugStart Debugging. Normally, you’d get an error if you tried to run a library project, because you can’t run a DLL. However, Visual Studio knows this is a WCF project, and it has a special feature for running and testing WCF libraries. When you run the project, you’ll see a balloon pop up in the taskbar notification area, as Figure 13-3 shows.

WCF test service host

Figure 13-3. WCF test service host

The WCF Service Host (or WcfSvcHost, as it’s abbreviated in the pop up) is a program provided by Visual Studio that loads your WCF DLL and makes its services available for local access for debugging purposes. Visual Studio also launches a second program, the WCF Test Client—this is a Windows application that provides a UI for invoking operations on your service to try it out. As Figure 13-4 shows, it presents a tree view listing all the services defined by your project, and all the operations available in each service. (If you’ve deleted the unwanted IService1 mentioned earlier in your code, you’ll only see one service.)

Services listed in the WCF Test Client

Figure 13-4. Services listed in the WCF Test Client

The test client has found both the original Service1 service that we chose to ignore and the ChatService we added. Double-clicking on the PostNote item that represents the operation we defined for the chat service shows a tab on the right that lets us try out the service—the test client’s job is to let us try invoking service operations without having to write a whole program just to do that. Figure 13-5 shows this tab with arguments. If you look at the Value column, you’ll see arguments for the from and note parameters of the PostNote operation—you can just type these directly into the Value column.

Clicking the Invoke button invokes the PostNote operation on the service. We can verify that the information typed into the WCF Test Client made it through, by looking in Visual Studio’s Output window—that’s where text sent to Debug.WriteLine appears. (There’s an item on the View menu to make the Output window visible, if it’s not already open.) The Output window gets fairly busy, so you might have to look quite carefully, but somewhere in the noise, you’ll see that the from and note argument values are both shown, for example:

Ian: Hello, world

Note

If you’re trying this yourself, it’s possible you’ll see an error back in the WCF Test Client if you set breakpoints in Visual Studio—the client program will time out if you spend too long suspended at a breakpoint. It’s common with networking systems to give up after a certain length of time. If a client doesn’t get a response, all manner of things could be wrong—there may be a network problem, perhaps locally, or maybe at the server end, or somewhere in between. Maybe the server is offline, or just too busy to respond to the request. The client can’t easily tell—all it knows is it’s not getting a response. So by default, WCF gives up after a minute and throws an exception. The WCF Test Client reports this with an error dialog.

Passing arguments with the WCF Test Client

Figure 13-5. Passing arguments with the WCF Test Client

Once the test client has received a response from the service, it indicates this in the bottom half of the tab. Our PostNote operation has a return type of void, which means that it sends back an empty response. (It still sends a response to report that the operation has finished. It just contains no data.)

You may be curious to know what the messages being sent between the client and the server look like. And if you’re not, we’d recommend becoming curious about such things. It’s difficult to design good, nontrivial distributed systems (and impossible to diagnose problems with them) if you don’t know what the messages they send look like. Sadly, some developers are happy to be ignorant about this sort of thing, but they frequently get stuck and have to ask for help from people who know what they’re doing anytime something goes wrong. If you’d rather be one of the wizards who can fix these problems, you need to learn what the messages that go over the network really look like. You can see the messages in the WCF Test Client by clicking on the XML tab at the bottom. It’s beyond the scope of this book about C# to explain the structure of these WCF messages in detail, but it’s easy to see where the data you sent ended up in this example. If you want to learn more, the book Learning WCF by Michele Leroux Bustamante (O’Reilly) would be a good place to start, or for a more advanced treatment, you could try Programming WCF Services by Juval Lowy (O’Reilly).

Note

If you plan to do any real work with network communications, one of the most useful things you can do is get familiar with a tool that lets you inspect the contents of the messages being sent and received by your computer’s network card. Microsoft’s Network Monitor program is available for free, as is the open source Wireshark. They can seem a little intimidating at first because of the sheer level of detail they offer, but they’re an indispensable tool for diagnosing communication problems, because they show you exactly what messages were sent and what they contained.

The WCF Service Host and Test Client are useful for very simple interactive testing, but a real, useful service needs to be hosted somewhere more permanent. So next, we’ll look at how .NET programs can host WCF services.

Hosting a WCF Service

WCF services are flexible about their location—any ordinary .NET application can host WCF services, so there’s no such thing as a specialized WCF Service Host project template in Visual Studio. You can host WCF services inside ASP.NET web applications, Windows Services, console applications, or even applications with GUIs built with Windows Forms or WPF. Any process that can accept incoming network connections should work, so about the only place you can’t host a WCF service is in a process where security constraints prevent inbound connections, such as a web browser. (For example, Silverlight clients can make outbound WCF connections, but they can’t host a service that accepts incoming connections.)

ASP.NET web applications are a particularly popular host environment for WCF services, because ASP.NET solves a lot of the problems you need to solve for an online service. Web applications automatically become available when a machine starts up—there’s no need for anyone to log in and start a program. ASP.NET provides a robust hosting environment—it’s able to restart after errors, and integrate into diagnostic management systems so that system administrators can discover when problems occur. There are well-understood ways to load-balance web applications across multiple servers. ASP.NET can make use of IIS security features such as integrated authentication.

However, ASP.NET is not always the right choice. A WCF service hosted in a web application can’t use the full range of protocols supported by WCF—incoming messages have to arrive by HTTP. Also, web applications usually get to run code only while they are actively handling a request from a client. If you need to perform long-running work that continues even when there are no clients connected right now, a web application host might be a bad idea, because in some configurations ASP.NET will restart web applications from time to time, or may even shut them down completely when they’ve had no incoming requests lately. So in some situations it might make more sense to write your own host. A Windows Service might be a good bet, as it can start automatically when the machine starts.

Sometimes it’s useful to host a WCF service inside a normal Windows application. Imagine a WPF application providing some sort of advertising display on a screen in a shop window—it could be useful to build a WCF service into this to enable the display to be controlled without needing physical access to the machine.

The techniques for hosting look much the same in all cases. And since we won’t be getting on to ASP.NET until later in the book, we’ll keep it simple by hosting our service in a console application. It’ll be easy enough to move it into different hosting environments later because the service itself is in a separate DLL project—we could just add it to a Windows Service or a web application.

Regardless of the type of host, one of the most important parts of WCF hosting is the configuration file.

WCF configuration

If you look in the ChatServerLibrary project, you’ll find an App.config file. You’ll find one of these, or its web equivalent, web.config, in lots of different kinds of .NET applications, but an App.config in a library project is something of an anomaly—application configuration files configure applications, and a library is not an application. Normally, adding an App.config file to a project that builds a DLL does nothing useful, but WCF projects are an exception because of the WCF Service Host we saw earlier. The test host loads the contents of this file into its own application configuration. Normally, application configuration files must go either into projects that build executable applications, or into web projects.

Warning

The App.config in a WCF Service Library project is used only by the WCF Service Host. You will always need to copy the configuration into your real service host application.

So that we can have an application to configure, we’ll add a console application called ChatHost to our WcfChat solution. This console application will host our WCF service, so we’ll add a reference to the ChatServerLibrary. And since we’ll be using this console application as the host from now on instead of WcfSvcHost, we’ll need to copy the configuration in the ChatServerLibrary project’s App.config into the ChatHost project’s App.config. (Once we’ve done this, we can delete the App.config in the ChatServerLibrary project.)

We’ll look at each of the App.config file’s sections to understand how the file works. Everything lives inside the root <configuration> element—all App.config and web.config files have one of these, no matter what sort of application you’re writing. The first child element will be this:

<system.web>
  <compilation debug="true" />
</system.web>

Our example doesn’t need this, so it’s safe to delete it. The WCF Service Library template adds this in case you are planning to host the project in a web application—this enables debugging in web apps. But since we’re not writing a web application, it’s not needed here.

Next is a <system.serviceModel> element—in fact, all the remaining contents of the App.config file are inside this element. This is where all WCF configuration lives, regardless of the type of host application.

The first element inside the WCF configuration is <services>. This contains a <service> element for each service the program will host. Visual Studio has added two: one for the Service1 service that we’re not using, and one for the ChatService we wrote. Since we don’t need the Service1 service, we can delete that first <service> element and everything it contains. This leaves the <service> element for our ChatService. It begins:

<service name="ChatServerLibrary.ChatService">

The name attribute is the name of the class that implements the service, including the namespace. Inside the <service> element we find some <endpoint> elements. Remember that earlier we said WCF can make a single service implementation accessible through multiple communication mechanisms. You do that by adding one endpoint for each mechanism you wish to support. Here’s the first endpoint Visual Studio added for us:

<endpoint address=""
          binding="wsHttpBinding"
          contract="ChatServerLibrary.IChatService">
  <identity>
    <dns value="localhost" />
  </identity>
</endpoint>

An endpoint is defined by three things: an address, a binding, and a contract—sometimes referred to collectively as the ABC of WCF. The address is typically a URL—it’s the address a client would use to connect to the service. In this case the address is blank, which means WCF will deduce the address for us—we’ll see how in a moment.

The binding determines the communication technology that WCF will use on this endpoint. Here we’ve used one of the built-in bindings called wsHttpBinding. The “ws” denotes that this uses the various web service standards that begin with WS-. So this binding supports standards such as WS-ADDRESSING and WS-SECURITY. This is a feature-rich binding, and it may use features that some clients don’t understand—it’s not supported by Silverlight, for example. If you wanted to use the basic profile that Silverlight clients support, you’d specify basicHttpBinding here instead. But for this application, you can leave the binding as it is.

Finally, the contract attribute here contains the name of the interface type that defines the operation contract for our service. We already looked at contracts—this refers to the interface we saw in Example 13-1 and modified in Example 13-2.

Inside the <endpoint> element you’ll see an <identity> element. This is intended for scenarios where the service needs to be able to identify itself securely to a client—for example, in a banking application you’d want to be confident that you’re really talking to your bank. But we’re not going to get into security in this example, so we can delete the <identity> element and its contents.

Visual Studio added a second endpoint to the App.config when we created the ChatService:

<endpoint address="mex"
          binding="mexHttpBinding"
          contract="IMetadataExchange" />

This enables something called metadata exchange—this endpoint doesn’t provide a way to use the service, and instead makes it possible to get a description of the service. We’ll be using this later when we build a client for our service.

Finally, after the two <endpoint> elements, you’ll see a <host> element, as Example 13-3 shows. (This contains a very long line, which has been split across two lines here to make it fit on the page.) This <host> element is still inside the <service> element, so like the two <endpoint> elements, this entry is still describing one particular service—our ChatService.

Example 13-3. Host element with default base address

<host>
  <baseAddresses>
    <add baseAddress=
"http://localhost:8732/Design_Time_Addresses/ChatServerLibrary/ChatService/" />
  </baseAddresses>
</host>

This element contains hosting information that applies to all of this service’s endpoints—this is how WCF works out what address to use for each endpoint. The baseAddress attribute is combined with the contents of the address attribute for each <endpoint> element to work out the effective address for that endpoint. Since the first endpoint’s address is empty, that endpoint’s address will be the baseAddress specified here. The second endpoint’s address was mex, so that endpoint for the service will be available at:

http://localhost:8732/Design_Time_Addresses/ChatServerLibrary/ChatService/mex

If you’re wondering why Visual Studio chose this slightly peculiar-looking address as the default base address for our service, see the sidebar below.

After the <services> element you’ll see a <behaviors> element in your App.config, containing a <serviceBehaviors> element which contains a <behavior> element. This section allows various WCF features to be switched on or off. You might wonder why these settings don’t just go into the <services> section. The reason is that you might want to host multiple services, all of which share common behavior configuration. You can define a single named <behavior> element, and then point multiple <service> elements’ behaviorConfiguration attributes at that behavior, reducing clutter in your configuration file. Or, as in this case, you can create an unnamed <behavior> element, which defines default behavior that applies to all services in this host process. Since we’re hosting only one service here, this doesn’t offer much advantage, but this separation can be useful when hosting multiple services.

The <behavior> element that Visual Studio provides has some comments telling you what you might want to change and why, but paring it down to the essential content leaves just this:

<behaviors>
  <serviceBehaviors>
    <behavior>
      <serviceMetadata httpGetEnabled="True" />
      <serviceDebug includeExceptionDetailInFaults="False" />
    </behavior>
  </serviceBehaviors>
</behaviors>

This configures a couple of optional features. The first is related to the metadata exchange mentioned earlier—it just ensures that the service description can be fetched in a certain way. Again, we’ll come back to metadata when we get to the client, so you can ignore that for now.

The second behavior here—the serviceDebug element—doesn’t have any effect, because it sets the includeExceptionDetailInFaults property to its default value, False. Nothing would change if you removed this. The only reason Visual Studio puts this here at all is to help you out when debugging—sometimes it’s useful to set this to True temporarily, and putting this entry in the file saves you from having to look up the name of the setting. Making this True will mean that if your service throws an exception, the full exception details including stack trace will be sent back to the client in the response.

Generally speaking, you should never do this, because sending stack traces to your clients reveals implementation details about your system. If some of your clients are evil hackers, this might make it easier for them to break into your system. (Technically, if your system is completely secure, a stack trace won’t help them, but when did you last hear about a computer system that was completely secure? It’s safe to presume that everything has security flaws, so the less help you give hackers the better—this is often described as reducing the attack surface area of your system.) While you don’t normally want to send stack traces over the network, doing so can sometimes make it easier to diagnose problems during development. So you might switch this setting on temporarily to make your life easier. But remember to turn it off before you ship!

That’s everything Visual Studio put into our configuration file. This shows just a tiny fraction of all the settings we could put in there, but this isn’t a book about WCF, so that’ll do for now.

After all that, our program still isn’t ready to host the service. As well as putting configuration entries into the application configuration file, our program needs to make an API call to tell WCF that it wants to host services. (If we were writing a web application, we wouldn’t need to do this—having the configuration in the web.config file would be enough. But for other application types, we need to do this one last step.)

So we need to add a reference to the System.ServiceModel component—that’s the main .NET Framework class library DLL for WCF—and we also need to add using System.ServiceModel; and using ChatServerLibrary; directives to the top of the Program.cs file in our ChatHost project. We can then write our Main method to look like Example 13-4.

Example 13-4. Hosting a WCF service

static void Main(string[] args)
{
    using (ServiceHost host = new ServiceHost(typeof(ChatService)))
    {
        host.Open();

        Console.WriteLine("Service ready");
        Console.ReadKey();
    }
}

This creates a ServiceHost object that will make the ChatService available. WCF will load the configuration from our App.config file to work out how to host it. And we need to make sure our program hangs around—the service will be available only for as long as the program that hosts it. So we leave the program running until a key is pressed.

If you want to try this out, you’ll need to make sure the host console application is the program Visual Studio runs by default—right now it won’t be because the ChatServerLibrary is still set as the default. You’ll need to right-click on ChatHost in the Solution Explorer and select Set as Startup Project. Now pressing F5 will run the program, and a console window will appear showing the message “Service ready” once the ServiceHost is ready.

Warning

If you didn’t delete the App.config file in the ChatServerLibrary project earlier, you’ll now get an error. Even when you set ChatHost as the startup application, Visual Studio will still attempt to launch the WCF Service Host for the ChatServerLibrary project. That would be useful in a solution that has just a WCF client and a service DLL. It’s unhelpful here because we end up with two programs trying to host the same server on the same URL—whichever one gets there second will fail.

If you don’t want to delete the App.config in that project, you can disable the WCF Service Host by opening the ChatServerLibrary project’s Properties, going to the WCF Options tab, and unchecking the relevant checkbox.

Now what? We no longer have the WCF Test Client, because Visual Studio thinks we’re running a normal console application. Since the default wsHttpBinding for our service endpoint uses HTTP we could try pointing a web browser at it. Remember, the service is running on the address in the configuration file:

http://localhost:8732/Design_Time_Addresses/ChatServerLibrary/ChatService/

Strictly speaking, the service isn’t really designed to support a web browser. This chapter is all about enabling programs to communicate with one another, not how to build web user interfaces. However, WCF is rather generous here—it notices when we connect with a web browser, and decides to be helpful. It generates a web page that patiently explains that the thing we’ve connected to is a service, and shows how to write code that could talk to the service. And that’s exactly what we’re going to do next.

Writing a WCF Client

We need to create a client program to talk to our service. Again, to keep things simple we’ll make it a console application. We’ll add this to the same solution, calling the project ChatClient. (Obviously, you’ll need to stop the ChatHost program first if you’re trying this out and it’s still running in the debugger.)

When you right-click on a project’s References item in Visual Studio’s Solution Explorer, you’re offered an Add Service Reference menu item as well as the normal Add Reference entry. We’re going to use that to connect our client to our server via WCF.

The Add Service Reference dialog offers a Discover button (shown in Figure 13-6) which attempts to locate services in your current solution. Disappointingly, if we were to click it with our code as it is now, it would report that it didn’t find any services. That’s because we wrote all the hosting code by hand for ChatHost—Visual Studio doesn’t realize that our console application is hosting services. It usually looks only in web projects—if we’d hosted the service in an ASP.NET web application, it would have found it. But with the approach we’re taking here, it needs a little help.

Note

If you left the App.config file in place in the ChatServerLibrary project, it would find that and would launch the WCF Service Host for you when you click Discover. But be careful—ChatHost is our real service, and when we start modifying settings in its App.config (which we’ll do later) it’s important that the Add Service Reference dialog is talking to the right service. That’s why we suggested deleting the App.config from the DLL project earlier—it avoids any possibility of accidentally configuring your client for the wrong service host.

For Visual Studio to be able to connect to our console-hosted service we need the service to be up and running before the Add Service Reference dialog is open. The easiest way to do this is to run the project, without debugging it. Instead of pressing F5, we choose DebugStart Without Debugging, or we press Ctrl-F5. This runs the ChatHost program without debugging, leaving Visual Studio free for other tasks, such as adding a service reference.

We’ll need the address of the service handy, and since it’s quite long, it’s easiest to open our host’s App.config and copy the service address to the clipboard. (It’s the baseAddress attribute in the <host> section.) Then we can go to the ChatClient project and add a Service Reference. If we paste the address of the service into the Address box and then click the Go button, after a few seconds we’ll see the Services panel on the left display a ChatService entry. Expanding this shows an IChatService item representing the contract, and selecting this shows the one operation available in our contract, PostNote, as Figure 13-6 shows.

Add Service Reference

Figure 13-6. Add Service Reference

While the list of services, contracts, and operations in the Add Service Reference dialog is useful for verifying that we have the service we wanted, the significance of the information here goes a little deeper—it’s part of an important feature of how systems communicate in WCF. Remember that we defined a contract earlier, to describe the operations our service provides to its clients. For the client to communicate successfully with the server, it also needs a copy of that contract. So the best way to think of the Add Service Reference dialog is that it’s a tool for getting hold of the contract from a service.

This is the purpose of the metadata exchange entry we saw earlier when we looked at the configuration Visual Studio generated for our WCF service. Metadata exchange is just a fancy way of saying that a service provides a way for a client to discover the contract and related information about the service. The Add Service Reference dialog uses this information to configure a client application to communicate with the service, and to provide it with a copy of the contract.

To see the results of this, we’ll finish with this dialog. In the Namespace text box near the bottom, we’ll type ChatService—Visual Studio will put the contract and any other types relating to this service into this namespace. When we click OK a Service References item appears in the project in the Solution Explorer, and it will contain an entry called ChatService. (Now that we’ve done this, we can stop the service host console window we ran earlier.)

Visual Studio generates some code when adding a service reference. By default, it hides this, but we can take a look at it. At the top of the Solution Explorer, there’s a toolbar, and if you hover your mouse pointer over the buttons you’ll find that one has a tool tip of Show All Files. This button toggles each time you click it. When it’s pressed in, the ChatService service reference can be expanded, as Figure 13-7 shows.

Generated files in a service reference

Figure 13-7. Generated files in a service reference

The most interesting file in here is Reference.cs, inside the Reference.svcmap item. Inside this file, near the top, there’s a copy of IChatService—the contract we wrote earlier:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel",
                                                "4.0.0.0")]
[System.ServiceModel.ServiceContractAttribute(
       ConfigurationName="ChatService.IChatService"]
public interface IChatService
{

    [System.ServiceModel.OperationContractAttribute(
         Action="http://tempuri.org/IChatService/PostNote",
         ReplyAction="http://tempuri.org/IChatService/PostNoteResponse")]
    void PostNote(string from, string note);
}

It looks a little more complex than the original, because Visual Studio has annotated it with various attributes, but it’s simply being explicit about the values that WCF fills in by default.[27] Aside from these extra details, you can see that it is essentially a copy of the original contract.

Sharing contracts

You might wonder why we jumped through all these hoops rather than just copying IChatService from the service project to the client. In fact, that would have worked, and we could even have written a separate DLL project to define the contract interface and shared that DLL across the two projects. As you’ll see shortly, Visual Studio generated a few other useful things for us as part of this Add Service Reference process, but as it happens, sharing the contract definition directly is sometimes a perfectly reasonable thing to do—you’re not obliged to use metadata exchange.

Of course, you won’t always own the code at both ends. If you need to connect to a service on the Internet provided by someone else, metadata exchange becomes more important—it provides a way to get hold of a contract you didn’t write. And since the metadata exchange mechanisms are standards-based, this can work even when the service is not written in .NET.

Warning

Metadata exchange is not universally supported. In practice, contract discovery can happen in all sorts of ways, including (and we’re not making this up) being faxed a printout showing samples of the messages the service expects to send and receive.[28] If you’re getting the contract through that kind of informal channel, you’ll need to write an interface (by hand) in your client program to represent the service contract.

The process of metadata import also highlights an important point about service evolution. You might modify the ChatService after the ChatClient has added its reference. If these modifications involve changing the contract, it’s clear that there’s a problem: the client’s copy of the contract is out of date. You might think that sharing the interface directly through a common DLL would be a good way to avoid this problem, but it might only make the problem harder to see: what if you’ve already deployed a version of the client? If you then modify the contract the modified code might run fine on your machine, but if you deploy an update to the service with this changed contract any copies of the old client out there will now be in trouble because they’re still working with an old copy of the contract. Explicitly going through the metadata exchange doesn’t make this problem any easier to solve, of course, but it makes it less likely for changes to creep in by accident and go undetected. A complete solution to the problem of service evolution is beyond the scope of this book, so for now, just be aware that changing a contract should not be undertaken lightly.

Note

Michele Leroux Bustamante’s Learning WCF (O’Reilly) discusses versioning of service contracts.

Proxy

Looking further through the Reference.cs file generated by adding the service reference, the next most interesting feature after the contract is a class called ChatServiceClient. This implements IChatService, because it acts as a proxy for the service. If we want to communicate with the service, all we need to do is create an instance of this proxy and invoke the method representing the operation we’d like to perform. So if we add a using ChatClient.ChatService; directive to the top of Program.cs in ChatClient, we can then modify its Main method as shown in Example 13-5.

Example 13-5. Invoking a web service with a WCF proxy

static void Main(string[] args)
{
    using (ChatServiceClient chatProxy = new ChatServiceClient())
    {

        chatProxy.PostNote("Ian", "Hello again, world");
    }
}

Notice the using statement—it’s important to ensure that you dispose of WCF proxies when you have finished using them. When the client calls this method on the proxy, WCF builds a message containing the inputs, and it sends that to the service. Over in the service (which is running in a separate process, perhaps on a different machine) WCF will receive that message, unpack the inputs, and pass them to the PostNote method in the ChatService class.

To try this out, we’re going to need to run both the client and the server simultaneously. This means configuring the solution in Visual Studio a little differently. If you right-click on the WcfChat solution in the Solution Explorer and select Set Startup Projects, the dialog that opens offers three radio buttons. If you select the Multiple Startup Projects radio button, you can choose which of your projects you’d like to run when debugging. In this case, we want to change the Action for both the ChatClient and ChatHost projects from None to Start. (We leave the ChatServerLibrary Action as None—we don’t need to run that project, because our ChatHost project hosts the server library.) Also, we want to give the service a head start so that it’s running before the client tries to use it, so select ChatHost and click the up arrow next to the list, to tell Visual Studio to run it first. (In theory, this is not a reliable technique, because there’s no guarantee that the server will get enough of a head start. In practice, it appears to work well enough for this sort of debugging exercise.) Figure 13-8 shows how these settings should look.

Starting multiple projects simultaneously

Figure 13-8. Starting multiple projects simultaneously

If we run the program by pressing F5, two console windows will open, one for the client and one for the service.

Note

If you’re following along, it’s possible that you’ll see an AddressAlreadyInUseException with an error message complaining that “Another application has already registered this URL with HTTP.SYS.” This usually means you have a copy of ChatHost still running—somewhere on your desktop you’ll find a console window running the service host. Or possibly, the WCF Service Host is still running. This error occurs when you launch a second copy of the service because it tries to listen on the same address as the first, and only one program can receive requests on a particular URL at any one time.

Visual Studio displays the message in its Output window because of the call to Debug.WriteLine in PostNote, just like it did when using the WCF Test Client earlier, verifying that the proxy was able to invoke an operation on the service. (You might need to look carefully to see this—the message can get buried among the various other notifications that appear in the Output window.)

Notice that in Example 13-5 we didn’t need to tell the proxy what address to use. That’s because the Add Service Reference dialog imported more than just the contract definition. It adds information to the ChatClient project’s App.config file, shown in all its gory detail in Example 13-6.

Example 13-6. Generated client-side App.config

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IChatService"
                    closeTimeout="00:01:00" openTimeout="00:01:00"
                    receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    bypassProxyOnLocal="false" transactionFlow="false"
                    hostNameComparisonMode="StrongWildcard"
                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8"
                    useDefaultWebProxy="true"
                    allowCookies="false">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192"
                        maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true"
                        inactivityTimeout="00:10:00" enabled="false" />
                    <security mode="Message">
                        <transport clientCredentialType="Windows"
                            proxyCredentialType="None" realm="" />
                        <message clientCredentialType="Windows"
                            negotiateServiceCredential="true"
                            algorithmSuite="Default" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:8732/Design_Time_Addresses/
                                  ChatServerLibrary/ChatService/"
                binding="wsHttpBinding"
                bindingConfiguration="WSHttpBinding_IChatService"
                contract="ChatService.IChatService"
                name="WSHttpBinding_IChatService">
                <identity>
                    <userPrincipalName value="[email protected]" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

Like the service configuration we examined earlier, this also has an <endpoint> element with an address, binding, and contract, although being on the client side, this <endpoint> appears inside a <client> element instead of a <service> element. The proxy gets the address from this endpoint definition.

Note

You can provide the proxy with an address from code if you want to. It offers various constructor overloads, some of which accept a URL. But if you don’t provide one, it will look in the configuration file.

Notice that the endpoint also has a bindingConfiguration attribute—this refers to a <binding> element earlier in the file that contains information on exactly how the wsHttpBinding should be configured. There was nothing like this in the service, because we were just using the defaults. But the Add Service Reference dialog always generates a binding configuration entry, even if you happen to be using the defaults.

Our “chat” application is demonstrating the ability for the client to send a note to the server, but it’s not complete yet. The client needs a couple of extra features. To make our conversation a bit less one-sided, we should be able to see notes written by other people. And unless our conversations are all going to be exceptionally brief, we need to be able to type in more than just one note.

We’ll fix that second problem by modifying the code in Example 13-5. We’ll put the call to the proxy inside a loop, and we’ll also ask for the user’s name, so we can support notes from people who may not be called Ian (see Example 13-7).

Example 13-7. Client with input loop

static void Main(string[] args)
{
    ChatServiceClient chatProxy = new ChatServiceClient();

    Console.WriteLine("Please enter your name:");
    string name = Console.ReadLine();
    while (true)
    {
        Console.WriteLine("Type a note (or hit enter to quit):");
        string note = Console.ReadLine();
        if (string.IsNullOrEmpty(note))
        {
            break;
        }
        chatProxy.PostNote(name, note);
    }
}

We’ll also modify the server so that it prints out the note, rather than sending it to the debug output—that’ll make it a bit easier to see when notes are coming in. So change PostNote in ChatService to this:

public void PostNote(string from, string note)
{
    Console.WriteLine("{0}: {1}", from, note);
}

If you run both programs again by pressing F5, the client program will ask you to type in your name, and will then let you type in as many notes as you like. Each new note will be sent to the server, and you should see the notes appear in the server console window.

This is an improvement, but there’s still no way for the client to find out when other users have typed notes. For this, we’ll need to add bidirectional communication.

Bidirectional Communication with Duplex Contracts

The contract for our chat service is a one-sided affair—it’s all about the notes the client sends to the server. But WCF supports duplex contracts, which provide a means for the server to call the client back. (Note that there are some issues with HTTP that can make duplex communication tricky—see the sidebar on the next page.) A duplex contract involves two interfaces—as well as an interface that the server implements, we also define an interface that the client must implement if it wants to use the service. In our example, the service wants to notify clients whenever any user posts a note. So the client-side interface, shown in Example 13-8, looks pretty similar to our current server interface.

Example 13-8. Callback interface for duplex contract

public interface IChatClient
{
    [OperationContract]
    void NotePosted(string from, string note);
}

Notice that while methods in a callback interface require the usual OperationContract attribute, the interface itself does not need to be marked with ServiceContract. That’s because this callback interface is not a contract in its own right—it’s one half of a duplex contract. So we need to modify the existing IChatService to associate it with this new callback interface (see Example 13-9).

Example 13-9. Duplex contract

[ServiceContract(
    CallbackContract=typeof(IChatClient),
    SessionMode=SessionMode.Required)]
public interface IChatService
{
    [OperationContract]
    bool Connect(string name);

    [OperationContract]
    void PostNote(string note);

    [OperationContract]
    void Disconnect();
}

By setting the ServiceContract attribute’s CallbackContract property, we’ve declared that this is a duplex contract, and have identified the interface that defines the client side of the contract. Example 13-9 also makes a couple of other changes that turn out to be necessary for our service to work as intended: we’ve set the SessionMode property of the ServiceContract attribute, and we’ve added a couple of extra methods to enable clients to connect and disconnect. We’ve also removed the string name argument from PostNote—as you’ll see, this will turn out to be redundant. All of these changes are related to sessions.

Session-based communication

The ServiceContract attribute’s SessionMode property determines the nature of the relationship between the server and any particular client. By default, the relationship is presumed to be transient, not necessarily lasting any longer than a single operation. This reflects the fact that WCF is designed to support web services, and HTTP does not offer any idea of a connection between the client and the server that lasts longer than a single request.

Note

It’s true that HTTP allows a single TCP connection to be reused across multiple requests, but this is just a performance optimization, and nothing is allowed to depend on it. Either the client or the server is free to close the connection at the end of a request, forcing a new one to be established for the next operation, without changing the semantics of the operations. (And even if the client and server both want to keep the connection alive between requests, a proxy in the middle is free to overrule them.) Logically speaking, each HTTP request is completely disassociated from the ones that came before or after.

This connectionless behavior is very useful for scalability and robustness—it means you can load-balance across large numbers of web servers, and it doesn’t greatly matter whether all of a particular client’s requests are handled by the same machine. It’s often possible to take a single machine in a web farm out of service without disrupting any of the clients. However, the absence of connections is sometimes unhelpful—some applications need some sort of session concept. For example, it would be annoying to have to type in your username and password every time you move from one page to another in a website—once you’ve logged in to a website, you want it to remember who you are. Likewise, if our chat application is going to be able to call clients back to notify them that notes have arrived, that implies that the application needs to know which clients are currently connected.

Although HTTP has no standard way to represent a session, various ad hoc systems have been developed to add such a feature. Websites typically use cookies. (Cookies are not part of the HTTP specification, but they are supported by all popular web browsers. Some users disable them, though, so they’re not necessarily universally available.) The web service standards supported by WCF prefer a slightly different solution—it’s similar to how cookies work, but it puts the relevant information in the messages being sent, rather than in the HTTP headers.[29]

Since our contract is now duplex, it requires the ability to maintain a connection between each client and the server. We tell WCF this by setting the SessionMode property to SessionMode.Required. Note that this doesn’t actually switch on sessions; it merely says that anything that wants to communicate using this contract had better do so with sessions enabled. Remember, the contract is separate from implementations that conform to the contract. The effect of this setting is that WCF will produce an error if you try to use this contract without enabling sessions; we’ll see how to enable sessions by modifying the client and server configuration files once we’ve finished modifying the code.

A session will be established the first time a client connects to a service, which presents our application with another problem. WCF won’t send a message until it has something to send, so our chat client will first connect to the service when we send our first note. (Creating an instance of the ChatServiceProxy does not connect—nothing goes over the network until the first time you try to invoke an operation.) But we want clients to be able to receive notes straight away, without being required to post one first. So we need a way for clients to announce their presence to the service without sending a note. That’s why Example 13-9 adds a Connect method. And we’ve also provided a Disconnect method for clients to announce that they are leaving so that the chat server doesn’t attempt to send notes to clients that are no longer there. (Without this, the server would get an exception the next time it tried to send a message. Although it would notice that the clients had gone, an explicit disconnect is a bit neater—it also makes it possible to tell the difference between users who deliberately leave the conversation and users who get cut off due to problems.)

We now need to update the server to implement the modified contract, and to track the clients.

Calling the client from the server

Our service is going to need to maintain a list of connected clients so that it can notify every client when it receives each note. We can store the list as private data in our service class, but since that one list needs to be available across all sessions, we need to tell WCF that we only ever want it to create one instance of that class.

WCF offers several different modes for creating instances of your service class. It can create one per client session—that’s useful when you want per-session state. But in our case, all notes get sent to everyone, so the only interesting state is global. Since our application state is global, we don’t have much use for per-client instances here. WCF can also create a new instance of your service class for every single request—if you don’t hold any state in the service class itself this is a reasonable thing to do. But in our case, we want one instance for the lifetime of the service. We can indicate this like so:

[ServiceBehavior(
    InstanceContextMode=InstanceContextMode.Single,
    ConcurrencyMode=ConcurrencyMode.Reentrant)]
public class ChatService : IChatService
{

We added a ServiceBehavior attribute to the code to specify this single-instance behavior. Notice that we also asked for a ConcurrencyMode of Reentrant. This tells WCF to have our service work on requests for only one session at a time—if requests from multiple clients come in simultaneously, WCF will service them one after another. This is convenient as it means that as long as any single client does only one thing at a time, we don’t need to write any code to ensure the thread safety of our state handling.

Note

An alternative to the single-instance context mode would have been to store our state in a static field. This would share the data across all clients, which is what we need. But then we’d be on our own for thread safety. The ConcurrencyMode property applies only to any particular instance of the service, so if you don’t choose the single-instance mode, WCF will let different instances of your service execute simultaneously.

In practice, real applications are likely to need to do their own thread synchronization. Here we’re relying on clients making only one call at a time, which might work in a small, controlled example but is a risky thing to do if you don’t completely trust your client machines. (Even with only one session at a time, a single client session could invoke multiple operations simultaneously.) You may be wondering why we didn’t use ConcurrencyMode.Single, which enforces a completely strict one-at-a-time model. Unfortunately, that turns out to prevent you from calling back into clients while you’re in the middle of handling a call from a client—a blocking outbound call from a nonreentrant single-threaded context presents an opportunity for deadlocks, so WCF forbids it.

Next, we’ll add a field to hold the state—a collection of currently connected clients:

private Dictionary<IChatClient, string> clientsAndNames =
     new Dictionary<IChatClient, string>();

This is a dictionary where the key type is the client callback interface we defined earlier. The value is the client’s name. To see how this gets used, here’s the Connect implementation:

public bool Connect(string name)
{
    if (clientsAndNames.ContainsValue(name))
    {
        // Name already in use, so refuse connection
        return false;
    }

    IChatClient clientCallback =
        OperationContext.Current.GetCallbackChannel<IChatClient>();

    // clientsAndNames is shared state, but we're not locking
    // here, because we're relying on ConcurrentMode.Reentrant
    // to give us messages one at a time.
    clientsAndNames.Add(clientCallback, name);
    Console.WriteLine(name + " connected");

    return true;
}

The first thing we do is check that the username is unique. Now that we’re maintaining a list of connected clients, we’re in a position to prevent multiple users from picking the same name. If a new user is trying to sign up with a duplicate name, we return false. (A return code here makes more sense than an exception because this isn’t really an exceptional condition.)

If the name looks OK, we retrieve the client callback interface with the following expression:

OperationContext.Current.GetCallbackChannel<IChatClient>()

OperationContext is a WCF class whose Current property provides information about the operation that your code is handling right now. One of the services it provides is the ability to retrieve the callback interface when a duplex contract is in use. GetCallbackChannel returns a proxy object similar to the one the client uses to talk to the service, but this proxy goes in the other direction—it invokes operations on the client that called our Connect method. We just add this to the dictionary of connected clients, associating it with the client’s chosen name, and then return true to indicate that we’re happy that the user’s name wasn’t previously in use and that we have accepted the user’s connection.

Next, let’s look at the modified PostNote:

public void PostNote(string note)
{
    IChatClient clientCallback =
        OperationContext.Current.GetCallbackChannel<IChatClient>();
    string name = clientsAndNames[clientCallback];

    Console.WriteLine("{0}: {1}", name, note);

    // ToArray() makes copy of the collection. This avoids an
    // exception due to the collection being modified if we have
    // to disconnect a client part way through the loop.
    KeyValuePair<IChatClient, string>[] copiedNames =
        clientsAndNames.ToArray();
    foreach (KeyValuePair<IChatClient, string> client in copiedNames)
    {
        // Avoid sending the message back to the client that just sent
        // it - they already know what they just typed.
        if (client.Key != clientCallback)
        {
            Console.WriteLine("Sending note to {0}", client.Value);
            try
            {
                client.Key.NotePosted(name, note);
            }
            catch (Exception x)
            {
                Console.WriteLine("Error: {0}", x);
                DisconnectClient(client.Key);
            }
        }
    }
}

Again, we begin by retrieving the callback interface for the current client. Remember, our chat server will usually have multiple clients attached, and this lets us discover which particular one is sending a note. The next line looks up the callback interface in the dictionary to find out what name this user originally passed to Connect—this is why we were able to remove the argument we previously had on this method in which the caller passed her name. We remember her name from before—we have to remember it to guarantee uniqueness—and since we’re remembering it, there’s no need to make the client pass in the name every single time.

This code then iterates through all the connected clients in the clientsAndNames dictionary, to deliver the new note to each client. It calls the NotePosted on the proxy. Notice that we wrapped this in exception-handling code. If a client becomes inaccessible because of a network failure, a crash, a machine failure, or a programming error that caused it to exit without remembering to call Disconnect, the proxy’s NotePosted method will throw an exception. Our code catches this and removes the client from the list, to avoid trying to send it any more notes.

Note

This code is a little simplistic, for two reasons. First, we might want to be a little more lenient with errors—perhaps we should give the client a chance to recover before giving up on it entirely. One way to do this would be to have a second collection of connections to act as a kind of sin bin—you could give failed clients another chance after a certain amount of time. (Another strategy would be to require that the client attempt to reconnect in the event of a failure, in which case the server’s error handling is just fine as it is.)

Second, calling each client in turn using a loop will perform poorly as the number of clients gets large, or if some clients are on slow connections. This code will be OK for small groups on a private network, but for a larger scale, an asynchronous approach would work better. WCF provides full support for asynchronous use of proxies, but the chapter on threading and asynchronous programming is coming later, so we can’t show you that just yet.

The code to disconnect clients is in a separate method, because it’s shared by the error-handling code and the Disconnect method that’s part of the new contract. Here’s the common code:

private void DisconnectClient(IChatClient clientCallback)
{
    string name = clientsAndNames[clientCallback];
    Console.WriteLine(name + " disconnected");
    clientsAndNames.Remove(clientCallback);
}

This just removes the client from the dictionary. This makes the Disconnect method very simple:

public void Disconnect()
{
    IChatClient clientCallback =
        OperationContext.Current.GetCallbackChannel<IChatClient>();
    DisconnectClient(clientCallback);
}

Once again, we get hold of the callback interface, and then call the same disconnection helper as the error-handling code.

We have one more modification to make on the server: the wsHttpBinding we’re using doesn’t support the duplex behavior we require, so we need to modify the ChatHost program’s configuration.

Server configuration for duplex and sessions

As we mentioned earlier, WCF lets us change the communication mechanism we’re using by configuring a different binding. We don’t need to change any code to do this. We just need to modify our host project’s App.config file, specifically the <endpoint> tag:

<endpoint address=""
          binding="wsHttpBinding"
          contract="ChatServerLibrary.IChatService">
</endpoint>

We change that binding attribute’s value to wsDualHttpBinding. This binding is very similar to wsHttpBinding; it just adds support for callbacks. It also enables sessions automatically. (Sessions are available with wsHttpBinding, but they are off by default, so you’d need to add further configuration to switch them on if you wanted sessions without duplex communication.)

Our server is now ready to work in duplex mode, so next we need to update the client.

Duplex client

We’ve made several changes to the contract: we modified the one existing method, added two new methods, and turned it into a duplex contract. We also changed the binding. Any one of these changes would need the client to be updated, because each has an impact on the work done by the Add Service Reference operation. (All these things change the contract, the configuration, or both.) However, we don’t need to completely redo the work of adding the service reference. If you right-click on an item in a client’s Service References in the Solution Explorer, you’ll see an Update Service Reference item. This modifies the generated source code and application configuration, saving you from having to build it all again from scratch. This refetches the metadata, so the service needs to be running when you do this, just as when adding the reference in the first place.

Once we’ve updated the reference, rebuilding the solution now produces two compiler errors. The call to PostNote fails, because we’re passing in two arguments where the new contract requires only one. And we also see the following error on the line where we construct the ChatServiceClient proxy:

error CS1729: 'ChatClient.ChatService.ChatServiceClient' does not contain
a constructor that takes 0 arguments

Because the service now has a duplex contract, the generated proxy insists that the client implement its half of the contract—we need to provide an implementation of the callback interface and pass that to the proxy. Example 13-10 shows a straightforward implementation of the interface.

Example 13-10. Implementing the client-side callback interface

[CallbackBehavior(ConcurrencyMode=ConcurrencyMode.Reentrant)]
class ChatCallback : IChatServiceCallback
{
    public void NotePosted(string from, string note)
    {
        Console.WriteLine("{0}: {1}", from, note);
    }
}

Note

The callback interface seems to have changed names. We called it IChatClient on the server, but here it’s IChatServiceCallback. This is the normal if slightly surprising behavior when using metadata exchange through Visual Studio’s Add Service Reference feature. It’s nothing to worry about. As far as WCF is concerned, a contract has only one name (IChatService in this case), even when it happens to be split into server-side and client-side pieces. WCF considers the name of the client-side interface to be irrelevant, and doesn’t advertise it through metadata exchange. When you add or update a reference to a service with a duplex contract, Visual Studio just makes up the client-side interface name by appending Callback to the contract name.

Notice the CallbackBehavior attribute—it specifies a ConcurrencyMode just like on the server. Again, we’ve specified Reentrant—this means that this particular callback handler expects to be dealing with just one session at a time, but can cope with being called back by the server while it’s waiting for the server to do something. We need this so that the server can send notifications to the client inside its PostNote implementation.

We need to provide WCF with an instance of this callback implementation, so we modify the code at the start of Main from Example 13-7 that creates the proxy:

ChatCallback callbackObject = new ChatCallback();
InstanceContext clientContext = new InstanceContext(callbackObject);
ChatServiceClient chatProxy = new ChatServiceClient(clientContext);

This wraps the callback object in an InstanceContext—this represents the session, and is essentially the client-side counterpart of the object returned by OperationContext.Current on the server. It provides various utility members for managing the session, but here the only thing we need it for is to pass our callback object to the proxy—the proxy won’t take the callback directly and demands that we wrap it in an instance context.

We have a few more modifications to make. Remember that the client now needs to tell the server that it wants to connect, so we can do that directly after asking for the user’s name:

Console.WriteLine("Please enter your name:");
bool ok = false;
while (!ok)
{
    string name = Console.ReadLine();
    ok = chatProxy.Connect(name);
    if (!ok)
    {
        Console.WriteLine("That name is taken. Please try another.");
    }
}

This checks the return code to see if the name we entered was already in use, and asks for a different name if it was. The end user can go through the relevant legal procedures to change her name, and then try again.

The line that calls PostNote no longer needs to pass our name each time, because the server now remembers our name based on our session:

chatProxy.PostNote(note);

And finally, we should add a line of code at the very end of Main to let the server know we’re going away:

chatProxy.Disconnect();

We’re now ready to test the application. We can run the client and service as before, but we want an extra client or two, to test out this multiuser chat service. Visual Studio doesn’t provide a way to debug two instances of the same application, so we need to run the extra instances manually. We can do this by finding the folder where the compiled program lives. This will be in a subfolder of the project folder—the program will be in a bindebug subfolder. Running a couple of instances of the client we can type in some different names, and we see notes appear in the service’s console window as the users connect:

Service ready
Ian connected
Matthew connected

When we type a note in one of the clients, it appears in all of the client console windows, as well as the server.

Our application’s user interface has a long way to go before it’ll become the new live chat tool of choice, but we have now demonstrated a complete, if rather basic, WCF-based application. We have only scratched the surface of WCF, of course—it’s a large enough technology to warrant a book in its own right. Learning WCF, a book we already mentioned a couple of times, is a good choice if you’d like to learn more about what WCF can do. Next, we’re going to look at how to work directly with HTTP.

HTTP

The .NET Framework class library provides various classes for working directly with HTTP. Some of these are for client scenarios, and are useful when you need to fetch resources from a web server such as bitmaps, or if you need to use an HTTP-based service that WCF cannot easily work with. You can also provide server-side HTTP support. You would normally do that by writing an ASP.NET web application, which we’ll look at in a later chapter. But there is a class that enables other program types to receive incoming HTTP requests, called HttpListener. (We won’t be covering that, and we mention it mainly for completeness—it’s more normal to use ASP.NET, to which we have devoted a whole chapter.)

WebClient

The most common starting point for client-side HTTP code is the WebClient class in the System.Net namespace. It offers a few ways of working with HTTP, starting from very simple but inflexible methods, through to relatively complex mechanisms that give you complete control over detailed aspects of HTTP. We’ll start with the simplest ones.

Note

Although the examples in this section are HTTP-based, WebClient supports other protocols, including https:, ftp:, and file: URLs. It is extensible, so in principle you can adapt it to support any protocol that has a URL scheme.

Downloading resources

Example 13-11 illustrates one of the simplest ways of using the WebClient class. We construct an instance, and then use its DownloadString method to fetch data at a particular URL. (You can specify the URL as either a string or a Uri object.)

Example 13-11. Fetching content with WebClient

WebClient client = new WebClient();
string pageContent = client.DownloadString("http://oreilly.com/");

Console.WriteLine(pageContent);

Of course, DownloadString succeeds only if the URL you’re fetching happens to contain textual content. The URL in Example 13-11 is an HTML web page, which is a text-based format, so it works just fine, but what if you’re fetching a bitmap, or a ZIP? In that case, there’s DownloadData, which works in the same way, except it returns an array of bytes instead of a string:

byte[] data =
   client.DownloadData("http://oreilly.com/images/oreilly/oreilly_large.gif");

There’s a third easy method for fetching data, DownloadFile. This downloads the resource into a local file:

client.DownloadFile("http://oreilly.com/", @"c:	emporeilly.html");

These three methods will block—they don’t return until they have finished fetching the data you asked for (or they have tried and failed, in which case they’ll throw some kind of exception). This could take awhile. You might be on a slow network, or talking to a busy server, or just downloading a particularly large resource. If you’re building a GUI, it’s a bad idea to call blocking APIs.[30] Fortunately, WebClient offers asynchronous versions of all these methods. You use these by attaching an event handler to the relevant completion event, for example:

client.DownloadFileCompleted += OnDownloadComplete;
client.DownloadFileAsync(new Uri ("http://oreilly.com/"), @"c:	emp");

...

static void OnDownloadComplete(object sender, AsyncCompletedEventArgs e)
{
    MessageBox.Show("Download complete");
}

The DownloadXxxAsync methods all return straight away. WebClient raises the relevant DownloadXxxCompleted event once the data has been fetched. (This means that you’ll need to ensure that your application hangs around long enough for that to happen; if you were to use these asynchronous techniques in a console application, you’d need to take steps to make sure the program doesn’t exit before the work completes.) Of course, DownloadStringAsync and DownloadDataAsync cannot provide the fetched data as a return value, unlike their blocking counterparts, so they provide it as the Result argument of their completion event argument.

Note

If you’re writing a Silverlight client, you’ll find that WebClient offers only the asynchronous versions. And in general, that’s true of all of Silverlight’s networking support—since Silverlight is designed just for building user interfaces, it doesn’t even offer you the blocking forms.

As well as providing completion event notifications, WebClient also offers progress notifications through its DownloadProgressChanged event. This is raised from time to time during asynchronous downloads, regardless of which of the three methods you used. It provides two properties, BytesReceived and TotalBytesToReceive, which tell you how far the download has gotten and how far it has to go.

Note

If you use these asynchronous methods in a GUI built with either WPF or Windows Forms, you don’t need to worry about threading issues. As you’ll see in later chapters, that is not true for all asynchronous APIs, but these automatically take care of UI threading for you—as long as you start asynchronous operations from the UI thread, WebClient will raise completion and progress events on the UI thread.

Uploading resources

WebClient offers the UploadString, UploadData, and UploadFile methods. These correspond directly to the DownloadString, DownloadData, and DownloadFile methods, but instead of fetching data with an HTTP GET, they send data to the server, typically using an HTTP POST, although overloads are available that let you specify other verbs, such as PUT.

Stream-based uploads and downloads

Lots of APIs in the .NET Framework work with the Stream abstraction defined in the System.IO namespace. The XML classes can load data from a Stream, or write data into one, for example. The bitmap decoding and encoding classes in WPF can also work with streams. The first three lines of Example 13-12 obtain a stream for an Atom feed[31] from a WebClient and use it to initialize an XDocument. The code then uses LINQ to XML to extract the list of titles and links advertised by this particular feed.

Example 13-12. From HTTP to LINQ to XML via a Stream

WebClient client = new WebClient();
Stream feedStm = client.OpenRead("http://feeds.feedburner.com/oreilly/news");
XDocument feedXml = XDocument.Load(feedStm);

string ns = "http://www.w3.org/2005/Atom";
var entries = from entryElement in feedXml.Descendants(XName.Get("entry", ns))
              select new
              {
                  Title = entryElement.Element(XName.Get("title", ns)).Value,
                  Link = entryElement.Element(XName.Get("link", ns)).
                                Attribute("href").Value
              };
foreach (var entry in entries)
{
    Console.WriteLine("{0}: {1}", entry.Title, entry.Link);
}

For sending data there’s an OpenWrite method. With HTTP or HTTPS, this defaults to POST, but as with the Upload methods, you can call an overload that takes the verb as well as the URL.

You can use streams asynchronously. Following the same pattern as the other methods we’ve looked at so far, you’ll find OpenReadAsync and OpenWriteAsync methods, with corresponding completion events. But streams add an extra dimension: the Stream abstract base class also offers both synchronous and asynchronous operation. For example, if you’re reading data, you can call either Read or BeginRead. You are free to use the Stream in either mode, regardless of whether you obtained it from the WebClient synchronously or asynchronously. But bear in mind that if you are trying to avoid blocking in order to keep your user interface responsive, you’ll most likely want to get hold of the stream asynchronously (e.g., use OpenReadAsync) and use the stream asynchronously. When you open a stream asynchronously, the completion notification tells you that the WebClient is ready to start reading (or writing) data, but that’s no guarantee that you’ll be able to finish reading data immediately. For example, if you use OpenReadAsync to fetch a 1 GB file by HTTP, WebClient won’t wait until it has downloaded the whole 1 GB before giving you a stream. You’ll get an OpenReadCompleted event when it has begun to fetch data so that you can start processing it straight away, but if you try to read data from the stream faster than your network connection can download it, you’ll be made to wait. So if you want nonblocking behavior for the whole download, you’ll need to use the Stream asynchronously too.

Warning

While the asynchronous methods offered by WebClient will call you back on the correct thread in a GUI application, the asynchronous stream methods will not, and you’ll have to deal with threading issues yourself.

The WebClient class’s most powerful mechanism is accessed through its GetWebRequest and GetWebResponse methods. But these turn out to be wrappers around another set of classes altogether—WebClient just provides these wrappers as convenient helpers. So we’ll move on to the classes that do the real work for these methods.

WebRequest and WebResponse

WebRequest and WebResponse are abstract base classes for a family of classes that provide the most detailed level of control over web requests. The concrete HttpWebRequest and HttpWebResponse classes add details specific to HTTP, and .NET also offers specialized FtpWebRequest/Response and FileWebRequest/Response classes. This section will mainly focus on the HTTP classes.

The main limitation with the WebClient-based mechanisms we’ve explored so far is that they focus on the content of the request or the response. They don’t provide any way to work with standard HTTP features such as the content type header, the UserAgent string, cache settings, or proxy configuration. But if you use HttpWebRequest and HttpWebResponse, all the detailed aspects of HTTP are available to you.

The cost of this power is additional verbosity. The main difference is that you end up with one object to represent the request and one to represent the response, in addition to streams representing the data being sent or received. Moreover, the only way to access the data with these classes is through streams. To do the same job as Example 13-11—fetching the data from a particular URL into a string—requires the rather more complex code shown in Example 13-13.

Example 13-13. Fetching a string with HttpWebRequest and HttpWebResponse

HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://oreilly.com/");
using (HttpWebResponse resp = (HttpWebResponse) req.GetResponse())
using (Stream respStream = resp.GetResponseStream())
using (StreamReader reader = new StreamReader(respStream))
{
    string pageContent = reader.ReadToEnd();
    Console.WriteLine(pageContent);
}

The two casts on the first two lines of Example 13-13 are a little messy, but are, unfortunately, usually necessary. The WebRequest family of classes is extensible to multiple protocols, so most of the methods are declared as returning the abstract base types, rather than the concrete types—the exact type returned depends on the kind of URL you use. So if you need access to a protocol-specific feature, you end up with a cast. In fact, Example 13-13 isn’t using anything protocol-specific, so we could have avoided the casts by declaring req and resp as WebRequest and WebResponse, respectively. However, the usual reason for using these classes is that you do in fact want access to HTTP-specific information. For example, you might want to simulate a particular web browser by setting the user agent string, as shown in Example 13-14.

Example 13-14. Changing the user agent header with HttpWebRequest

HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://oreilly.com/");
req.UserAgent = "Mozilla/5.0 (iPod; U; CPU iPhone OS 2_2_1 like Mac OS X; en-us)
AppleWebKit/525.18.1 (KHTML, like Gecko) Mobile/5H11a";

... as before

This code has been split across multiple lines, as the user agent string is too wide to fit. This would let you discover what response a website would send if the request came from an Apple iPhone. (Many websites adapt their content for different devices.)

As you’d expect, asynchronous operation is available so that you can avoid blocking the current thread while waiting for network operations to complete. But it looks slightly different from the WebClient mechanisms we’ve seen so far, because of the way in which the methods you call can change when the request gets sent. No network communication happens at the point where you create the request, so there is no asynchronous method for that. Remember, the request object represents all the settings you’d like to use for your HTTP request, so it won’t actually attempt to send anything until you’ve finished setting the request’s properties and tell it you’re ready to proceed.

There are two ways in which you can cause an HttpWebRequest to send the request. Asking for the response object will cause this, but so will asking for a request stream—the request’s GetStream method returns a write-only stream that can be used to supply the body of the request for POST or similar verbs (much like WebClient.OpenWrite). This stream will start sending data over the network as soon as your code writes data into the stream—it doesn’t wait until you close the stream to send the data all in one go. (For all it knows, you might be planning to send gigabytes of data.) This means that by the time it returns the stream, it needs to be ready to start sending data, which means that the initial phases of the HTTP request must be complete—for example, if the request is going to fail for some reason (e.g., the server is down, or the client machine has lost its network connection), there’s no point in attempting to provide the data for the request. So you’ll be notified of failures of this kind when you ask for the stream.

The upshot of all this is that GetStream is a blocking method—it won’t return until the server has been contacted and the request is underway. So there’s an asynchronous version of this. But WebRequest doesn’t support the event-based pattern that WebClient uses. Instead, it uses the more complex but slightly more flexible method-based Asynchronous Programming Model, in which you call BeginGetRequestStream, passing in a delegate to a method that the request will call back once it’s ready to proceed, at which point you call EndGetRequestStream. This Begin/End pattern is very common in .NET and will be discussed in Chapter 16.

The second way in which the sending of the request can be triggered is to ask for the response object—if you haven’t already asked for the request stream (e.g., because you’re doing a GET, so there is no request body) the request will be sent at this point. So GetResponse also has an asynchronous option. Again, this uses the method-based asynchronous pattern. Example 13-15 shows a version of Example 13-13 modified to get the response object asynchronously.

Example 13-15. Obtaining a response asynchronously

HttpWebRequest req = (HttpWebRequest) WebRequest.Create("http://oreilly.com/");
req.BeginGetResponse(delegate(IAsyncResult asyncResult)
{
    using (HttpWebResponse resp = (HttpWebResponse)
                                      req.EndGetResponse(asyncResult))
    using (Stream respStream = resp.GetResponseStream())
    using (StreamReader reader = new StreamReader(respStream))
    {
        string pageContent = reader.ReadToEnd();
        Console.WriteLine(pageContent);
    }
}, null);

This example uses an anonymous method as the completion callback, which allows the code to retain a similar structure to the original, synchronous version. But you need to be mindful that the code that handles the response in Example 13-15 is now a separate method, and could run some considerable length of time after the call to BeginGetResponse returns, and probably on a different thread. So as with the event-based pattern, you’ll need to ensure that your application runs for long enough for the operation to complete—having some outstanding asynchronous operations in progress will not keep your process alive if all of the nonbackground threads exit.

Warning

This asynchronous pattern does not take care of UI threading issues (unlike the event-based pattern seen previously). The completion callback will usually occur on some random thread, and attempting to update the user interface from that code will fail. We’ll see how to handle this in Chapter 16.

Example 13-14 shows just one of the HTTP protocol features you can customize—the UserAgent string. Many similar settings are available, many of which are quite obscure, so we won’t go through all of them here. That’s what the MSDN reference is for. But we will cover the most common cases.

Authentication

HTTP defines various ways for a client to authenticate itself to the server. Note that most public-facing websites don’t actually use any of these—a website that presents a login UI where you type a username and password directly into fields in the web page itself isn’t using HTTP authentication at all, and is usually relying on cookies instead (more on this later). HTTP authentication gets involved in two main scenarios. The most visible scenario is when the browser opens a small window asking for credentials before it navigates to the web page—this is less common than logging in via a form on a web page, but a few websites work this way. Slightly more subtly, HTTP authentication is used for integrated security scenarios—for example, when a client machine belongs to a Windows domain, and the user’s identity is automatically available to an intranet web server on the same domain. In this case, you don’t need to log in explicitly to an intranet site, and yet it knows exactly who you are—this is thanks to implicit use of HTTP authentication.

By default, HttpWebRequest will not attempt to authenticate the client to the server, even in integrated authentication scenarios. (So it has a different default policy than Internet Explorer—IE will automatically authenticate you to servers on your local network with integrated authentication, but HttpWebRequest will not.) If you’re writing client code and you want it to identify the user to the server, you must set the request’s Credentials property.

For integrated authentication, there’s a special credentials object to represent the user’s identity, provided by the CredentialCache class. Example 13-16 shows how to use this to enable integrated authentication. (Obviously, this will only do anything if the server is prepared to use it—so this code merely tells HttpWebRequest that we’re happy to use integrated authentication if the server asks for it. If the server turns out not to require authentication at all, you won’t see an error.)

Example 13-16. Enabling the use of integrated authentication

HttpWebRequest request =
    (HttpWebRequest) WebRequest.Create("http://intraweb/");
request.Credentials = CredentialCache.DefaultCredentials;

...

HTTP authentication isn’t always integrated with Windows security. It also supports username- and password-based authentication. The HTTP specification supports two ways of using this. Basic authentication just sends your username and password as part of the request, so unless you’re using HTTPS, that’s not very secure. The alternative, digest authentication, is better, but seems to be rarely used. In practice, basic authentication over HTTPS seems to be the popular choice. For either kind of authentication, you specify the username and password in the way shown in Example 13-17.

Example 13-17. Providing credentials for basic or digest authentication

HttpWebRequest request =
    (HttpWebRequest) WebRequest.Create("https://intraweb/");
request.Credentials = new NetworkCredential("user1", "p@ssw0rd");

...

This approach doesn’t let you specify whether to use basic or digest authentication because the server gets to choose. Since you therefore don’t know whether the password will be sent in the clear, you should normally provide credentials this way only when using HTTPS. You can force the use of digest authentication by wrapping the NetworkCredential in a CredentialCache object, which lets you specify the authentication schemes you want to support. Even so, you might want to be wary of using digest authentication without HTTPS—although digest authentication can be secure, some servers implement it in an unsecure way.

Working with proxies

By default, web requests will look at the Internet Explorer settings to determine whether a web proxy should be used. But you might not want this default behavior, so there are a couple of ways you can change it.

Note

Prior to .NET 2.0, IE proxy settings weren’t honored, so you may occasionally come across code that goes to some lengths to work out whether it needs to use a proxy. Usually such code is either old or written by someone who didn’t know that .NET 2.0 fixed this issue.

You can add entries to your App.config file to modify the default proxy behavior. Example 13-18 stops web requests using the configured default proxy by default.

Example 13-18. Configuring default proxy behavior

<configuration>
  <system.net>
    <defaultProxy enabled="false" />
  </system.net>
</configuration>

The default behavior, in the absence of any configuration, specifies that the use of the default proxy is enabled, but the application will not use the user’s credentials to identify the user to the proxy server. (Authenticating the user to a proxy happens independently of authenticating the user to the web server.) Some companies require users to authenticate with the proxy in order to access the Internet, in which case you would need to change the configuration, setting the <defaultProxy> element’s useDefaultCredentials attribute to true.

You can also modify the behavior in code. The HttpWebRequest class has a Proxy property, and you can set this to null to disable the use of a proxy. Or you can set it to a WebProxy object specifying a specific proxy and settings, as Example 13-19 shows.

Example 13-19. Setting an explicit proxy

HttpWebRequest request =
    (HttpWebRequest) WebRequest.Create("https://intraweb/");
request.Proxy = new WebProxy("http://corpwebproxy/");

Controlling cache behavior

Windows maintains a per-user cache of web resources, to avoid having to download frequently used bitmaps, CSS, JavaScript, HTML pages, and other content again and again. Internet Explorer uses this cache, but it’s also accessible to .NET code. By default, your programs won’t use the cache, but you can enable caching by setting the request’s CachePolicy, as Example 13-20 shows.

Example 13-20. Setting cache policy

HttpRequestCachePolicy cachePolicy = new HttpRequestCachePolicy(
    HttpRequestCacheLevel.CacheIfAvailable);
HttpWebRequest request =
    (HttpWebRequest) WebRequest.Create("https://intraweb/");
request.CachePolicy = cachePolicy;

The default policy is BypassCache, which means that not only will requests not look in the cache, but any resources you fetch will not be added to the cache. Example 13-20, on the other hand, will use a cached copy of the resource if one is available, and if not, it will add the resource it downloads to the cache (unless headers in the HTTP response indicate that it’s not a cacheable resource).

The HttpRequestCacheLevel enumeration supports various other caching options. If you want to force the resource to be fetched anew, but would like the result to be added to the cache, you can specify Reload. You can also force a check for freshness—HTTP allows clients to tell the server that they have a cached version and that they want to download the resource only if a newer version is available, and you can enable this behavior with Revalidate. (Some more obscure options are also available, for developers who are familiar with the full complexities of HTTP caching and want complete control.)

Using cookies

As far as the HTTP specification is concerned, each request is entirely unconnected with any previous requests from the same client. But it’s often useful for a website to be able to recognize a series of requests as having come from the same client, and so a common mechanism to support this, called cookies, is widely used.[32] Cookies underpin features such as shopping baskets, where a web application needs to maintain per-user state—I expect to see only the things that I’ve put in my basket, and not the items that any other users who are logged in right now have put in theirs. Cookies are also commonly used for managing logins—once the user has typed in his username and password in an HTML form, a cookie is often used, in effect, to authenticate the user from then on.

If you’re using a web browser, cookies just work without needing any intervention (unless you’ve disabled them, of course). But if you’re writing code, you need to take specific steps to use them—by default, .NET will not use cookies at all, and does not have access to the cookie store for Internet Explorer.[33] Nor does it implement a cookie store of its own.

Often, ignoring cookies doesn’t cause any problems. But you may find that you sometimes need to write code that accesses a site that depends on cookies to work, in which case you’ll need to write code on the client side to make that happen.

The basic idea behind cookies is that when a client receives a response from a server, that response may include information that the server would like the client to remember and to pass back the next time that client makes a request. The client isn’t expected to do anything other than pass the information back verbatim—there’s no useful information that the client can extract from the cookie. (Or at least there shouldn’t be, although there are some infamous cases where people got this wrong. For example, one online store made the mistake of putting prices of shopping basket entries into a cookie, enabling devious customers to grant themselves discounts by manually editing their cookies.) The client is just expected to hold onto the cookies it receives. (See Example 13-21.)

Example 13-21. Getting the cookies from a response

CookieContainer container = new CookieContainer();

Uri address = new Uri("http://amazon.com/");
HttpWebRequest req = (HttpWebRequest) WebRequest.Create(address);
HttpWebResponse resp = (HttpWebResponse) req.GetResponse();

CookieCollection cookies = resp.Cookies;
container.Add(address, cookies);

We’re using the CookieContainer class provided by .NET to remember which cookies we’ve seen from the various servers we’ve been talking to, and which addresses they are associated with. When we come to make our next request, we can then supply this container:

Uri address = new Uri("http://oreilly.com/");
HttpWebRequest newReq = (HttpWebRequest) WebRequest.Create(address);
newReq.CookieContainer = container;

Anytime we get a response, the server is allowed to return new cookies, or to modify the value of existing cookies, so you would need to make sure you updated your cookie container anytime you get a response, using the code in Example 13-21.

That’s it for HTTP. Finally, we’ll take a look at sockets.

Sockets

Sockets are the most powerful networking mechanism available in .NET—HTTP is layered on top of sockets, and in most cases WCF is too. Sockets provide more or less direct access to the underlying TCP/IP network services—they effectively let you speak the native language of the network. This can offer some flexibility and performance benefits over HTTP-based communications, but the downside is that you need to do more work. Also, in corporate environments, communication with the outside world with ad hoc use of sockets is often blocked, as firewalls may be configured to let through only the traffic they understand and expect. But in cases where those restrictions do not apply, and if the flexibility or (relatively small) performance benefits are worth the effort, sockets are a useful tool.

The basic idea of a socket has been around for decades, and appears in many operating systems. The central concept is to present network communication through the same abstractions as file I/O. We already saw something like that with WebClient—it can provide Stream support. However, those streams are concerned with the body of an HTTP request or response. With sockets, the streams are at a lower level, encompassing all the data. (If you used a socket-based stream to connect to a web server, you’d see all of the details of the HTTP protocol in the stream, not just the body.)

Besides the file-like abstraction, socket APIs also have a standard set of operations for establishing connections, and for controlling aspects of those connections’ behavior.

To understand sockets, you need some familiarity with the network protocols they depend on, so as well as introducing the API features the next section incorporates a very quick overview of the TCP/IP family of protocols. If you already know TCP/IP, please feel free to skim through the next section and just look at the examples that illustrate usage.

Note

Sockets can be used with some other protocols besides those in the TCP/IP family. For example, you can use sockets for IrDA (Infrared) or Bluetooth communications to communicate with local devices. There are other network protocols too, but the TCP/IP family is the most widely used.

IP, IPv6, and TCP

The Internet uses a family of protocols typically known collectively as TCP/IP. The lowest level is IP, which is short for Internet Protocol. This is the means by which all network traffic flows across the Internet—when you buy an Internet connection, you’re buying the ability to deliver information from your computer to the Internet, and vice versa, via IP.

IP’s main job is the ability to get packets (as individual messages are called in networking) of data between different computer networks (hence internet). For example, data sent by a web server in a data center out of its network port somehow needs to make its way to your home WiFi network. These networks are connected together by routers, whose job is to work out where to send IP packets next; there are well-defined rules for how they should do this, ensuring that data ends up at the machine it’s meant for. This process depends on the IP address—a number that identifies a machine in a way that makes it possible for routers to work out how to route messages to that machine.

If you’re using sockets, you will need to work with IP addresses because they’re how you identify the machine you’d like to communicate with. You can typically just treat them as opaque identifiers, wrapped by the IPAddress class in the System.Net namespace. But there’s one aspect of IP addressing that it’s worth being aware of: the distinction between IPv4 and IPv6 addresses. See the sidebar below.

While the Internet protocol uses numbers to identify machines, users are more familiar with names such as oreilly.com and www.microsoft.com. The Internet has a system called the Domain Name Service (DNS)—your Internet service provider gives you access to this as part of your connection—whose job is to convert these textual names into the IP addresses required to communicate with the machines (or hosts, as the entities associated with IP addresses are conventionally called). Example 13-22 uses the Dns class in the System.Net namespace to look up the IP addresses for a particular hostname. DNS can associate multiple addresses with a name; for example, a DNS name may have both an IPv4 and an IPv6 address. This code loops through all the addresses, printing their type and value. (If you call ToString() on an IPAddress, which is what Console.WriteLine will do in Example 13-22, it’ll return the standard string representation for the numeric address.)

Example 13-22. Getting the IP addresses for a hostname

IPHostEntry hostDnsEntry = Dns.GetHostEntry("localhost");
foreach(IPAddress address in hostDnsEntry.AddressList)
{
    Console.WriteLine("Type: {0}, Address: {1}", address.AddressFamily,
                                                 address);
}

This example looks up the special hostname localhost, which always refers to the local machine on which the program is running. Both IPv4 and IPv6 define special addresses that are reserved to refer to the local machine, so if you run Example 13-22, you’ll see that it prints out two addresses, one for IPv6 and one for IPv4:

Type: InterNetworkV6, Address: ::1
Type: InterNetwork, Address: 127.0.0.1

Note

For years, IPv4 was the only IP version in use, so it’s often not qualified with a version number, which is why this IPv4 address’s AddressFamily property is just displayed as InterNetwork, and not InterNetworkV4.

Many DNS entries don’t have an IPv6 address, and if you modify Example 13-22 to look up such an address (e.g., at the time of this writing, w3.org has only an IPv4 address) you’ll see just one address back from GetHostEntry:

Type: InterNetwork, Address: 128.30.52.45

Armed with an IP address for the machine we want to talk to, we now have enough information for the Internet to deliver IP packets to the target machine. But there are a couple of issues to resolve. First, there’s the question of how the receiving machine will know what to do with the packet when it arrives. Second, there’s the problem that the Internet is fundamentally unreliable. TCP (the Transmission Control Protocol) offers a solution to both of these problems.

The Internet does not guarantee to deliver all IP packets. It can’t. Suppose you are using a machine connected to the Internet with a 100 Mbps connection and you try to send data at full speed to a machine that is connected with a 56 Kb modem. (Remember those? In some parts of the world, they’re still used. If you get a chance, try using a modern website via a 56 Kb dial-up connection, and then marvel at the fact that 56 kbps modems were once considered really fast.) As we send data to this bandwidth-impoverished machine, the routers between us and them will initially try to manage the speed difference—a router connecting a fast network to a slower network will store incoming packets from the fast network in its memory, and they queue up while it plays them out in slow motion to the target network. But eventually it’ll run out of memory, at which point it’ll just start discarding packets.

At busy times of the day, packets may get discarded even if both ends of the connection can operate at the same speed—perhaps the route the traffic needs to take through the Internet between the two networks includes busy links that just don’t have the bandwidth to support all the traffic that all of the ISP’s customers are trying to send. So network congestion can also cause packet loss, even in the absence of speed mismatches.

The upshot of this is that IP is not a reliable protocol—you get what’s sometimes called a best effort service. In attempting to deliver your data, the Internet will give it its best shot, but there are no guarantees. (You may have a service level agreement with your ISP that makes statistical guarantees about the proportion of data it will successfully deliver to and from the boundaries of the ISP’s network infrastructure, but there are no guarantees for any single packet, nor can your ISP guarantee what will happen to your data once it has been passed off to someone else’s network.)

To add to the fun, IP doesn’t even guarantee to deliver messages in the same order you sent them. ISPs might have multiple routes through their network to ensure reliability in the face of individual link failures, or just to ensure enough bandwidth to cope with high loads. So if you send a series of IP packets to the same computer, not all of those packets will necessarily take the same route—they might be split across two or more routes. Some of those routes may prove to be quicker, meaning that the packets can arrive at their destination in a different order than you sent them.

Writing networked applications can be challenging if you have no idea whether any particular message will be received, nor any way of knowing in what order the ones that do arrive will turn up. So to make life easier, we have the Transmission Control Protocol—the TCP in TCP/IP. This is a protocol that sits on top of IP and adds some useful features. It provides support for connections—rather than each packet being handled in isolation, each transmission is part of the sequence of communication occurring over the connection. TCP puts sequence numbers into each IP packet so that it can detect when packets arrived out of order. And finally, the receiving machine acknowledges receipt of each message. Clients use this to work out how fast the messages are getting through, which enables them to send data at a rate that matches the network’s ability to deliver, avoiding problems with mismatched network speeds and network congestion. And clients also use this to work out when data didn’t get through and needs to be resent.

These features enable TCP to offer a data transmission service that sends data in order, at a rate that will not try to exceed the capacity of the network routes available and in a fashion that is reliable in the face of occasional packet loss. A socket is usually just an API on top of a TCP connection that presents a stream-style API—your program can write data into a socket stream, and the TCP/IP networking code running on the computers at both ends uses TCP to ensure that the program at the receiving end has another socket stream from which it can read the exact same sequence of bytes you wrote into the stream. The programs don’t need to know about out-of-order delivery or packet loss. As long as the networks are not hopelessly lossy, it looks like there is perfectly reliable in-order transmission. TCP sockets are symmetrical, in that both ends can send and receive data. And the directions are independent—communication can be full duplex, so there’s no need for the two ends to take it in turns.

TCP also solves the problem of how the receiving computer knows what it’s supposed to do with incoming data. A single computer may offer many network services—a small company might run the intranet web server, file server, and email server on the same computer, for example. So TCP adds the concept of port numbers. A service on a target machine will be associated with a particular number. There’s a central administrative body called IANA—the Internet Assigned Numbers Authority—which (among other things) assigns and publishes port numbers for common services. For example, IANA has designated port 80 as the TCP port on which HTTP servers usually accept incoming requests. When a web browser (or the WebClient class we saw earlier) fetches a resource via HTTP, it does so by opening a TCP connection to port 80.

Note

A single client computer might open several simultaneous connections to the same service—web browsers often do this in order to download the various pictures, CSS, and JavaScript files concurrently, so as to be able to display the web page sooner. To distinguish between them, each connection has a client-side port number as well as a server-side port. But while you need to know the server port in order to connect, the client port number is usually picked for you dynamically by the OS.

Let’s look at a real example. We’re going to connect to a service using a very old and very simple protocol called Daytime Protocol. This hasn’t changed since its specification was published in 1983—you can find its definition in a document called RFC867 at http://www.faqs.org/rfcs/rfc867.html. It’s remarkably simple: clients open a TCP connection to port 13 on a server that offers the daytime service, and the server will send back the time of day as text and then close the connection. The specification is pretty vague about the format—it says this:

There is no specific syntax for the daytime.  It is recommended that
   it be limited to the ASCII printing characters, space, carriage
   return, and line feed.  The daytime should be just one line.

It then goes on to give examples of a couple of popular formats, but servers are free to do pretty much anything they like.

This is a service that cannot be accessed with the WebClient or any of the WebRequest family of classes—those types expect data to be layered inside HTTP (or sometimes another higher-level protocol such as FTP), but Daytime Protocol just makes very basic, direct use of plain TCP. So we need to use sockets if we want to access such a service.

The U.S. government’s National Institute of Standards and Technology (NIST) lists a few servers that offer this daytime service. Once such machine, located in Redmond, Washington, has the DNS name of time-nw.nist.gov. We’ll use that. To start with, we need to look up its IP address, which we’ll do using a similar technique to Example 13-22:

IPHostEntry hostDnsEntry = Dns.GetHostEntry("time-nw.nist.gov");
IPAddress serverIp = hostDnsEntry.AddressList[0];

Next, we need to open a TCP connection to port 13 (the daytime service port) on that machine. To do this, we’ll need a Socket object.

Connecting to Services with the Socket Class

The System.Net.Sockets namespace defines the Socket class, which makes the socket features of the underlying operating system available from .NET. We use a Socket when we want to open a TCP connection to a remote service:

Socket daytimeSocket = new Socket(
    serverIp.AddressFamily,
    SocketType.Stream,
    ProtocolType.Tcp);

Note

Socket implements IDisposable, so you will need to call Dispose at some point. And while we would normally write a using statement to handle that, that’s somewhat unusual with sockets, because they often have a longer lifetime than any particular method. There isn’t one right way to handle this, because the right moment to dispose a socket will depend on the way in which your application uses the socket. The next few examples therefore don’t show disposal, because we are illustrating aspects of the API that will be the same no matter how you are using sockets. But be aware that you will always need to find a suitable place to call Dispose.

The Socket constructor needs three pieces of information. It needs to know the address family we will use to identify the server (e.g., IPv4 or IPv6). It also needs to know what style of communication we’re expecting—we’re asking for stream-like communication. (Some protocols support some other styles of communication, but with TCP you always specify Stream here.) Finally, we specify the specific protocol we’d like to use—TCP in this case.

Note

If this constructor seems more complex than necessary, it’s because sockets aren’t just for TCP/IP. The underlying Windows socket API (WinSock) was introduced before TCP/IP had become the dominant protocol, so it supports numerous protocols. Windows even supports custom providers that add socket support for new protocols.

Note that we don’t specify where we’re connecting to yet. That information doesn’t go in the constructor because not all sockets work the same way—some protocols support transmission patterns other than simple point-to-point connections. So the Socket class requires that we first say what sort of socket we want before going on to say what we’re trying to communicate with. We supply that information when we connect to the service:

daytimeSocket.Connect(serverIp, 13);

Remember, port 13 is the port number allocated by IANA for the daytime service. We’re going to retrieve the time of day as text from this service, so we declare a variable to hold the result:

string data;

Sockets represent all data as bytes. (Or more precisely, octets, which are 8-bit bytes. Back in the old days, some computers used other byte sizes, and you occasionally come across evidence of this—for example, some parts of the Internet email system guarantee to transfer 8-bit bytes, and may truncate your data to seven bits per byte.) The Daytime Protocol specification says that the service will return text using the ASCII encoding, so we need something that can convert a stream of bytes containing ASCII into a .NET string. Example 13-23 does this.

Example 13-23. Retrieving ASCII data from a TCP socket

using (Stream timeServiceStream = new NetworkStream(daytimeSocket, true))
using (StreamReader timeServiceReader = new StreamReader(timeServiceStream,
                                                         Encoding.ASCII))
{
    data = timeServiceReader.ReadToEnd();
}

A few things are going on here. First, we constructed a NetworkStream—this class derives from Stream, and it’s how .NET lets us treat a socket-based connection in the same way as any other Stream. In general, the use of streams is optional because the Socket class provides methods that let you read and write data directly. But in this example, getting an actual Stream object is useful because we can plug it into a StreamReader. StreamReader takes a stream that contains text and can convert the bytes in that stream into string objects. Example 13-23 uses the StreamReader class’s ReadToEnd method—this asks to read all of the data in the stream to the very end and to return it as a single string.

Notice that the first line of Example 13-23 passes true as a second argument to the NetworkStream constructor. This tells the NetworkStream that we’d like it to take ownership of the Socket object—once we are done with the NetworkStream and call Dispose on it, it will shut down the Socket object for us. That’ll happen at the end of the block for the using statement here. This is important: we must close connections when we have finished with them, because otherwise, we could end up hogging resources on the server unnecessarily.

Having fetched the data and closed the socket, we finally print out the data:

Console.WriteLine(data);

Example 13-24 shows the whole example.

Example 13-24. Using a Socket to fetch data from a daytime server

IPHostEntry hostDnsEntry = Dns.GetHostEntry("time-nw.nist.gov");
IPAddress serverIp = hostDnsEntry.AddressList[0];

Socket daytimeSocket = new Socket(
    serverIp.AddressFamily,
    SocketType.Stream,
    ProtocolType.Tcp);

daytimeSocket.Connect(serverIp, 13);
string data;
using (Stream timeServiceStream = new NetworkStream(daytimeSocket, true))
using (StreamReader timeServiceReader = new StreamReader(timeServiceStream))
{
    data = timeServiceReader.ReadToEnd();
}
Console.WriteLine(data);

If you run the program, you’ll see something like this:

55059 09-08-16 06:29:42 50 0 0 912.5 UTC(NIST) *

It’s not strictly relevant to the use of sockets, but if you’re interested, here’s what the numbers this particular server returns all mean. The first number is the number of days that have elapsed since midnight on November 17, 1858. (If you’re curious to know why anyone might find that useful, search the Web for “Modified Julian Date”.) The set of three numbers that follows are the year, month, and date (2009, August 16 in this example), followed by the time of day as UTC (time zone zero, or as we British authors like to call it, Greenwich Mean Time). The 50 signifies that daylight saving time is in effect where the server is located, and the following two zeros indicate respectively that no leap second will be added this month and that the server believes it is not currently experiencing any problems. The next number indicates that the server is deliberately advancing times by 912.5 ms to compensate for transmission delays in the Internet.

That’s all you need to do to use a service with sockets—construct a suitably configured socket, call Connect, and then read data. If the service you’re using expects to be sent data, you can also write data into the NetworkStream. Obviously, you need to be prepared for errors—the Connect method will throw an exception if it is unable to connect to the service, and you should also be prepared to get errors anytime you try to read or write data with a socket; even if you connect successfully, parts of the network may later fail, severing the connection to the service. Again, .NET indicates this by throwing exceptions.

We’ve looked at only half of the story so far—what if you wanted to write a program that implements a service like the daytime service? You can do this with the Socket class too, but it’s a little more involved.

Implementing Services with the Socket Class

To implement a TCP-based service, we need to make sure our program is ready to receive requests when they come in. If a computer receives an incoming TCP connection request for some port number and no programs are currently listening for connections on that port number, it will simply reject the request. So the first thing we need to do is create a socket that listens for incoming connections (see Example 13-25).

Example 13-25. Listening for incoming TCP connections

using (Socket daytimeListener = new Socket(
    AddressFamily.InterNetworkV6,
    SocketType.Stream,
    ProtocolType.Tcp))
{
    daytimeListener.SetSocketOption(
        SocketOptionLevel.IPv6, (SocketOptionName) 27, 0);

    IPEndPoint daytimeEndpoint = new IPEndPoint(IPAddress.IPv6Any, 13);
    daytimeListener.Bind(daytimeEndpoint);

    daytimeListener.Listen(20);
    ...

As with the client side, we create a Socket object, once again specifying the address family, socket type, and protocol. (In this particular example, the lifetime we require for the Socket happens to be the same as the lifetime of our Main method, so a using statement is an appropriate way to manage the socket’s disposal.) Whereas with the client we could just use whichever IP address type came back from Dns.GetHostEntry, when we write a server we need to state which sort of address we want to listen on. Example 13-25 chooses the InterNetworkV6 family to enable the use of IPv6. If you want to support just IPv4 you can specify InterNetwork. In fact, this example supports both kinds of address—the call to SetSocketOption after the constructor puts this socket into dual mode, meaning that it’s able to accept connections through either IPv4 or IPv6. (The magic number 27 that appears in the call corresponds to a value defined by the Windows SDK that doesn’t currently have an equivalent entry in the SocketOptionName enumeration. So unfortunately, this is just a magic incantation that you need to know in order to enable a socket to accept incoming connections on either IP version.)

Warning

Dual-mode sockets are supported only on Windows Vista or later versions of Windows. If you want to accept incoming connections on both IPv4 and IPv6 on earlier versions of Windows, you’ll need to create two sockets and listen for connections on both.

Next, we call Bind—this is how our application claims ownership of a particular TCP port number. We’ve built an IPEndPoint object that specified port 13—the port number for the daytime service—and also indicates which of the local machine’s addresses we’d like to listen on. Machines often have multiple addresses—in fact, a connected machine usually has at least two IPv4 and two IPv6 addresses. Earlier we saw the special machine name localhost, and this corresponds to special IPv4 and IPv6 addresses. Even a completely disconnected machine has these addresses—the IPv4 address 127.0.0.1 and the IPv6 address ::1 always refer to the local machine. On top of this, a machine usually gets both an IPv4 and an IPv6 address when it connects to a network.

It’s possible to create sockets that listen on only the local addresses. That might not sound very useful, as it means that you cannot connect to those sockets over the network. In fact, this is quite handy for software developers. You can run services on your machine that are inaccessible over the network but which programs running locally on your machine can still connect to. This may allay the concerns of your IT administrators who don’t like the idea of desktop machines running web servers or other services because they (quite reasonably) consider such things to be a security risk. If you configure a service to listen on only these local addresses, it won’t be visible on the network, making it less likely to be a security liability. The test web server that Visual Studio can set up for ASP.NET web projects works this way—it uses only a local address, so it is accessible only to browsers running on the same machine. Note that this technique is not very useful outside of a developer machine. A local socket cannot be secured, so it will be accessible to any user logged in to the machine. For a developer box that’s fine, but on server systems, this might constitute a security risk. So you should avoid using local sockets.

Example 13-25 chooses the special address IPAddress.IPv6Any, which means that the socket will accept incoming connections directed to any of the computer’s IPv6 addresses. And since we’ve configured this to be a dual-mode socket, it will also accept incoming connections for any of the computer’s IPv4 addresses too.

If some other program on the computer is already using TCP port 13, the call to Bind will throw an exception—any particular port number can be owned by only one process on the machine at any one time. If Bind succeeds the port is now ours, and so we can call Listen to indicate that we’re ready for incoming connection requests.

As you can see from the last line of Example 13-25, Listen takes a single argument. This indicates the maximum backlog for this socket. The backlog allows for the situation where new connections arrive faster than our server can handle them. As you’ll see shortly, we need to execute some code to accept each incoming connection, and at busy times, we might lag behind—if a new connection request comes in before we’ve managed to accept the last one, that new request goes into the backlog queue. If the number of requests in the backlog gets as high as the number we pass to Listen, the OS will start rejecting any further requests until our application catches up.

Our socket is now in a listening state, which means that if client programs start trying to connect to our computer on port 13, the OS knows those connections are destined for our program. The next thing our code has to do is accept those connections. Example 13-26 does this in a loop so that it can keep accepting connection requests for as long as the program runs.

Example 13-26. Accepting incoming connections

while (true)
{
    Socket incomingConnection = daytimeListener.Accept();
    using (NetworkStream connectionStream =
                        new NetworkStream(incomingConnection, true))
    using (StreamWriter writer = new StreamWriter(connectionStream))
    {
        writer.WriteLine(DateTime.Now);
    }
}

This code calls Accept on the listening Socket. If there are currently no clients trying to connect to the service, this call will block—it won’t return until there’s a client. Once at least one client is attempting to use the service, this will return, handing back another Socket object. The Socket API is designed to allow multiple simultaneous connections to a single service, and so each call to Accept returns a new Socket. Your server will end up with one Socket object for each distinct connected client, plus the one listening Socket.

Note

You never actually send or receive data on the listening socket. It doesn’t represent a TCP connection—its only job is to return a new socket for each incoming TCP connection you accept. Arguably it’s a little weird to use the same Socket class for both jobs, because accepting incoming connections feels like a pretty different kind of activity than representing an active TCP connection. But that’s how sockets have worked for decades. .NET is merely continuing the slightly eccentric tradition.

Example 13-26 chooses to deal with the clients one at a time—the loop accepts a single connection, sends a response, closes the connection, and then moves on to the next client. So this particular server will have up to two active Socket objects at any one time—the one for the client connection it’s currently handling, and the one Socket that is listening for incoming connections. You don’t need to do this—it’s very common to accept new connections on a listening socket when you already have open connections that came from the same socket. (For example, a web server does not insist on finishing the processing of whatever request it’s handling at the moment before starting work on the next one. It’s common for a server to have hundreds of inbound connections open simultaneously.) But since this particular service can do all the work it needs to do and then close the connection immediately, it doesn’t have any particular reason to open several connections simultaneously.

The code that does the work here is pretty similar to the client code we saw in Example 13-24. As before, we create a NetworkStream, passing true to indicate that we want to close the Socket when we dispose the stream. This time we create a StreamWriter instead of a StreamReader, because we’re now implementing the server, and it’s going to be sending data rather than receiving it. We call the writer’s WriteLine method, passing the current date and time, which, as you may recall, was the whole point of this service in the first place. Example 13-27 shows the completed code.

Example 13-27. The complete daytime service

using (Socket daytimeListener = new Socket(
    AddressFamily.InterNetworkV6,
    SocketType.Stream,
    ProtocolType.Tcp))
{
    daytimeListener.SetSocketOption(SocketOptionLevel.IPv6,
                                    (SocketOptionName) 27, 0);

    IPEndPoint daytimeEndpoint = new IPEndPoint(IPAddress.IPv6Any, 13);
    daytimeListener.Bind(daytimeEndpoint);
    daytimeListener.Listen(20);

    while (true)
    {
        Socket incomingConnection = daytimeListener.Accept();
        using (NetworkStream connectionStream =
                            new NetworkStream(incomingConnection, true))
        using (StreamWriter writer = new StreamWriter(connectionStream,
                                                        Encoding.ASCII))
        {
            writer.WriteLine(DateTime.Now);
        }
    }
}

The first time you run this code, you can expect to see the warning dialog shown in Figure 13-9 (unless you’ve disabled your Windows Firewall). By default, the Windows Firewall will notify you when programs start listening for incoming network connections out of the blue. Typically, a program that has a legitimate need to accept connections will register itself with the firewall when it’s installed, so when a program that the firewall knows nothing about suddenly starts listening for incoming connections, that’s likely to be a sign of trouble—it’s exactly the sort of thing that malware would do if it wanted to make your machine available to hackers for distributing spam or launching distributed denial of service attacks. Of course, in this case, you know that the code is legitimate because you just wrote it, and the reason your program hasn’t gone through the official route of registering itself during installation is that you only just wrote the code, and you haven’t written the Windows Installer .msi yet. So as a developer, you expect to see this sort of warning for your own programs when they listen for incoming connections. (You didn’t see this for the WCF example earlier because it was using the specially reserved design-time address space that Visual Studio sets up when you install it. But that works only for HTTP—there’s no equivalent for sockets.) You just need to click Unblock, and you shouldn’t see this warning again for this particular program.

Firewall warning when listening for connections

Figure 13-9. Firewall warning when listening for connections

To test this program, you can use the client program you wrote earlier. The simplest approach will be to run two copies of Visual Studio, one for the client and one for the server. (Or you could configure Visual Studio to run both projects, as we did earlier.) Run the server first. Then go to the client, modify the line that specifies the machine name—replace time-nw.nist.gov with localhost—and then run the client. It should print out the current time and date. The format will be different from the one used by the NIST server—it’ll be the default used by the DateTime type. But that’s fine, because the Daytime Protocol specification says we’re free to use any format we like as long as it’s ASCII and it fits on a single line.

And that’s it for basic socket use. Sockets also support asynchronous versions of all the methods—in fact, they support both the event-based and the method-based asynchronous styles we encountered earlier. Since you’ve already seen this kind of code in action, we won’t show it again here, but we’ll come back to asynchronous programming techniques later in the book.

Other Networking Features

This chapter has touched on the most widely used networking types, but for completeness we should mention that some more specialized networking APIs are available. For example, the System.Net.Mail namespace provides types for sending email through an SMTP relay, and the related System.Net.Mime namespace supports MIME features, which are the standard way to represent attachments for emails. The System.Net.PeerToPeer namespaces provide access to the peer-to-peer networking features of Windows. (There are also WCF bindings that support this system.) The System.Net.NetworkInformation namespace provides types for discovering network status, through network interface information, and TCP/IP ICMP mechanisms such as ping. The TLS/SSL infrastructure that enables HTTPS to send encrypted data is also available for you to use directly, through the System.Net.Security namespace.

Summary

We looked at three approaches to networked communication in this chapter. WCF works at a fairly high level, enabling us to write servers that offer operations that can be invoked by clients, modeling these remote invocations as method calls. We also looked at the support for HTTP operations provided by the WebClient, HttpWebRequest, and HttpWebResponse classes. And finally, we looked at how to work at a very low level, dealing directly with the bytes sent across the network with TCP, by using the Socket class. There’s one particularly common form of communication that we’ve not yet looked at: many applications need to talk to a database. We’ll look at this in the next chapter.



[26] More accurately, the demands to which you feel inclined to accede.

[27] In fact, it has revealed a small problem: the tempuri.org that appears in the URL indicates something temporary that we’re supposed to fill in—the ServiceContract attribute on the original service definition has a Namespace attribute, and we’re supposed to pick a URI that is unique to our service. It’s not mandatory in this particular scenario because everything works with the default, but a temporary-looking URI doesn’t look entirely professional.

[29] In general, the WS-* family of web service protocols avoids depending on HTTP. This may seem like a peculiar tendency for web service standards, but a lot of the organizations involved in creating these specifications wanted the message formats to be useful in message-queue-based systems as well as HTTP. So in general, they tend to avoid transport-specific mechanisms.

[30] If it’s a multithreaded application, it’s usually OK to call a blocking API on a worker thread. It’s a bad idea only if you’re on the UI thread, but that’s the thread that all the interesting UI stuff happens on, so it’s an easy mistake to make.

[31] Atom is a common format for representing sets of items, such as blog entries or news articles. It’s similar to RSS, but tries to avoid some of RSS’s inconsistencies and limitations.

[32] Cookies are so widely supported that although they’re not technically part of the HTTP specification, they might as well be.

[33] Silverlight applications are an exception. They rely on the web browser to make HTTP requests, and so your requests will send whatever cookies the containing browser would normally send.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset