Working with Sockets
Overview
The preceding chapters talked generally about .NET support for network programming. They showed you how to manipulate streams in .NET applications, and the classes to work with IP addresses and DNS lookups. In this chapter we'll start programming with sockets.
In particular, we'll cover:
What sockets are, and the different types of sockets
Socket support in .NET-the System.Net.Sockets.Socket class
Creating a TCP socket server application
Setting socket options
Creating an asynchronous TCP socket server application
Socket permissions
Sockets
A socket is one end of a two-way communication link between two programs running on a network. By connecting the two sockets together, you can pass data between different processes (either local or remote). The socket implementation provides the encapsulation of the network and transport level protocols.
Sockets were originally developed for UNIX at the University of California at Berkeley. In UNIX, the input/output method for communication follows an open/read/write/close algorithm. Before a resource can be used, it needs to be opened, by specifying the relevant permissions and other parameters. Once the resource is opened, it can be read from or written to. After using the resource, the user can finally call the Close() method, signaling to the operating system that it has finished its tasks with the resource.
When Inter-Process Communication (IPC) and Networking facilities are added to the UNIX operating system, they adopt the familiar pattern of Input/Output. All the resources opened for communication are identified by a descriptor in UNIX and Windows. These descriptors (also called handles) can be a pointer to a file, memory, or any other channel, and actually point to an internal data structure primarily used by the operating system. A socket, being a resource, is also represented by a descriptor. Therefore, for sockets, a descriptor's life can be divided into three phases: open/create socket, receive and send to socket, and ultimately closing the socket.
The IPC interface for communicating between different processes is built on top of the I/O methods. They facilitate sockets to send and receive data. Each target is specified by a socket address; therefore this address can be specified in the client to connect with the target.
Socket Types
There are two basic types of sockets-stream sockets and datagram sockets.
Stream Sockets
A stream socket is a connection-oriented socket that consists of a stream of bytes that can be bi-directional, meaning that the application can both transmit and receive through the endpoint. A stream socket guarantees error correction, handles delivery, and preserves data sequence. It can be relied upon to deliver sequenced, unduplicated data. It is also suitable for transmitting large amounts of data because the overhead of establishing a separate connection for sending each message can be unacceptable for small amounts of data. Stream sockets achieve this level of quality through the use of the Transmission Control Protocol (TCP). TCP makes sure that your data arrives on the other side in sequence, and error free.
In these types of sockets, a path is formed before communicating messages. This ensures that both the sides taking part in the communication are alive and responding. If your application sends two messages to the recipient, then the messages are guaranteed to be received in sequence. However, individual messages can be broken up into several packets and there is no way to determine record boundaries. Under TCP, its up to the protocol to disassemble the transmission of data into packets of appropriate sizes, send them over and put them back together on the other side. An application only knows that it's sending a certain number of bytes to the TCP layer and that the other side gets those bytes. What TCP effectively does here is to break up that data into appropriately sized packets, receive the packets on the other side, unwrap the data and then put it back together.
Streams are based on explicit connections: socket A requests a connection to socket B, and socket B accepts or rejects the connection request.
Stream sockets are preferable to datagram sockets when the data must be guaranteed delivery on the other side, and when the data size is large. Therefore, if the reliability of the connection between two applications is paramount, use stream sockets. An e-mail server is one example of an application that must deliver content in the correct order, and without duplications or omissions. A stream socket depends on TCP to ensure the delivery of messages to their destinations-we'll talk more about TCP in the next chapter.
Datagram Sockets
Datagram sockets are sometimes called connection-less sockets, that is, no explicit connection is established-a message is sent to the specified socket that can be received appropriately from the specified socket. Using stream sockets is indeed a more reliable method than datagram sockets, but with some applications, the overhead incurred by establishing an explicit connection is unacceptable. An example application would be a day/time server, which is proving day/time synchronization to its clients. After all, it takes some time to establish a reliable connection with the server, which merely delays the service and ultimately the purpose of the server application is unseen. To reduce the overhead we would use datagram sockets.
The use of datagram sockets requires User Datagram Protocol (UDP) to pass the data from a client to the server. There are some limitations on the size of the message in this protocol, and unlike stream sockets, where a message can be reliably sent to the destination server, datagram sockets offer no such guarantee. There are no errors returned from the server if the data is lost in between. We'll talk more about UDP in Chapter 6.
Apart from the two types that are described above, there is also a generalized form of sockets, called raw sockets.
Raw Sockets
The main purpose of using raw sockets is to bypass the mechanism by which the computer handles TCP/IP. It works by providing a custom implementation of the TCP/IP stack replacing the mechanism provided by the TCP/IP stack on the kernel-the packet is passed directly to the application that needs it and therefore it is much more efficient than going through the client's main network stack.
A raw socket, by definition, is a socket that takes packets, bypasses the TCP and UDP layers in the TCP/IP stack and sends them directly to the application.
For such sockets, the packet is not passed through the TCP/IP filter, meaning that there is no processing on the packet, and it comes out in its raw form. This puts the responsibility of handling all the data appropriately, and dealing with such actions as stripping off the headers and parsing onto the receiving application-it's like making a small TCP/IP stack in the application. However, you rarely need to program with raw sockets. Unless you are writing system software, or a packet analyzer type program, you don't need to get into the details. They are mainly used when writing custom low-level protocol applications. For example, various TCP/IP utilities such as traceroute, ping, or arp use raw sockets.
Working with raw sockets requires substantial knowledge of the underlying TCP/UDP/IP protocol, and is beyond the scope of this book.
Ports
A port is defined to solve the problem of communicating with multiple applications simultaneously; it basically expands the notion of an IP address. A computer running simultaneous applications that receives a packet though the network can identify the target process using a unique port number that is determined when the connection is being made.
The socket is composed of the IP address of the machine and the port number used by the TCP application. Because the IP address is unique across the Internet and the port numbers are unique on the individual machine, the socket numbers are also unique across the entire Internet. This enables a process to talk to another process across the network, based entirely on the socket number.
Certain services have port numbers reserved for them; these are the well-known port numbers, such as FTP on port number 21. Your application can use any port number that hasn't been reserved or is already in use. The Internet Assigned Numbers Authority (IANA) prescribes the list of well-known port numbers.
Normally, a client-server application using sockets consists of two different applications-a client that initiates the connection with the target (the server), and the server, which waits for the connection from the client.
For example, on the client side, the client must know the target's address and the port number. To make the connection request the client tries to establish the connection with the server:
If everything goes well, provided that the server is already running before the client tries to connect to it, the server accepts the connection.
Upon acceptance, the server application creates a new socket to deal specifically with the connected client.
The client and server can now communicate with each other by reading from and writing to their respective sockets.