Now that the actual data-access logic has been written, the actual discovery web service will need very little code. For the most part, its methods simply wrap the P2PDatabase component. All exceptions are caught, logged, and suppressed, so that sensitive information will not be returned to the client, who is in no position to correct low-level database errors anyway.
A typical interaction with the discovery service goes as follows:
The client generates a new GUID to identify itself, records its current IP address and port, and calls Register() with this information.
The client inspects the files that it's sharing, creates the keywords lists, and calls PublishFiles() to submit the catalog.
After this point, the client calls RefreshRegistration() periodically, to prevent its login information from expiring.
Optionally, the client calls SearchForFile() with any queries.
The client ends the session by calling Unregister().
The complete web-service code is shown here:
Public Class DiscoveryService Inherits System.Web.Services.WebService ' This object will be created with each new method request. ' (This isn't a problem because P2PDatabase is stateless.) Private DB As New P2PDatabase() <WebMethod()> _ Public Function Register(ByVal peer As Peer) As Boolean Try DB.AddPeer(peer) Return True Catch Return False End Try End Function <WebMethod()> _ Public Function RefreshRegistration(ByVal peer As Peer) As Boolean Try DB.RefreshPeer(peer) Return True Catch err As Exception Trace.Write(err.ToString) Return False End Try End Function <WebMethod()> _ Public Sub Unregister(ByVal peer As Peer) Try DB.DeletePeerAndFiles(peer) Catch err As Exception Trace.Write(err.ToString) End Try End Sub <WebMethod()> _ Public Function PublishFiles(ByVal files() As SharedFile, _ ByVal peer As Peer) As Boolean Try DB.AddFileInfo(files, peer) Return True Catch err As Exception Trace.Write(err.ToString) Return False End Try End Function <WebMethod()> _ Public Function SearchForFile(ByVal keywords() As String) As SharedFile() Try Return DB.GetFileInfo(keywords) Catch err As Exception Trace.Write(err.ToString) Dim EmptyArray() As SharedFile = {} Return EmptyArray End Try End Function End Class
To improve performance, you might consider using ASP.NET caching. However, as queries are likely to differ quite a bit, and the list of keywords stored in the database can grow dramatically, it's difficult to implement an effective caching strategy.
There's one function that the web service doesn't provide: removing expired peer information. In Chapter 5, you saw how this type of work can be performed on a dedicated thread. However, as web services are stateless, it's not easy to run other code asynchronously. Instead, you would need to create a separate component that runs on the server (perhaps a Windows service), and periodically scans the database for Peer records beyond a certain age limit. It would then remove these records using the DeletePeerAndFiles stored procedure. This logic is easy to implement and could be added to the P2PDatabase class.
Once you've completed the service, you can load the corresponding .asmx page into Internet Explorer to see an automatically generated test page that lists the web methods exposed by this web service (see Figure 8-6). However, you won't be able to test them directly because they require a client that can create and configure the custom Peer and SharedFile objects.
To put the directory service to a real test, you need to build a dedicated client application, such as the one presented in the next chapter. In this case, you'll probably want to debug your web service and client application at the same time. To do so, right-click the project name in the Solution Explorer, and select Properties. Then, navigate to the Configuration Properties → Debugging node, and choose "Wait for an external process to connect" (as shown in Figure 8-7). Now, when you run your web-service project, Visual Studio .NET will load the debugging symbols and wait for a client request. You can use the full set of debugging tools to watch the web service as it reacts, including breakpoints, variable watches, and the command window.