Writing a simple TCP/IP server

In document in .NET (Page 86-94)

Working with Sockets

3.4 Using TCP/IP to transfer files

3.4.2 Writing a simple TCP/IP server

C#

public class Form1 : System.Windows.Forms.Form {

private ArrayList alSockets;

...

VB.NET

Public Class Form1 Inherits System.Windows.Forms.Form private alSockets as ArrayList

...

Because any client wishing to connect to this server would need to know its IP address, it is helpful to display this on-screen. This is a cosmetic fea-ture, but it may come in handy in other applications. In order to retrieve the local IP address, we call the static method Dns.GetHostByName. This returns an IPHostEntry object, which is a collection of IP addresses, to accommodate multihomed computers, which many are. Element zero in this array is commonly the external IP address for the computer.

The Form1_Load method displays the local IP address on the form and starts the thread that will wait for incoming connections. If the

listenerThread method were to be called directly, the program would become unresponsive and appear to hang, while the socket waited on incoming connections. This effect is avoided by executing the

listenerThread method in a separate thread of execution, which can block without adversely affecting the user interface.

Figure 3.4 TCP Server application.

C#

private void Form1_Load(object sender, System.EventArgs e) {

IPHostEntry IPHost = Dns.GetHostByName(Dns.GetHostName());

lblStatus.Text = "My IP address is " + IPHost.AddressList[0].ToString();

alSockets = new ArrayList();

Thread thdListener = new Thread(new ThreadStart(listenerThread));

thdListener.Start();

}

VB.NET

Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim IPHost as IPHostEntry

IPHost = Dns.GetHostByName(Dns.GetHostName()) lblStatus.Text = "My IP address is " + _ IPHost.AddressList(0).ToString()

alSockets = new ArrayList()

Dim thdListener As New Thread(New ThreadStart _ (AddressOf listenerThread))

thdListener.Start() End Sub

The listenerThread method’s function is to wait indefinitely for TCP connections on port 8080 and then to redelegate the work of handling these requests to the handlerThread method. This function also reports the source of the connections.

This time, the reason for redelegating work to a thread is not to main-tain the responsiveness of the user interface, but rather to ensure that the application will continue to listen for new connections while it is handling a previous client. The new thread will be required to have access to the socket that is dealing with the current client. Otherwise, there would be no means of returning data.

This thread will block on the call to AcceptSocket. Execution will not continue until an incoming connection has been detected; when it has, a new socket is created and dedicated to handling this particular client. Once

this socket has established a connection, the socket is placed on top of the

alSockets array list to await pickup by the handler thread.

It may seem unusual that the socket is not passed directly to the thread.

This is because it is not valid to specify parameters when defining the start-ing point of a thread, for example, makstart-ing an erroneous statement such as

New ThreadStart(AddressOf handlerThread(Parameter))

Therefore, another means of passing parameters to threads is required. In this example, a public array list of sockets is used, where the top-most entry is used by the newest thread, and so forth. Another common technique for passing parameters to threads is to encapsulate the thread’s methods in a sep-arate class, with public variables acting as parameters. When a new instance of this class is created, it can be passed to the ThreadStart constructor.

Once the socket has been added to the array list, the handler thread is invoked, and this thread continues to listen for incoming connections.

Note: You may notice a port number added to the end of the source IP address. This is an internally negotiated port number used by TCP/IP.

More details on this topic can be found in Chapter 13.

C#

public void listenerThread() {

TcpListener tcpListener = new TcpListener(8080);

tcpListener.Start();

while(true) {

Socket handlerSocket = tcpListener.AcceptSocket();

if (handlerSocket.Connected) {

lbConnections.Items.Add(

handlerSocket.RemoteEndPoint.ToString() + " connected."

);

lock (this) {

alSockets.Add(handlerSocket);

}

ThreadStart thdstHandler = new

ThreadStart(handlerThread);

Thread thdHandler = new Thread(thdstHandler);

thdHandler.Start();

} } }

VB.NET

Public sub listenerThread()

Dim tcpListener as new TcpListener(8080) Dim handlerSocket as Socket

Dim thdstHandler as ThreadStart Dim thdHandler as Thread tcpListener.Start() do

handlerSocket = tcpListener.AcceptSocket() if handlerSocket.Connected then

lbConnections.Items.Add( _

handlerSocket.RemoteEndPoint.ToString() + _ "connected.")

SyncLock (Me)

alSockets.Add(handlerSocket) end SyncLock

thdstHandler = New ThreadStart(AddressOf _ handlerThread)

thdHandler = New Thread(thdstHandler) thdHandler.Start()

end if Loop

End sub

The remainder of the work is carried out in the handlerThread method.

This function finds the last used socket and then retrieves the stream from this socket. An array is allocated to the same size as the stream, and once the stream is fully received, its contents are copied into this array.

Once the connection closes, the data is written to file at c:\my

documents\upload.txt. It is important to have the lock() keyword around the lines of code associated with file access; otherwise, if two concurrent con-nections try to access the same file, the program will crash. The contents of the file are then displayed in the list box on-screen. The socket is then set to

null to remove it from memory. If this point were omitted, the array list would quickly fill with sockets that had lost connection with their clients.

Note that the constructor for TcpListener that takes only a single int

for a port number is now obsolete. To stop the compiler complaining about this line of code, simply call the constructor thus:

new TcpListener(IPAddress.Any,8080)

C#

public void handlerThread() {

Socket handlerSocket = (Socket)alSockets[alSockets.Count-1];

NetworkStream networkStream = new NetworkStream(handlerSocket);

int thisRead=0;

int blockSize=1024;

Byte[] dataByte = new Byte[blockSize];

lock(this) {

// Only one process can access // the same file at any given time

Stream fileStream = File.OpenWrite("c:\\my documents\

\upload.txt");

while(true) {

thisRead=networkStream.Read(dataByte,0,blockSize);

fileStream.Write(dataByte,0,thisRead);

if (thisRead==0) break;

}

fileStream.Close();

}

lbConnections.Items.Add("File Written");

handlerSocket = null;

}

VB.NET

Public Sub handlerThread() Dim handlerSocket As Socket

handlerSocket = alSockets(alSockets.Count - 1)

Dim networkStream As NetworkStream = New _ NetworkStream(handlerSocket)

Dim blockSize As Int16 = 1024 Dim thisRead As Int16

Dim dataByte(blockSize) As Byte SyncLock Me

' Only one process can access the ' same file at any given time Dim fileStream As Stream

fileStream = File.OpenWrite("c:\upload.txt") While (True)

thisRead = networkStream.Read(dataByte, _ 0, blockSize)

fileStream.Write(dataByte, 0, dataByte.Length) If thisRead = 0 Then Exit While

End While

fileStream.Close() End SyncLock

lbConnections.Items.Add("File Written") handlerSocket = Nothing

End Sub

As before, add the namespace references to the head of the code:

C#

using System.Threading;

using System.Net;

using System.Net.Sockets;

using System.Text;

using System.IO;

VB.NET

imports System.Threading imports System.Net

imports System.Net.Sockets imports System.Text

imports System.IO

To test the application, run the server application, and take note of the IP address displayed. Then, run the client application. Type the IP address into the box provided. Click on browse to select a file. Press send to transfer the

file. A file will soon appear on the server at c:\my documents\upload.txt, which is an exact copy of the file that was located on the client.

To further demonstrate this principle, you can use a telnet program to write text to c:\upload.txt remotely.

On Windows 95, 98, or ME machines, click Start→→Run, then type→→

Telnet. Click Connect→→→→Remote System. Type the server IP address into the host name textbox, and type 8080 into the port textbox. Press Con-nect. Type some text into the window, and when finished, press Connect, Disconnect. A file will soon appear on the server at c:\my documents\

upload.txt.

On Windows NT, 2000, and XP machines, click Start→→→→Run, then type

Telnet. Type Open 127.0.0.1 8080. Replace 127.0.0.1 with the IP address of your server, if you have two computers. Type some text into the window, and when finished, close the window. A file will soon appear on the server at c:\upload.txt.

Table 3.4 Significant members of the TcpListener class.

Method or Property Purpose

Constructor Initializes a new instance of the TcpListenerClient class. It may be used thus: new TcpListener(int). LocalEndpoint Gets the underlying EndPoint of the current

TcpListener. Returns EndPoint.

AcceptSocket() Accepts a pending connection request. Returns Socket.

AcceptTcpClient() Accepts a pending connection request. Returns TcpClient.

Pending() Determines if there are pending connection requests.

Returns Bool.

Start() Starts listening to network requests.

Stop() Closes the listener.

Active Gets a value that indicates whether TcpListener is actively listening for client connections. Returns Bool. Server Gets the underlying network socket. Returns Socket.

Ways have already been developed to send files through the Internet.

Anybody who has ever written a Web site would be familiar with programs such as cuteFTP and smartFTP, which do exactly what was demonstrated in the previous example, albeit with a much more flexible interface.

It is rarely a good idea to try to reinvent the wheel and develop a new way to send data through the Internet. The global standardization of proto-cols has made the Internet what it is today.

Table 3.4 shows the significant methods and properties for TcpListener.

In document in .NET (Page 86-94)