王朝网络
分享
 
 
 

Asynchronous Socket Utility Classes – Part II

王朝other·作者佚名  2006-01-08
宽屏版  字体: |||超大  

Asynchronous Socket Utility Classes – Part IIAuthor: William Kennedy

Date Added: 31st Mar 2003

Type: Tutorial

Rating:

This is a printable version of "Asynchronous Socket Utility Classes – Part II". For the complete online version, please visit http://www.devarticles.com/content.php?articleId=493

Page 1: Introduction

A socket is a connection oriented communication channel that is established between two computers. In the OSI network model, http://www2.rad.com/networks/1994/osi/layers.htm, sockets are implemented at the session layer. This means that sockets can be used with any transport protocol like TCP or UDP. Session layer protocols, like sockets, require that a computer must establish a connection to any computer it wished to communicate with. Once a connection is established, messages can be passed between both computers.

A socket connection can be thought of as a virtual pipe between two computers. Each pipe provides an exclusive communication channel. If multiple computers connect to the same computer, each will have their own pipe. It is easy to identify which computer sent a message because each message flows through that computers pipe. This means an application must be looking for messages on every pipe that exists. The most common way to do this is to spawn a thread for each pipe.

This thread just waits for messages to arrive on their respective pipe and then processes the message. This design works well until you have hundreds of pipes. Each pipe requires a thread and each thread has the potential to be in a running state at the same time. This solution is not scalable and will cause performance problems as the number of pipes and messages going through those pipes increase.

Asynchronous sockets provide a way to have a pool of threads process messages for all established pipes, instead of having a thread per pipe. This solution is scalable and improves performance. The .NET framework provides nice low-level classes to help us add asynchronous socket support into our applications. However, the implementation still lacks an organizational structure that is easy to use.

For more information on socket programming look at the follow sites:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconnon-blockingserversocketexample.asp

http://squirl.nightmare.com/medusa/async_sockets.html

When adding asynchronous socket support into our applications it can seem initially confusing, and at times overwhelming. We have to understand how the asynchronous socket framework is implemented. We need to understand threading and how it is applied. Most importantly we need to have some understanding of how to integrate this into our application.

To help us through these issues, I have built two classes. These classes provide reusable interfaces, which cleanly and quickly add asynchronous socket support into our applications. Both of these classes take care of the low-level details that allow us to only have to worry about coding the business problem at hand. Low level details such as implementing the asynchronous socket thread pool, identifying which messages came from which computers, and handling any error conditions that occur in a consistent way.

These classes will save us hours of time developing and debugging our asynchronous socket applications. In this article I will build the server socket class and sample application to test it.

System Requirements

A basic understanding of C# is required to follow through the examples and the classes. Basic concepts of type, properties, threading, synchronization, and delegates are required. It is important that you have completed part I of this article. In that article we build the CSocketServer class.

Defining The Problem

High-level class needs to be developed that abstract the .NET framework classes used to develop asynchronous socket applications. This new class needs to abstract the details behind asynchronous socket programming. This class must be easy to use and reliable. Any classes that are developed need to be reusable so they can be shared among many different types of applications.

Defining The Solution

A single class will be developed. We will develop a class that abstracts the intricacies of building asynchronous socket servers. This class will allow us to be ignorant of the details and yet allow us to access the underlining framework if we need to.

Page 2: Component Design and Coding

CSocketServer

Now we will build a class that provides socket server support.

Setup CSocketServer Class

Open the UtilSocket.cs file created during Part I of this article and add the follow class just after the CSocketClient class inside the namespace.

} // End of CSocketClient

//========================================================================

/// <summary> This class abstracts a socket server </summary>

public class CSocketServer

{

// Delegate Method Types

// Private Properties

// Public Properties

// Constructor, Finalize, Dispose

//********************************************************************

/// <summary> Constructor for client support </summary>

public CSocketServer()

{

}

// Private Methods

// Public Methods

}

} // End of Namespace

Delegate Method Types Section

We need to add four delegate method types.

The first three delegate method types are identical to the delegate method types we added to the CSocketClient class and perform the same function. The new delegate method type is for the Accept Handler. We will call the method referenced by a property of this type when a client socket connection request is accepted.

/// <summary> DelType: Called when a message is extracted from the socket </summary>

public delegate void MESSAGE_HANDLER(CSocketClient pSocket, Int32 iNumberOfBytes);

/// <summary> DelType: Called when a socket connection is closed </summary>

public delegate void CLOSE_HANDLER(CSocketClient pSocket);

/// <summary> DelType: Called when a socket error occurs </summary>

public delegate void ERROR_HANDLER(CSocketClient pSocket, Exception pException);

/// <summary> DelType: Called when a socket connection is accepted </summary>

public delegate void ACCEPT_HANDLER(CSocketClient pSocket);

Private Properties Section

We need to add eleven private properties

In the CSocketClient class, we used the TcpClient class to provide the support we needed to establish socket connections to a server. In this class we will create a property using the TcpListener class. This property will provide the support we need to listen for and accept those client connections requests coming from the TcpClient class or similar technologies.

private TcpListener m_pTcpListener;

/// <summary> RefType: A TcpListener object to accept socket connections </summary>

private TcpListener GetTcpListener { get { return m_pTcpListener; } set { m_pTcpListener = value; } }

The CSocketServer class will be responsible for listening and accepting client socket connection requests via the TcpListener property. We need a thread to perform this responsibility. This property is a thread object to manage the Accept thread.

private Thread m_pAcceptThread;

/// <summary> RetType: A thread to process accepting socket connections </summary>

private Thread GetAcceptThread { get { return m_pAcceptThread; } set { m_pAcceptThread = value; } }

Once a connection request is accepted the Accept thread, the thread will instantiate a CSocketClient object. The new CSocketClient object will be used to encapsulate the new socket connection. The CSocketServer will maintain an array of the CSocketClient objects it instantiates and will be responsible for the lifetime of these objects.

private CSocketClient[] m_pSocketClientList;

/// <summary> RefTypeArray: An Array of SocketClient objects </summary>

private CSocketClient[] GetSocketClientList { get { return m_pSocketClientList; } set { m_pSocketClientList = value; } }

The application developer will decide the maximum number of client socket connections that will be accepted. This property maintains this number. It is used later in the constructor to instantiate the SocketClientList property array.

private Int32 m_iMaxClientConnections;

/// <summary> SimType: Maximum number of client connections </summary>

public Int32 GetMaxClientConnections { get { return m_iMaxClientConnections; } set { m_iMaxClientConnections = value; } }

Since the Accept thread is instantiating CSocketClient objects, it must pass the required arguments to its constructor. This property is maintained by the CSocketServer class so the Accept thread can pass this value to the constructor of the CSocketClient objects it creates.

private Int32 m_iSizeOfRawBuffer;

/// <summary> SimType: Size of the raw buffer for received socket data </summary>

private Int32 GetSizeOfRawBuffer { get { return m_iSizeOfRawBuffer; } set { m_iSizeOfRawBuffer = value; } }

The following delegate method type properties are maintained because they need to be passed to the constructor of the CSocketClient objects created by the Accept thread. The Accept Handler property is used by the Accept thread and not passed to the instantiated CSocketClient object. When the Accept thread accepts a client socket connection request and instantiates a CSocketClient object, the Accept thread will call the Accept Handler method. This is when the application developer can perform any application specific actions required when a client connection is accepted.

private MESSAGE_HANDLER m_pfnMessageHandler;

/// <summary> DelType: A reference to a user supplied function to be called when a socket message arrives </summary>

private MESSAGE_HANDLER GetMessageHandler { get { return m_pfnMessageHandler; } set { m_pfnMessageHandler = value; } }

private CLOSE_HANDLER m_pfnCloseHandler;

/// <summary> DelType: A reference to a user supplied function to be called when a socket connection is closed </summary>

private CLOSE_HANDLER GetCloseHandler { get { return m_pfnCloseHandler; } set { m_pfnCloseHandler = value; } }

private ERROR_HANDLER m_pfnErrorHandler;

/// <summary> DelType: A reference to a user supplied function to be called when a socket error occurs </summary>

private ERROR_HANDLER GetErrorHandler { get { return m_pfnErrorHandler; } set { m_pfnErrorHandler = value; } }

private ACCEPT_HANDLER m_pfnAcceptHandler;

/// <summary> DelType: A reference to a user supplied function to be called when a socket connection is accepted </summary>

private ACCEPT_HANDLER GetAcceptHandler { get { return m_pfnAcceptHandler; } set { m_pfnAcceptHandler = value; } }

There will be a need for synchronization because of the SocketClientList array. Different threads will need to add and remove CSocketClient object references from this array at possibly the same time. This object property will be used in a Monitor object to provide critical section support to the required member methods.

private Object m_pSocketListCS;

/// <summary> RefType: A synchronization object to protect the class state </summary>

private Object GetSocketListCS { get { return m_pSocketListCS; } set { m_pSocketListCS = value; } }

The last private property is the dispose flag property. The finalize method will use this flag to determine if the class has already been disposed or not.

private Boolean m_bDisposeFlag;

/// <summary> SimType: Flag to indicate if the class is disposing </summary>

private Boolean IsDisposed { get { return m_bDisposeFlag; } set { m_bDisposeFlag = value; } }

Public Properties Section

We need to add three public properties.

This property maintains the ipaddress of a local nic card used to listen for client socket connections. If you want to listen on multiple nic cards you will need to instantiate multiple CSocketServer objects, one for each card. This property can store a host name or an ipaddress. If this value is a host name it must be resolvable by a DNS lookup.

private String m_strIpAddress;

/// <summary> RefType: The IpAddress to either connect to or listen on </summary>

public String GetIpAddress { get { return m_strIpAddress; } set { m_strIpAddress = value; } }

This property maintains the port the server is to listen for client socket connections on. You must make sure the port is currently not in use. To manually check if a port is in use, run the “netstat –a” command from the command line.

private Int16 m_iPort;

/// <summary> SimType: The Port to either connect to or listen on </summary>

public Int16 GetPort { get { return m_iPort; } set { m_iPort = value; } }

This property is for the application developer. If the application developer would like to store some state associated with the socket server, he can use this property.

private Object m_pUserArg;

/// <summary> RefType: A reference to a user defined object to be passed through the handler functions </summary>

public Object GetUserArg { get { return m_pUserArg; } set { m_pUserArg = value; } }

Constructor, Finalize, and Dispose Section

In the constructor, all we need to do is instantiate the object for the critical section property and set the dispose property flag to false.

//********************************************************************

/// <summary> Constructor </summary>

public CSocketServer()

{

// Create the critical section object

GetSocketListCS = new Object();

// Init the dispose flag

IsDisposed = false;

}

The finalize method verifies that the object instance has not been disposed already. If the application developer remembers properly to call dispose, there is nothing for finalize to do. This finalize function only exists as a safe guard.

//********************************************************************

/// <summary> Finalize </summary>

~CSocketServer()

{

// If this object has not been disposed yet

if (!IsDisposed)

Stop();

}

The dispose method sets the dispose flag to true so the finalize function does not try to dispose the object instance again. All that needs to be done is to make sure the Accept thread is terminated. The Stop method performs that action as you will see later in the public methods section.

//********************************************************************

/// <summary> Dispose function to shutdown the SocketManager </summary>

public void Dispose()

{

try

{

// Mark the object as disposed

IsDisposed = true;

// Stop the server if the thread is running

if (GetAcceptThread != null)

Stop();

}

catch

{

}

}

Private Methods Section

We need to add two private methods.

The LocateSocketIndex method is used to locate an empty slot in the SocketClientList property array. This is used by the Accept thread to save a reference to a newly instantiated CSocketClient object.

In the LocateSocketIndex method, a local simple type variable called iSocket is set to the max number of client connections the CSocketServer object is allowed to accept. This is because the method returns the value of iSocket at the end of the method. If no slots are available, we need to return the max client connections value to indicate the maximum number of client connections currently exist.

Next, we wait to enter the Monitor object. We only want one thread accessing the SocketClientList property array at a time. In the call to Monitor.Enter, we pass the SocketList object property to protect the SocketClientList property array. Now we safely search the SocketClientList property array for an empty slot. Once we find an empty slot, we break out of the loop. After the loop, we call Monitor.Exit passing the SocketList object property again and return the value of iSocket.

Notice how the calls to Monitor.Enter and Monitor.Exit are outside the try and catch statements. This is done to make sure that if we successfully enter the critical section we absolutely exit from it. If you do not call Monitor.Exit after passing through Monitor.Enter, no other threads will ever get through the critical section again. Those threads waiting to pass through the Monitor.Enter call will block forever. It is important to always make sure Monitor.Exit is called and only called once. I have found this to be the most efficient and safest way to guarantee critical sections get released properly.

//********************************************************************

/// <summary> Function to locate an empty slot in the client socket list array </summary>

/// <returns> Will return the index slot or user defined MaxClientConnections if none found </returns>

private Int32 LocateSocketIndex()

{

Int32 iSocket = GetMaxClientConnections;

Monitor.Enter(GetSocketListCS);

try

{

// Find an empty slot in the list

for (iSocket = 0; iSocket < GetMaxClientConnections; ++iSocket)

if (GetSocketClientList[iSocket] == null)

break;

}

catch

{

}

Monitor.Exit(GetSocketListCS);

return iSocket;

}

The AcceptThread method is spawned as a worker thread. It performs most of the work for the CSocketServer class. The job of the AcceptThread method is to listen for incoming client socket connection requests and accept those requests. Once a client socket connection request is accepted by the call to the TcpListener AcceptSocket method, the Accept thread instantiates a CSocketClient object.

The new CSocketClient object exists to encapsulate the accepted socket connection. We then store a reference to the CSocketClient object in the SocketClientList property array. Once these activities are complete, then we call the Accept Handler. This allows the application developer to be notified a client socket connection request has been accepted.

In both of the catch statements, we call the Error Handler. This notifies the application developer to be notified about an error accepting a socket client connection. In the catch statement that catches the System.Net.Sockets.SocketException object, we check for error code 10004.

This error code means that a socket blocking call was cancelled. We get this error when the TcpListener property is stopped. In this case, we do not call the Error Handler since this is a known error that is unavoidable. In both catch statements, we close the socket client connection if we accepted the connection before the error.

//********************************************************************

/// <summary> Function to process and accept socket connection requests </summary>

private void AcceptThread()

{

Socket pClientSocket = null;

try

{

// Create a new TCPListner and start it up

GetTcpListener = new TcpListener(Dns.Resolve(GetIpAddress).AddressList[0],GetPort);

GetTcpListener.Start();

for (;;)

{

// If a client connects, accept the connection

pClientSocket = GetTcpListener.AcceptSocket();

if (pClientSocket.Connected)

{

// Locate a socket index

Int32 iSocketIndex = LocateSocketIndex();

// If we got a valid index

if (iSocketIndex != GetMaxClientConnections)

{

// Create a SocketClient object

GetSocketClientList[iSocketIndex] = new CSocketClient(this,

pClientSocket, // The socket object for the connection

iSocketIndex, // The index into the SocketClientList

pClientSocket.RemoteEndPoint.ToString().Substring(0,15), // The IpAddress of the client

GetPort, // The port the client connected to

GetSizeOfRawBuffer, // The size of the byte array for storing messages

GetUserArg, // Application developer state

new CSocketClient.MESSAGE_HANDLER(GetMessageHandler), // Application developer Message Handler

new CSocketClient.CLOSE_HANDLER(GetCloseHandler), // Application developer Close Handler

new CSocketClient.ERROR_HANDLER(GetErrorHandler)); // Application developer Error Handler

// Call the Accept Handler

GetAcceptHandler(GetSocketClientList[iSocketIndex]);

}

else

{

// Call the Error Handler

GetErrorHandler(null, new Exception("Unable To Accept Socket Connection"));

// Close the socket connection

pClientSocket.Close();

}

}

}

}

catch (System.Net.Sockets.SocketException e)

{

// Did we stop the TCPListener

if (e.ErrorCode != 10004)

{

// Call the error handler

GetErrorHandler(null, e);

// Close the socket down if it exists

if (pClientSocket != null)

if (pClientSocket.Connected)

pClientSocket.Close();

}

}

catch (Exception e)

{

// Call the error handler

GetErrorHandler(null, e);

// Close the socket down if it exists

if (pClientSocket != null)

if (pClientSocket.Connected)

pClientSocket.Close();

}

}

Public Methods Section

We need to add one public method.

The RemoveSocket method is used to remove a reference of a CSocketClient object from the SocketClientList property array. A reference to a CSocketClient object is passed to the RemoveSocket method. We first need to check to make sure the reference to the passed CSocketClient object is valid. We do this by comparing the reference passed into RemoveSocket with the reference located in the SocketClientList property array. We determine which slot to look at in the SocketClientList array by using the slot the passed CSocketClient object says it’s located in.

If these references match, we de-reference the object from the SocketClientList property array. You may have noticed that the CSocketClient class currently does not have a public property called GetSocketListIndex. We will be adding the public property to the CSocketClient class later. Again the critical section is used to protect the SocketClientList property array. The calls to Monitor.Enter and Monitor.Exit are outside the try and catch statements.

//********************************************************************

/// <summary> Funciton to remove a socket from the list of sockets </summary>

/// <param name="iSocketListIndex"> SimType: The index of the socket to remove </param>

public void RemoveSocket(CSocketClient pSocketClient)

{

Monitor.Enter(GetSocketListCS);

try

{

// Is the supplied CSocketClient object valid

if (pSocketClient == GetSocketClientList[pSocketClient.GetSocketListIndex])

{

// Set the slot to null

GetSocketClientList[pSocketClient.GetSocketListIndex] = null;

}

}

catch

{

}

Monitor.Exit(GetSocketListCS);

}

CSocketClient Updates

Now we will add some new properties and methods to the CSocketClient class to support the CSocketServer class.

Private Properties Section

We need to add one private property.

This property maintains a reference back to the owning CSocketServer object. We need this reference to call the CSocketServer RemoveSocket method from the CSocketClient Dispose method.

private CSocketServer m_pSocketServer;

/// <summary> RefType: The SocketServer for this socket object </summary>

private CSocketServer GetSocketServer { get { return m_pSocketServer; } set { m_pSocketServer = value; } }

Public Properties Section

We need to add two public properties.

This property maintains the index where this object is located in the CSocketServer SocketClientList array.

private Int32 m_iSocketListIndex;

/// <summary> SimType: Index into the Socket List Array </summary>

public Int32 GetSocketListIndex { get { return m_iSocketListIndex; } set { m_iSocketListIndex = value; } }

This property maintains a reference to the socket object created by the TcpListener property when the CSocketServer Accept thread accepts a client socket connection request. This property will be passed to the NetworkStream property instead of the TcpClient property when the CSocketServer class instantiates a CSocketClient object.

private Socket m_pClientSocket;

/// <summary> RefType: The socket for the client connection </summary>

public Socket GetClientSocket { get { return m_pClientSocket; } set { m_pClientSocket = value; } }

Constructor, Finalize, and Dispose Section

We need to add a new constructor method to support the CSocketServer class. This overloaded constructor method takes 10 arguments. The XML documentation for the constructor describes the arguments. In the constructor, we are setting the class properties that should not change during the lifetime of an object instance of this class.

In this version of the constructor method, we need to do all of the things we did as in the first version plus a few new things. First, we need to save the reference to the CSocketServer object that created this CSocketClient object. Next, we save the reference to the socket for the accepted connection and the index where this object can be located in the CSocketServer SocketClientList array. Now we need to save the ipaddress and port of the client that connected to us.

In the CSocketClient Connect method we made the following call.

GetNetworkStream = GetTcpClient.GetStream();

This associated the new socket connection we established with the NetworkStream property so we could process socket messages asynchronously. The Connect method will never be called in this context because the connection is already established. In this case, we need to associate the socket being passed in with the NetworkStream property.

GetNetworkStream = new NetworkStream(GetClientSocket);

This statement above is used here in the constructor to associate the socket being passed in with the NetworkStream property.

The last thing to add is the socket options. Since the Connect method will never be called, we need to set the socket options here. We set all the same options we did in connect.

//********************************************************************

/// <summary> Constructor for SocketServer Suppport </summary>

/// <param name="pSocketServer"> RefType: A Reference to the parent SocketServer </param>

/// <param name="pClientSocket"> RetType: The Socket object we are encapsulating </param>

/// <param name="iSocketListArray"> SimType: The index of the SocketServer Socket List Array </param>

/// <param name="strIpAddress"> RetType: The IpAddress of the remote server </param>

/// <param name="iPort"> SimType: The Port of the remote server </param>

/// <param name="pfnMessageHandler"> DelType: Reference to the user defined message handler function </param>

/// <param name="pfnCloseHandler"> DelType: Reference to the user defined close handler function </param>

/// <param name="pfnErrorHandler"> DelType: Reference to the user defined error handler function </param>

/// <param name="iSizeOfRawBuffer"> SimType: The size of the raw buffer </param>

/// <param name="pUserArg"> RefType: A Reference to the Users arguments </param>

public CSocketClient(CSocketServer pSocketServer, Socket pClientSocket, Int32 iSocketListArray, String strIpAddress, Int16 iPort,

Int32 iSizeOfRawBuffer, Object pUserArg, MESSAGE_HANDLER pfnMessageHandler, CLOSE_HANDLER pfnCloseHandler,

ERROR_HANDLER pfnErrorHandler)

{

// Create the raw buffer

GetSizeOfRawBuffer = iSizeOfRawBuffer;

GetRawBuffer = new Byte[GetSizeOfRawBuffer];

// Save the user argument

GetUserArg = pUserArg;

// Set the handler functions

GetMessageHandler = pfnMessageHandler;

GetCloseHandler = pfnCloseHandler;

GetErrorHandler = pfnErrorHandler;

// Set the async socket function handlers

GetCallbackReadFunction = new AsyncCallback(ReceiveComplete);

GetCallbackWriteFunction = new AsyncCallback(SendComplete);

// Init the dispose flag

IsDisposed = false;

// Set reference to SocketServer

GetSocketServer = pSocketServer;

// Init the socket references

GetClientSocket = pClientSocket;

GetSocketListIndex = iSocketListArray;

// Set the Ipaddress and Port

GetIpAddress = strIpAddress;

GetPort = iPort;

// Init the NetworkStream reference

GetNetworkStream = new NetworkStream(GetClientSocket);

// Set these socket options

GetClientSocket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.ReceiveBuffer, 1048576);

GetClientSocket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.SendBuffer, 1048576);

GetClientSocket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.DontLinger, 1);

GetClientSocket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Tcp, System.Net.Sockets.SocketOptionName.NoDelay, 1);

// Wait for a message

Receive();

}

We need to make one change to the Dispose method. We need to add the call to the CSocketServer RemoveSocket method.

// Remove the socket from the list

if (GetSocketServer != null)

GetSocketServer.RemoveSocket(this);

This is added after the catch statement to guarantee the call is made. This will remove the reference to this object from the CSocketServer SocketClientList array.

//********************************************************************

/// <summary> Dispose </summary>

public void Dispose()

{

try

{

// Flag that dispose has been called

IsDisposed = true;

// Disconnect the client from the server

Disconnect();

}

catch

{

}

// Remove the socket from the list

if (GetSocketServer != null)

GetSocketServer.RemoveSocket(this);

}

The last change is to the Disconnect method. Here we need to add the following code.

if (GetClientSocket != null)

GetClientSocket.Close();

GetClientSocket = null;

This code will close the socket and remove the reference this class has to the memory so garbage collection can clean up.

//********************************************************************

/// <summary> Function used to disconnect from the server </summary>

public void Disconnect()

{

// Close down the connection

if (GetNetworkStream != null)

GetNetworkStream.Close();

if (GetTcpClient != null)

GetTcpClient.Close();

if (GetClientSocket != null)

GetClientSocket.Close();

// Clean up the connection state

GetClientSocket = null;

GetNetworkStream = null;

GetTcpClient = null;

}

Page 3: Sample Application Update

Now it is time to test our CSocketServer class. Open the class1.cs file and go to the bottom. Add a new method called MessageHandlerServer. This method is called when we receive a message from a socket client connection. In this method we will send back to the client a HTTP HTML command.

//********************************************************************

/// <summary> Called when a message is extracted from the socket </summary>

/// <param name="pSocket"> The SocketClient object the message came from </param>

/// <param name="iNumberOfBytes"> The number of bytes in the RawBuffer inside the SocketClient </param>

static public void MessageHandlerServer(CSocketClient pSocket, Int32 iNumberOfBytes)

{

try

{

// Find a complete message

String strMessage = System.Text.ASCIIEncoding.ASCII.GetString(pSocket.GetRawBuffer, 0, iNumberOfBytes);

Console.WriteLine(strMessage);

// Send the following HTTP command back

String strServerResponse = "HTTP/1.1 200 OK\n" +

"Date: Tue, 18 Feb 2003 18:47:39 GMT\n" +

"Server: Apache/1.3.27 (Unix) (Red-Hat/Linux) mod_perl/1.24_01 PHP/4.2.2 FrontPage/5.0.2 mod_ssl/2.8.12 OpenSSL/0.9.6b\n" +

"Last-Modified: Sat, 25 Jan 2003 21:45:30 GMT\n" +

"ETag: \"2201ab-11cd-3e33057a\"\n" +

"Accept-Ranges: bytes\n" +

"Content-Length: 53\n" +

"Keep-Alive: timeout=15, max=100\n" +

"Connection: Keep-Alive\n" +

"Content-Type: text/html\n\n" +

"<html> <body> <h1> Hello World! </h1> </body> </html>\n";

pSocket.Send(strServerResponse);

}

catch (Exception pException)

{

Console.WriteLine(pException.Message);

}

}

Add the AcceptHandler next. This is allow us to be notified when a connection is established to our server.

//********************************************************************

/// <summary> Called when a socket connection is accepted </summary>

/// <param name="pSocket"> The SocketClient object the message came from </param>

static public void AcceptHandler(CSocketClient pSocket)

{

Console.WriteLine("Accept Handler");

Console.WriteLine("IpAddress: " + pSocket.GetIpAddress);

}

The TestServer method will be used to start the server. We will listen for connections on port 9000 using the network card configured under our machine name.

//********************************************************************

/// <summary> Function to test the CSocketServer class </summary>

static void TestServer()

{

try

{

// Instantiate a CSocketServer object

CSocketServer pSocketServer = new CSocketServer();

// Start listening for connections

pSocketServer.Start(System.Environment.MachineName, 9000, 1024, 10240, null,

new CSocketServer.MESSAGE_HANDLER(MessageHandlerServer),

new CSocketServer.ACCEPT_HANDLER(AcceptHandler),

new CSocketServer.CLOSE_HANDLER(CloseHandler),

new CSocketServer.ERROR_HANDLER(ErrorHandler));

Console.WriteLine("Waiting for a client connection on Machine: {0} Port: {1}", System.Environment.MachineName, 9000);

// Stay here until you are ready to shutdown the server

Console.ReadLine();

pSocketServer.Dispose();

}

catch (Exception pException)

{

Console.WriteLine(pException.Message);

}

}

In the Main method, comment out the call to TestClient and add the call to TestServer. Now we are ready to start our server and test it.

//*******************************************************************

/// <summary> Function to test the CSocketClient class </summary>

static void Main(string[] args)

{

// Test the CSocketClient class

// TestClient();

// Test the CSocketClient class

TestServer();

}

Start the server and launch your browser. Our server is listening for connections on our local machine on port 9000. You need the machine name or ipaddress of our local machine. In the address bar type the following: http://machinename:9000 . This will have the browser connect to our server on port 9000 and our server will send the browser back the HTML statement that will display Hello World on the browser.

Page 4: Review

So now we have the ability to add socket client and server support to our applications easily and quickly. In the client sample application, all we needed to do was to instantiate a CSocketClient object. Then we can decide which server we want to connect to and code the handler functions appropriately.

In the server sample application, all we needed to do was to instantiate a CSocketServer object. Then we can decide which ipaddress and port to listen for socket client requests and code the handler functions appropriately.

For more great programming articles, please visit http://www.devarticles.com. If you have an opinion or question about this article, then please post it at the devArticles developer forum, which is located at http://www.devarticles.com/forum

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
>>返回首页<<
推荐阅读
 
 
频道精选
 
静静地坐在废墟上,四周的荒凉一望无际,忽然觉得,凄凉也很美
© 2005- 王朝网络 版权所有