< C++, C# & VB .NET Services & Types Program Examples | Main | C# & VB .NET Socket Program Examples >


 

Chapter 6 Part 3:

Introduction to System.Net

 

 

What do we have in this chapter 6 Part 3?

  1. Using the Socket-Level Classes

  2. Socket Classes

  3. Reading and Writing with NetworkStream

  4. Using the Socket-Level Helper Classes

  5. C++ Socket Program Example

 

Using the Socket-Level Classes

 

The socket-level classes in System.Net enable fine-grained control over the way data is read from and written to the network. These classes include access to sockets, a stream pattern for network I/O, and some helper classes that simplify a number of the most common tasks that are desirable when working with this level of the network. Figure 6-3 displays the groups of classes that make up the socket-level APIs for System.Net.

 

Socket-level classes in System.Net in .NET namespace

 

Figure 6-3: Socket-level classes in System.Net

 

Socket Classes

 

The System.Net.Sockets namespace contains a class-based representation of the Windows Sockets model commonly known as Winsock. These classes are particularly focused on a subset of the Winsock model that deals with IP-based protocols such as User Datagram Protocol (UDP) and Transmission Control Protocol (TCP). These classes can be extended to enable other protocols such as NetBIOS and Infrared. The key classes to be aware of at this level are Socket, SocketPermission, and SocketException. The Socket class provides the vast majority of the functionality needed to access the network and acts as the gatekeeper for the transition from managed .NET Framework code to the underlying native Win32 APIs. SocketException is used to represent most exceptions that occur within the Socket class. The SocketPermission class is used to control access to socket-level resources through code access security. These classes are described in more detail in Chapters 8 and 9. The following example demonstrates how to download a Web page using the socket-level classes.

 

C#

 

static void Main(string[ ] args)

{

    // Validate the input values

    if(args.Length < 2)

    {

        Console.WriteLine("Expected executable_file_name serverName path");

        Console.WriteLine("Example: DownloadWebPage contoso.com /");

        return;

    }

 

    string server = args[0];

    string path = args[1];

    int port = 80;

 

    IPHostEntry host = null;

    IPEndPoint remoteEndPoint = null;

    Socket client = null;

 

    // Resolve the server name

    try

    {

        host = Dns.GetHostByName(server);

    }

    catch(Exception ex)

    {

        Console.WriteLine(ex.ToString());

        return;

    }

 

    // Attempt to connect on each address returned from DNS.

    // Break out once successfully connected

    foreach(IPAddress address in host.AddressList)

    {

        try

        {

            client = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);

 

            remoteEndPoint = new IPEndPoint(address, port);

            client.Connect(remoteEndPoint);

            break;

        }

        catch(SocketException ex)

        {

            Console.WriteLine(ex.ToString());

        }

    }

 

    // MakeRequest will issue the HTTP download request and write the server response to the console

    MakeRequest(client, path, server);

    client.Close();

}

 

public static void MakeRequest(Socket client, string path, string server)

{

    // Format the HTTP GET request string

    string Get = "GET " + path + " HTTP/1.0\r\nHost: " + server  + "\r\nConnection: Close\r\n\r\n";

 

    Byte[ ] ByteGet = Encoding.ASCII.GetBytes(Get);

 

    // Send the GET request to the connected server

    client.Send(ByteGet);

 

    // Create a buffer that is used to read the response

    byte[ ] responseData = new byte[1024];

 

    // read the response and save the ASCII data in a string

    int bytesRead = client.Receive(responseData);

    StringBuilder responseString = new StringBuilder();

    while (bytesRead != 0)

    {

        responseString.Append(Encoding.ASCII.GetChars(responseData), 0, bytesRead);

        bytesRead = client.Receive(responseData);

    }

 

    // Display the response to the console

    Console.WriteLine(responseString.ToString());

}

 

 

 

 

Visual Basic .NET

 

Sub Main(ByVal args As String())

 

    ' Validate the input values

    If args.Length < 2 Then

        Console.WriteLine("Expected executable_file_name serverName path")

        Console.WriteLine("Example: DownloadWebPage contoso.com /")

        Return

    End If

 

    Dim server As String = args(0)

    Dim path As String = args(1)

    Dim port As Integer = 80

 

    Dim host As IPHostEntry

    Dim remoteEndPoint As IPEndPoint

    Dim client As Socket

 

    ' Resolve the server name

    Try

        host = Dns.GetHostByName(server)

    Catch ex As Exception

        Console.WriteLine(ex.ToString())

        Return

    End Try

 

    ' Attempt to connect on each address returned from DNS.

    ' Exit out once successfully connected

    For Each address As IPAddress In host.AddressList

        Try

            client = New Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp)

            remoteEndPoint = New IPEndPoint(address, port)

            client.Connect(remoteEndPoint)

            Exit For

        Catch ex As Exception

            Console.WriteLine(ex.ToString())

        End Try

    Next

 

    ' MakeRequest will issue the HTTP download request and write the server response to the console

    MakeRequest(client, path, server)

    client.Close()

End Sub

 

Sub MakeRequest(ByVal client As Socket, ByVal path As String,

ByVal server As String)

 

    ' Format the HTTP GET request string

    Dim getString As String = "GET " + path + " HTTP/1.0" + ControlChars.CrLf

    getString += "Host: " + server + ControlChars.CrLf + "Connection: Close"

    getString += ControlChars.CrLf + ControlChars.CrLf

 

    Dim ByteGet As Byte() = Encoding.ASCII.GetBytes(getString)

 

    ' Send the GET request to the connected server

    client.Send(ByteGet)

 

    ' Create a buffer that is used to read the response

    Dim responseData() As Byte = New Byte(1024) { }

 

    ' Read the response and save the ASCII data in a string

    Dim bytesRead As Integer = client.Receive(responseData)

    Dim responseString As StringBuilder = New StringBuilder

 

    While bytesRead > 0

        responseString.Append(Encoding.ASCII.GetChars(responseData), 0, bytesRead)

        bytesRead = client.Receive(responseData)

    End While

 

    ' Display the response to the console

    Console.WriteLine(responseString.ToString())

End Sub

 

You’ll notice that the sample for downloading a Web page using sockets is much longer than the code it would take to do the same thing using a higher layer in System.Net, such as WebRequest or WebClient. This additional code is the tradeoff that’s often made when you decide to program at the socket level. Your application will have much greater control over the protocol that’s emitted, but it does so at the cost of additional code.

 

Reading and Writing with NetworkStream

 

Although NetworkStream isn’t a large class, it’s extremely useful because it provides a stream-based model for reading and writing data over a network. NetworkStream implements the standard methods and properties that you’ll find on the base System.IO.Stream pattern, as discussed in Chapter 2. You should consider a couple of interesting elements that are specific to this implementation when working with NetworkStream. First, a NetworkStream instance can be obtained either by constructing one from a socket or by getting one from another class such as one of the socket-level helper classes described in the next section. Second, because NetworkStream is based on a TCP connection and does not buffer data, it’s not seekable.

You can’t set the Position property to seek a particular point in the stream. You should be aware of this limitation if you rely on other classes that use the Stream type and expect this functionality from the stream.

 

Using the Socket-Level Helper Classes

 

The socket-level helper classes include TcpClient, TcpListener, and UdpClient. These classes simplify the most common tasks that an application might perform using the TCP and UDP protocols. The TcpClient class is focused on the scenario of connecting to a remote host and getting a stream that can be used to read and write data. The TcpListener class is the inverse of TcpClient. It provides methods for accepting an incoming socket connection and accessing a stream that’s created on top of that connection. The UdpClient class contains methods for sending and receiving data over UDP, including JoinMulticastGroup and DropMulticastGroup for IP multicasting data. Because the UDP protocol is connectionless, no model is provided for creating a NetworkStream on top of it. Without the ability to create a stream, there’s little value to having a listener correspondent to the UdpClient class because the UdpClient.Receive method takes care of receiving data from a remote host.

Because the socket-level helper classes do not include asynchronous methods, applications designed for high-load scenarios should use the Socket class instead.

 

C++ Socket Program Example

 

Create a new CLR console application and you can use SocketChap6CP as the project and solution names if you want.

 

C++ Socket Program Example - creating a new CLR console mode application

 

Add the following code.

 

// SocketChap6CP.cpp : main project file.

/// <summary>

/// This sample demonstrates the socket classes

/// being used to download a Web page that is passed in on the command line.

/// </summary>

#include "stdafx.h"

 

using namespace System;

using namespace System::IO;

using namespace System::Net;

using namespace System::Text;

using namespace System::Net::Sockets;

 

static void MakeRequest(Socket^ client, String^ path, String^ server)

{

    // Format the HTTP GET request string

    Console::WriteLine("Formatting the HTTP GET request string...");

    String^ Get = "GET " + path + " HTTP/1.0\r\nHost: " + server + "\r\nConnection: Close\r\n\r\n";

    array<Byte>^ ByteGet = Encoding::ASCII->GetBytes(Get);

 

    // Send the GET request to the connected server

    Console::WriteLine("Sending the GET request to the connected server...");

    client->Send(ByteGet);

    Console::WriteLine("Send() is OK...");

 

    // Create a buffer that is used to read the response

    Console::WriteLine("Creating a buffer that is used to read the response...");

    array<Byte>^ responseData = gcnew array<Byte>(1024);

 

    // Read the response and save the ASCII data in a string

    Console::WriteLine("Reading the response and save the ASCII data in a string...");

    int bytesRead = client->Receive(responseData);

    Console::WriteLine("Receive() is OK...");

    Console::WriteLine("Creating the StringBuilder and do the appending using Append()...");

    StringBuilder^ responseString = gcnew StringBuilder();

    while (bytesRead != 0)

    {

        responseString->Append(Encoding::ASCII->GetChars(responseData), 0, bytesRead);

        bytesRead = client->Receive(responseData);

    }

 

    // Display the response to the console

    Console::WriteLine("Displaying the response to the console...");

    Console::WriteLine("   " + "\"" + responseString->ToString() + "\"");

}

/// <summary>

/// The main entry point for the application.

/// </summary>

[STAThread]

int main(array<System::String ^> ^args)

{

    // Validating the input values

    if (args->Length < 2)

    {

        Console::WriteLine("Usage: Executable_file_name serverName path");

        Console::WriteLine("Example: Executable_file_name contoso.com /");

        Console::WriteLine("Example: Executable_file_name youtube.com /");

        // Return to OS

        return 0;

    }

 

    // Else, if enough args supplied...

    // Store the first and second command line args

    Console::WriteLine("Reading & storing the first and second command line args...");

    String^ server = args[0];

    String^ path = args[1];

    // change accordingly

    int port = 80;

 

    IPHostEntry^ host = nullptr;

    IPEndPoint^ remoteEndPoint = nullptr;

    Socket^ client = nullptr;

 

    // Resolve the server name

    try

    {

         Console::WriteLine("Resolving the server name...");

         host = Dns::GetHostEntry(server);

    }

    catch (Exception^ ex)

    {

        Console::WriteLine("Error: " + ex->Message);

        return 0;

    }

 

    // Attempt to connect on each address returned from DNS. Break out once successfully connected

    Console::WriteLine("Attempt to connect on each address returned from DNS...");

    for each (IPAddress^ address in host->AddressList)

    {

        try

        {

            client = gcnew Socket(address->AddressFamily, SocketType::Stream, ProtocolType::Tcp);

            Console::WriteLine("Socket() is OK...");

            Console::WriteLine("Creating the IPEndPoint...");

            remoteEndPoint = gcnew IPEndPoint(address, port);

            client->Connect(remoteEndPoint);

            Console::WriteLine("Remote Connect() is OK, address: " + address + ", port: " + port);

            break;

        }

        catch (SocketException^ ex)

        {

            Console::WriteLine("Error: " + ex->Message);

        }

    }

 

    // MakeRequest will issue the HTTP download request and write the server response to the console

    Console::WriteLine("Issuing the HTTP download request using MakeRequest()...");

    MakeRequest(client, path, server);

    Console::WriteLine("MakeRequest() is OK...");

    Console::WriteLine("Closing the client...");

    client->Close();

 

    return 0;

}

 

Build and run the project. The following are the output samples.

 

 

 

 

C++ Socket Program Example - a sample output without any argument supplied

 

C++ Socket Program Example - a sample output with argument values supplied

 


< C++, C# & VB .NET Services & Types Program Examples | Main | C# & VB .NET Socket Program Examples >