Previously, I used C ++ to implement the network port components on the windows platform. How do I do this in C? At first, I planned to use PInvoke to call the underlying Windows API to simulate the C ++ implementation, but the problem soon came out-the Unsafe pointer in C # Could not stably point to the first address of a buffer, that is to say, when garbage collection is performed, the value of our unsafe pointer may be invalid. Pin? I also thought about it, but the TCP receiving buffer in the lock address will greatly reduce the runtime efficiency. Is there no way? The essential idea of completing the port model is to disband "threads that start asynchronous operations" and "threads that provide services" (that is, worker threads. Isn't that enough? This is the essence.
We need several types of threads. First, we need a thread to receive TCP connection requests. This is called a listener thread. When a connection is successfully received, it sends a request to the connection to receive data asynchronously. Because it is an asynchronous operation, it will return immediately and then receive new connection requests, so the listening thread will run cyclically. It is worth noting that in the callback function of asynchronous reception, the data received should be processed and the port model should put the received data in the completed port queue, note: It is a queue.
The second thread type is the worker thread. The number of worker threads has an experience (number of CPUs × 2 + 2). Of course, the actual number depends on your application requirements. The job of the worker thread is to constantly retrieve data from the completion port queue and process it. If there is a reply, the reply is written to the corresponding connection.
As mentioned earlier, I plan not to use Pinvoke in C # To implement the port.. NET platform does not specifically complete the port Queue component or class, so we must first implement such a queue.
Well, Let's define the interface IRequestQueueManager, which is used to simulate the queue that completes the port.
The definition and default implementation are given below.
Using System;
Using System. Collections;
Namespace policiseserverbase. Network
{
/// <Summary>
/// IRequestQueueManager is used to simulate the queue that completes the port.
/// </Summary>
Public interface IRequestQueueManager: IRequestPusher
{
// Void Push (object package );
Object Pop ();
Void Clear ();
Int Length {get ;}
}
// Push a request packet to the queue
Public interface IRequestPusher
{
Void Push (object package );
}
/// <Summary>
/// Default Implementation of IRequestQueueManager
/// </Summary>
Public class RequestQueueManager: IRequestQueueManager
{
Private Queue queue = null;
Public RequestQueueManager ()
{
This. queue = new Queue ();
}
Public void Push (object package)
{
Lock (this. queue)
{
This. queue. Enqueue (package );
}
}
Public object Pop ()
{
Object package = null;
Lock (this. queue)
{
If (this. queue. Count> 0)
{
Package = this. queue. Dequeue ();
}
}
Return package;
}
Public void Clear ()
{
Lock (this. queue)
{
This. queue. Clear ();
}
}
Public int Length
{
Get
{
Return this. queue. Count;
}
}
}
}
On the basis of IRequestQueueManager, you can split the worker thread and the thread that starts asynchronous operations. Since the worker threads are only related to the port queue, I decided to encapsulate them together-IIOCPManager
To see how to implement the port class:
Using System;
Using System. Threading;
Namespace policiseserverbase. Network
{
/// <Summary>
/// IIOCPManager completes the port manager, mainly manages the worker threads and completes the port queue.
/// 2005.05.23
/// </Summary>
Public interface IIOCPManager: IRequestPusher
{
Void Initialize (IOCPPackageHandler I _packageHandler, int threadCount );
Void Start (); // Start the worker thread
Void Stop (); // exit the worker thread
Int WorkThreadCount {get ;}
Event CallBackPackageHandled PackageHandled;
}
// IOCPPackageHandler is used to process the package retrieved from the completion port queue
Public interface IOCPPackageHandler
{
Void HandlerPackage (object package); // It is generally implemented synchronously.
}
Public delegate void CallBackPackageHandled (object package );
/// <Summary>
/// IOCPManager is the default Implementation of IIOCPManager.
/// </Summary>
Public class IOCPManager: IIOCPManager
{
Private IRequestQueueManager requestQueueMgr = null;
Private IOCPPackageHandler packageHandler;
Private int workThreadCount = 0; // actual number of worker threads
Private int MaxThreadCount = 0;
Private bool stateIsStop = true;
Public IOCPManager ()
{
}
# Region ICPWorkThreadManager Member
Public event CallBackPackageHandled PackageHandled;
Public void Initialize (IOCPPackageHandler I _packageHandler, int threadCount)
{
This. requestQueueMgr = new RequestQueueManager ();
This. MaxThreadCount = threadCount;
This. packageHandler = I _packageHandler;
}
Public void Push (object package)
{
This. requestQueueMgr. Push (package );
}
Public void Start ()
{
If (! This. stateIsStop)
{
Return;
}
This. stateIsStop = false;
This. CreateWorkThreads ();
}
Public void Stop ()
{
If (this. stateIsStop)
{
Return;
}
This. stateIsStop = true;
// Wait until all worker threads end
Int count = 0;
While (this. workThreadCount! = 0)
{
If (count <10)
{
Thread. Sleep (200 );
}
Else
{
Throw new Exception ("WorkThread Not Terminated! ");
}
++ Count;
}
This. requestQueueMgr. Clear ();
}
Public int WorkThreadCount
{
Get
{
Return this. workThreadCount;
}
}
# Endregion
# Region CreateWorkThreads
Private void CreateWorkThreads ()
{
For (int I = 0; I <this. MaxThreadCount; I ++)
{
Thread t = new Thread (new ThreadStart (this. ServeOverLap ));
Interlocked. Increment (ref this. workThreadCount );
T. Start ();
}
}
# Endregion
# Region ServeOverLap worker thread
Private void ServeOverLap ()
{
While (! This. stateIsStop)
{
Object package = this. requestQueueMgr. Pop ();
&