Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Visual CSharp 2005 Recipes (2006) [eng]

.pdf
Скачиваний:
48
Добавлен:
16.08.2013
Размер:
4.04 Mб
Скачать

368 C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

{

writer.Write(Recipe10_11Shared.AcknowledgeOK); Console.WriteLine(ID + ": Connection established.");

string message = "";

while (message != Recipe10_11Shared.Disconnect)

{

try

{

// Read the message from the client. message = reader.ReadString();

}

catch

{

// For the purpose of the example, any // exception should be taken as a

// client disconnect.

message = Recipe10_11Shared.Disconnect;

}

if (message == Recipe10_11Shared.RequestData)

{

Console.WriteLine(ID + ": Requested data. ", "Sending...");

// The filename could be supplied by the // client, but in this example a test file // is hard-coded.

fileStream = new FileStream("test.bin", FileMode.Open, FileAccess.Read);

// Send the file size--this is how the client // knows how much to read. writer.Write(fileStream.Length.ToString());

// Start an asynchronous send operation. stopDataTransfer = false; StreamData(null);

}

else if (message == Recipe10_11Shared.Disconnect)

{

Console.WriteLine(ID +

": Client disconnecting..."); stopDataTransfer = true;

}

else

{

Console.WriteLine(ID + ": Unknown command.");

}

}

}

}

else

{

Console.WriteLine(ID +

": Could not establish connection.");

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

369

}

}

}

Console.WriteLine(ID + ": Client connection closed.");

}

private void StreamData(IAsyncResult asyncResult)

{

// Always complete outstanding asynchronous operations to avoid leaks. if (asyncResult != null)

{

try

{

client.GetStream().EndWrite(asyncResult);

}

catch

{

//For the purpose of the example, any exception obtaining

//or writing to the network should just terminate the

//download.

fileStream.Close();

return;

}

}

if (!stopDataTransfer && !Recipe10_11Server.Terminate)

{

// Read the next block from the file.

int bytesRead = fileStream.Read(buffer, 0, buffer.Length);

// If no bytes are read, the stream is at the end of the file. if (bytesRead > 0)

{

Console.WriteLine(ID + ": Streaming next block.");

// Write the next block to the network stream. client.GetStream().BeginWrite(buffer, 0, buffer.Length,

StreamData, null);

}

else

{

// End the operation.

Console.WriteLine(ID + ": File streaming complete."); fileStream.Close();

}

}

else

{

// Client disconnected.

Console.WriteLine(ID + ": Client disconnected."); fileStream.Close();

}

}

}

}

370 C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

And here is the client code:

using System; using System.Net;

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

namespace Apress.VisualCSharpRecipes.Chapter10

{

public class Recipe10_11Client

{

private static void Main()

{

using (TcpClient client = new TcpClient())

{

Console.WriteLine("Attempting to connect to the server ", "on port 8000.");

//Connect to the server. client.Connect(IPAddress.Parse("127.0.0.1"), 8000);

//Retrieve the network stream from the TcpClient. using (NetworkStream networkStream = client.GetStream())

{

//Create a BinaryWriter for writing to the stream.

using (BinaryWriter writer = new BinaryWriter(networkStream))

{

// Start a dialogue. writer.Write(Recipe10_11Shared.RequestConnect);

// Create a BinaryReader for reading from the stream. using (BinaryReader reader =

new BinaryReader(networkStream))

{

if (reader.ReadString() == Recipe10_11Shared.AcknowledgeOK)

{

Console.WriteLine("Connection established." + "Press Enter to download data.");

Console.ReadLine();

//Send message requesting data to server. writer.Write(Recipe10_11Shared.RequestData);

//The server should respond with the size of

//the data it will send. Assume it does.

int fileSize = int.Parse(reader.ReadString());

//Only get part of the data then carry out a

//premature disconnect.

for (int i = 0; i < fileSize / 3; i++)

{

Console.Write(networkStream.ReadByte());

}

Console.WriteLine(Environment.NewLine);

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

371

Console.WriteLine("Press Enter to disconnect."); Console.ReadLine(); Console.WriteLine("Disconnecting..."); writer.Write(Recipe10_11Shared.Disconnect);

}

else

{

Console.WriteLine("Connection not established.");

}

}

}

}

}

// Wait to continue. Console.WriteLine(Environment.NewLine); Console.WriteLine("Connection closed. Press Enter"); Console.ReadLine();

}

}

}

10-12. Communicate Using UDP

Problem

You need to send data between two computers on a network using a UDP stream.

Solution

Use the System.Net.Sockets.UdpClient class, and use two threads: one to send data and the other to receive it.

How It Works

UDP is a connectionless protocol that doesn’t include any flow control or error checking. Unlike TCP, UDP shouldn’t be used where reliable communication is required. However, because of its lower overhead, UDP is often used for “chatty” applications where it is acceptable to lose some messages. For example, imagine you want to create a network in which individual clients send information about the current temperature at their locations to a server every few minutes. You might use UDP in this case because the communication frequency is high and the damage caused by losing a packet is trivial (because the server can just continue to use the last received temperature reading).

The Code

The application shown in the following code uses two threads: one to receive messages and one to send them. The application stops when the user presses the Enter key without any text to send. Notice that UDP applications cannot use the NetworkStream abstraction that TCP applications can. Instead, they must convert all data to a stream of bytes using an encoding class, as described in recipe 2-2.

372C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

using System; using System.Text; using System.Net;

using System.Net.Sockets; using System.Threading;

namespace Apress.VisualCSharpRecipes.Chapter10

{

class Recipe10_12

{

private static int localPort;

private static void Main()

{

// Define endpoint where messages are sent. Console.Write("Connect to IP: ");

string IP = Console.ReadLine(); Console.Write("Connect to port: ");

int port = Int32.Parse(Console.ReadLine());

IPEndPoint remoteEndPoint =

new IPEndPoint(IPAddress.Parse(IP), port);

//Define local endpoint (where messages are received). Console.Write("Local port for listening: ");

localPort = Int32.Parse(Console.ReadLine());

//Create a new thread for receiving incoming messages. Thread receiveThread = new Thread(ReceiveData); receiveThread.IsBackground = true; receiveThread.Start();

UdpClient client = new UdpClient();

Console.WriteLine("Type message and press Enter to send:");

try

{

string text;

do

{

text = Console.ReadLine();

// Send the text to the remote client. if (text.Length != 0)

{

//Encode the data to binary using UTF8 encoding. byte[] data = Encoding.UTF8.GetBytes(text);

//Send the text to the remote client. client.Send(data, data.Length, remoteEndPoint);

}

} while (text.Length != 0);

}

catch (Exception err)

{

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

373

Console.WriteLine(err.ToString());

}

finally

{

client.Close();

}

// Wait to continue. Console.WriteLine(Environment.NewLine); Console.WriteLine("Main method complete. Press Enter"); Console.ReadLine();

}

private static void ReceiveData()

{

UdpClient client = new UdpClient(localPort);

while (true)

{

try

{

// Receive bytes.

IPEndPoint anyIP = new IPEndPoint(IPAddress.Any, 0); byte[] data = client.Receive(ref anyIP);

//Convert bytes to text using UTF8 encoding. string text = Encoding.UTF8.GetString(data);

//Display the retrieved text. Console.WriteLine(">> " + text);

}

catch (Exception err)

{

Console.WriteLine(err.ToString());

}

}

}

}

}

Usage

To test this application, load two instances at the same time. On computer A, specify the IP address for computer B. On computer B, specify the address for computer A. You can then send text messages back and forth at will. You can test this application with clients on the local computer using the loopback alias 127.0.0.1, provided you use different listening ports. For example, imagine a situation with two UDP clients, client A and client B. Here’s a sample transcript for client A:

Connect to IP: 127.0.0.1

Connect to port: 8001

Local port for listening: 8080

Hi there!

374 C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

And here’s the corresponding transcript for client B (with the received message):

Connect to IP: 127.0.0.1

Connect to port: 8080

Local port for listening: 8001

>> Hi there!

10-13. Avoid Hard-Coding the XML Web Service URL

Problem

You need to use an XML Web service located at a URL that might change after you deploy the client application.

Solution

Use a dynamic URL, which will be retrieved automatically from the client application’s configuration file. You can configure a dynamic URL in the URL Behavior section of a web reference’s properties in Microsoft Visual Studio or by using the /urlkey parameter with the Web Services Description Language tool (wsdl.exe).

How It Works

When you create a Web reference in Visual Studio 2005, the automatically generated proxy class is configured to use a dynamic URL as the address of the referenced Web service. The actual URL used to contact the Web service at runtime is read from your application’s configuration file. This allows you to easily change the location of the Web service without recompiling your code. The automatically generated configuration section looks something like the following, where the value attribute contains the URL of the Web service:

<applicationSettings> <Recipe10_13.Properties.Settings>

<setting name="Recipe10_13_MyWebService_MyWebService" serializeAs="String"> <value>http://localhost/TestWebService/MyWebService.asmx</value>

</setting> </Recipe10_13.Properties.Settings>

</applicationSettings>

In previous releases of Visual Studio, dynamic URLs were not the default behavior. In these cases you can configure the setting by looking at the URL Behavior option in the Properties window for the web reference, as shown in Figure 10-2.

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

375

Figure 10-2. Configuring a dynamic URL for a Web service in Visual Studio

If you use wsdl.exe from the command line to generate your Web service proxy class, it uses a static URL by default. To configure wsdl.exe to use a dynamic URL, you must use the /urlkey parameter and specify the configuration setting name that the proxy class should read from the configuration file. For example:

wsdl http://localhost/TestWebService/MyWebService.asmx?WSDL /urlkey:MyWebService

Whether you’re using Visual Studio or wsdl.exe, the automatically generated proxy class is coded in such a way that if the class doesn’t find the configuration parameter containing a dynamic URL, it defaults to the static URL that was used during development.

Tip You can always manually override the URL setting in your code by modifying the Url property of the proxy class after you instantiate it.

Tip

376 C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

10-14. Set Authentication Credentials for an XML Web Service

Problem

You want an XML Web service client to submit logon credentials for IIS authentication.

Solution

Configure the Credentials and Certificates properties of the Web service’s proxy class with the appropriate credentials prior to calling a Web service method.

How It Works

You can configure XML Web services, like Web pages, to require users to authenticate using credentials such as usernames and passwords or X.509 certificates. Unlike Web pages, XML Web services have no built-in method for retrieving authentication information from the client because XML Web services are executed by other applications, not directly by the user. Thus, the application that’s interacting with the XML Web service bears the responsibility for submitting any required authentication information.

Similar to the System.Net.WebRequest discussed in recipe 10-6, the Web service proxy classes automatically generated by Visual Studio and the Web Services Description Language tool (wsdl.exe) implement Credentials and ClientCertificates properties. Using these properties allows you to associate user credentials with Web method calls. The approach you use depends on the type of authentication implemented by the Web service:

If the Web service is using basic or digest authentication, you can transmit a username and password combination by manually creating a new System.Net.NetworkCredential object and assigning it to the proxy’s Credentials property. With digest authentication, you may also supply a domain name.

If the Web service is using Windows integrated authentication, you can take the same approach and manually create a new NetworkCredential object. Alternatively, you can configure the proxy to use the current user login information by setting the proxy’s UseDefaultCredentials property to true.

If the Web service requires a client certificate, you can load the certificate from a file using the System.Security.Cryptography.X509Certificates.X509Certificate2 class and add that to the proxy’s ClientCertificates collection.

In the .NET Framework 2.0, you can load an X.509 certificate from a certificate store using the class System.Security.Cryptography.X509Certificates.X509Store. You can either find a certificate in the store programmatically using the X509Store.Certificates.Find method or present the user with a Windows dialog box using X509Store.Certificates.Select and allow them to select the certificate.

To add more than one set of credentials to a proxy, create a CredentialCache object and add multiple NetworkCredential objects to the credential collection using the Add method. Add also allows you to specify the URI, port, and authentication mechanism for which each NetworkCredential object should be used. Then assign the CredentialCache object to the proxy’s Credentials property.

C H A P T E R 1 0 N E T W O R K I N G A N D R E M OT I N G

377

The Code

The following XML Web service provides a simple user authentication test. GetIISUser returns the user that was authenticated by IIS. If anonymous access is allowed, the result will be an empty string because no authentication will be performed. If anonymous access is denied, the result will be a string in the form [DomainName]\[UserName] or [ComputerName]\[UserName].

public class AuthenticationTest : System.Web.Services.WebService {

// Retrieves the authenticated IIS user. [WebMethod()]

public string GetIISUser() { return User.Identity.Name;

}

}

The following example shows how a client can access an XML Web service that uses basic authentication, Windows integrated authentication, and X.509 certificate–based authentication:

using System; using System.Net;

using Recipe10_14.MyWebService;

using System.Security.Cryptography.X509Certificates;

namespace Apress.VisualCSharpRecipes.Chapter10

{

class Recipe10_14

{

public static void Main()

{

//Create a Web service proxy. For the purpose of the example, set

//the ConnectionGroupName to a unique value to stop the

//ServicePointManager reusing the connection in future requests. MyWebService proxy1 = new MyWebService(); proxy1.ConnectionGroupName = "Test1";

//Configure the proxy with a set of credentials for use over basic

//authentication.

CredentialCache cache = new CredentialCache(); cache.Add(new Uri(proxy1.Url), "Basic",

new NetworkCredential("user", "password")); proxy1.Credentials = cache;

//Try to call the GetIISUser Web method.

try

{

Console.WriteLine("Authenticated user = {0}", proxy1.GetIISUser());

}

catch (WebException)

{

Console.WriteLine("Basic authentication failed");

}

//Create a proxy that authenticates the current user

//with Windows integrated authentication.

MyWebService proxy2 = new MyWebService(); proxy2.ConnectionGroupName = "Test2"; proxy2.Credentials = CredentialCache.DefaultCredentials;

Соседние файлы в предмете Программирование на C++