Writing programs that access the network used to be a relatively difficult
task. With .NET, this is no longer the case. The .NET Framework class library
includes two namespaces that are full of classes that help you with
networking: System.Net and System.Net.Sockets. In this article, I
will discuss one of the most important classes in System.Net.Sockets:
the Socket class. The System.Net.Sockets.Socket class can be used as a socket
in a server application as well as in a client application. It also allows both
synchronous and asynchronous operations. This article shows how to use the
Socket class in a client application.
A socket is an endpoint of a connection. It is a descriptor that lets an
application read from and write to the network. With sockets, client and server
applications can communicate by sending and receiving streams of bytes over
connections. To send a message to another socket used in a software
application, you need to know not only the machine's IP address that hosts the
software application, but also the software's process identifier on that
machine. The identification of a software process in a machine is achieved
through the use of a unique number called port. Therefore, to send a message
from a socket in one application to another socket at the other end of
a connection, you need to know the machine's IP address and the application's
port number. In the .NET Framework, a socket is represented by the
System.Net.Sockets.Socket class. This class is an implementation of the Sockets
API, which is also known as the Berkeley sockets interface. The Sockets API was
developed in the early 80s at the University at Berkeley for the 4.1c release
of Berkeley Software Distribution (BSD) Unix. This distribution contained an
early version of the Internet protocols.
|
Related Reading
VB.NET Core Classes in a Nutshell |
The Socket class provides the following constructor for you to use to create a Socket
object.
public Socket (AddressFamily addressFamily,
SocketType socketType,
ProtocolType protocolType );
Instantiating a Socket object requires you to pass three arguments to its
constructor: AddressFamily, SocketType, and ProtocolType, all of which are
enumerations that are part of the System.Net.Sockets namespace. An
AddressFamily member defines the addressing scheme that a Socket object uses to
resolve an address. For socket applications that will work on the Internet, you
use InterNetwork.
SocketType determines the type of socket. The most popular socket type is
Stream. This type of socket supports two-way connection-based byte streams.
ProtocolType specifies the type of the low-level protocol that the socket uses
to communicate. A stream socket must be used with the Transmission Control
Protocol (TCP) protocol type and the InterNetwork address family. For example,
the following code instantiates a Socket object.
Socket mySocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
The arguments you pass to the constructor are available in the following
read-only properties: AddressFamily, SocketType, and ProtocolType.
Once you have a socket instance, you can connect to a remote server using the
Connect method of the Socket class. This method attempts to connect to a remote
server synchronously. It waits until a connection attempt is successful or
failed before releasing control to the next line in the program. Even though
this method is easy to use, there is some preliminary work youneed to do before you can use
this method to connect to a remote server. Consider the signature of the
Connect method below.
public void Connect( EndPoint remoteEP);
It accepts an argument: an instance of System.Net.EndPoint. The abstract
EndPoint represents a network address. It has a subclass:
System.Net.IPEndPoint. When using the Connect method, you typically pass an
IPEndPoint object containing the IP address and port number of the remote
server to which you want to connect. The question will then be, "How do you construct
an IPEndPoint object for your socket to connect to a remote server?" Now, look
at the IPEndPoint class definition. It has two constructors.
public IPEndPoint(long address, int port);
public IPEndPoint(IPAddress address, int port);
Of these two constructors, the second is normally used because IP addresses
are often represented in dotted-quad notation (such as 129.36.129.44) and, as we
soon will see, in .NET socket programming, it is easier to get an IP
address in this notation than in a long address. However, the two constructors are
actually very similar. It's just that the remote IP address in the first
constructor is a long address, whereas in the second constructor, it is a
System.Net.IPAddress object. Whichever constructor you choose, you need to have
an IP address and the port number of the remote server. The port number is
usually not a problem, because popular services are allocated default port
numbers. For instance, HTTP uses port 80, Telnet uses port 25, and FTP uses port
21. The IP address is not normally directly available because it is easier for
us to remember domain names such as microsoft.com or oreillynet.com rather than
the IP addresses mapped to them. With this in mind, we need to resolve a domain
name to obtain the IP address of the remote server to which we'd like to connect. In this
event, to obtain an IPAddress instance that you can use to connect to a remote
server, you need the following other classes: System.Net.Dns and
System.Net.IPHostEntry. The Dns class is a final class that retrieves
information about a specific host from the Internet Domain Name System (DNS),
hence the name Dns. It is mainly used for its Resolve method, to obtain a set of
IP addresses mapped to a domain name. The Resolve method returns an IPHostEntry
object that contains an array of IP addresses. To obtain these IP addresses,
you use the IPHostEntry class' AddressList property. For example, the
following code displays all IP addresses mapped to a DNS name.
using System.Net.Sockets;
using System.Net;
try
{
String server = "microsoft.com"; // or any other domain name
IPHostEntry hostEntry = Dns.Resolve(server);
IPAddress[] ipAddresses = hostEntry.AddressList;
Console.WriteLine(server + " is mapped to:");
foreach (IPAddress ipAddress in ipAddresses)
{
Console.WriteLine(ipAddress.ToString());
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
When run, the code will display all IP addresses mapped to the DNS name microsoft.com. If a DNS name is mapped to more than one IP address, you can use any of those addresses, even though people normally use the first one. The reason for choosing the first one is because a DNS name is often mapped to one IP address only. Obtaining the first IP address mapped to a DNS name is achieved using the following code:
HostEntry.AddressList[0]
What's more important, once you get an IPAddress object, you can construct an
IPEndPoint object to connect to a remote server. If the connection is
successful, the Socket instance will set its Connected property to true. A
programmer often checks the value of this property before performing other
operations on the socket instance, because a server application can close a
connection after a period of time lapses. To close a connection explicitly when
you are done with a socket, you use the Close method. Normally, you need to call
the Shutdown method prior to invoking Close to flush all pending data.
After your socket is connected to a remote machine, you can send and receive
data. To send data in synchronous mode, you use the Send method. The data you
send must be placed in an array of bytes. There are four overloads of the Send
method, all of which return an Integer indicating the number of bytes sent. The
first overload is the simplest and the easiest to use of the four. It has the
following signature:
public int Send(byte[] buffer);
where buffer is an array of bytes containing the data you want to send. Using
this overload, all data in the buffer will be sent. The second overload allows
you to send all data in the buffer and specify the bitwise combination of the
System.Net.Sockets.SocketFlags enumeration members. It has the following
signature:
public int Send(byte[] buffer, SocketFlags socketFlags);
The third overload allows you to send all or part of the data in the buffer
and specify the bitwise combination of the SocketFlags enumeration.
public int Send( byte[] buffer, int size, SocketFlags socketFlags );
In this overload, size is the number of bytes to be sent.
The last overload is similar to the third overload, but it also allows you specify an offset position in the buffer to begin sending data. Its signature is as follows:
public int Send( byte[] buffer, int offset, int size, SocketFlags socketFlags );
In this overload, offset is the offset position. To receive data
synchronously, you use the Receive method. This method also has four overloads,
which are similar to the Send method overloads. The signatures of the overloads
are as follows:
public int Receive(byte[] buffer);
public int Receive(byte[] buffer, SocketFlags socketFlags);
public int Receive( byte[] buffer, int size, SocketFlags socketFlags );
public int Receive( byte[] buffer, int offset, int size, SocketFlags socketFlags );
When using the Receive method, you can use the Socket class' Available
property, which specifies the number of bytes of data received and is available
to be read.
This article introduced the System.Net.Sockets.Socket class and some other
supporting classes from the System.Net namespace. Socket is used to easily
access the network from your .NET application. You have also learned how to
instantiate a Socket object and how to send and receive streams.
Budi Kurniawan is a senior J2EE architect and author.
|
Related Reading .NET Framework Essentials |
Return to ONDotnet.com
Copyright © 2009 O'Reilly Media, Inc.