mirror of
https://github.com/FastTunnel/FastTunnel.git
synced 2025-02-08 02:39:29 +08:00
解决访问已释放的资源问题
This commit is contained in:
parent
7c90e04489
commit
92ba5cc825
|
@ -20,6 +20,7 @@ namespace FastTunnel.Core
|
|||
|
||||
int m_numConnectedSockets;
|
||||
|
||||
bool shutdown = false;
|
||||
IListenerDispatcher _requestDispatcher;
|
||||
Socket listenSocket;
|
||||
|
||||
|
@ -38,6 +39,7 @@ namespace FastTunnel.Core
|
|||
|
||||
public void Listen(IListenerDispatcher requestDispatcher)
|
||||
{
|
||||
shutdown = false;
|
||||
_requestDispatcher = requestDispatcher;
|
||||
|
||||
listenSocket.Listen(100);
|
||||
|
@ -45,7 +47,30 @@ namespace FastTunnel.Core
|
|||
StartAccept(null);
|
||||
}
|
||||
|
||||
public void StartAccept(SocketAsyncEventArgs acceptEventArg)
|
||||
public void ShutdownAndClose()
|
||||
{
|
||||
if (shutdown)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (listenSocket.Connected)
|
||||
{
|
||||
listenSocket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
shutdown = true;
|
||||
listenSocket.Close();
|
||||
Interlocked.Decrement(ref m_numConnectedSockets);
|
||||
}
|
||||
}
|
||||
|
||||
private void StartAccept(SocketAsyncEventArgs acceptEventArg)
|
||||
{
|
||||
if (acceptEventArg == null)
|
||||
{
|
||||
|
@ -78,6 +103,7 @@ namespace FastTunnel.Core
|
|||
// Accept the next connection request
|
||||
StartAccept(e);
|
||||
|
||||
// 将此客户端交由Dispatcher进行管理
|
||||
_requestDispatcher.Dispatch(accept);
|
||||
}
|
||||
else
|
||||
|
@ -90,21 +116,5 @@ namespace FastTunnel.Core
|
|||
{
|
||||
ProcessAccept(e);
|
||||
}
|
||||
|
||||
public void ShutdownAndClose()
|
||||
{
|
||||
try
|
||||
{
|
||||
listenSocket.Shutdown(SocketShutdown.Both);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
}
|
||||
finally
|
||||
{
|
||||
listenSocket.Close();
|
||||
Interlocked.Decrement(ref m_numConnectedSockets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,252 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace FastTunnel.Core
|
||||
{
|
||||
// Implements the connection logic for the socket server.
|
||||
// After accepting a connection, all data read from the client
|
||||
// is sent back to the client. The read and echo back to the client pattern
|
||||
// is continued until the client disconnects.
|
||||
class Server
|
||||
{
|
||||
private int m_numConnections; // the maximum number of connections the sample is designed to handle simultaneously
|
||||
private int m_receiveBufferSize;// buffer size to use for each socket I/O operation
|
||||
BufferManager m_bufferManager; // represents a large reusable set of buffers for all socket operations
|
||||
const int opsToPreAlloc = 2; // read, write (don't alloc buffer space for accepts)
|
||||
Socket listenSocket; // the socket used to listen for incoming connection requests
|
||||
// pool of reusable SocketAsyncEventArgs objects for write, read and accept socket operations
|
||||
SocketAsyncEventArgsPool m_readWritePool;
|
||||
int m_totalBytesRead; // counter of the total # bytes received by the server
|
||||
int m_numConnectedSockets; // the total number of clients connected to the server
|
||||
Semaphore m_maxNumberAcceptedClients;
|
||||
|
||||
// Create an uninitialized server instance.
|
||||
// To start the server listening for connection requests
|
||||
// call the Init method followed by Start method
|
||||
//
|
||||
// <param name="numConnections">the maximum number of connections the sample is designed to handle simultaneously</param>
|
||||
// <param name="receiveBufferSize">buffer size to use for each socket I/O operation</param>
|
||||
public Server(int numConnections, int receiveBufferSize)
|
||||
{
|
||||
m_totalBytesRead = 0;
|
||||
m_numConnectedSockets = 0;
|
||||
m_numConnections = numConnections;
|
||||
m_receiveBufferSize = receiveBufferSize;
|
||||
// allocate buffers such that the maximum number of sockets can have one outstanding read and
|
||||
//write posted to the socket simultaneously
|
||||
m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToPreAlloc,
|
||||
receiveBufferSize);
|
||||
|
||||
m_readWritePool = new SocketAsyncEventArgsPool(numConnections);
|
||||
m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
|
||||
}
|
||||
|
||||
// Initializes the server by preallocating reusable buffers and
|
||||
// context objects. These objects do not need to be preallocated
|
||||
// or reused, but it is done this way to illustrate how the API can
|
||||
// easily be used to create reusable objects to increase server performance.
|
||||
//
|
||||
public void Init()
|
||||
{
|
||||
// Allocates one large byte buffer which all I/O operations use a piece of. This gaurds
|
||||
// against memory fragmentation
|
||||
m_bufferManager.InitBuffer();
|
||||
|
||||
// preallocate pool of SocketAsyncEventArgs objects
|
||||
SocketAsyncEventArgs readWriteEventArg;
|
||||
|
||||
for (int i = 0; i < m_numConnections; i++)
|
||||
{
|
||||
//Pre-allocate a set of reusable SocketAsyncEventArgs
|
||||
readWriteEventArg = new SocketAsyncEventArgs();
|
||||
readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
|
||||
readWriteEventArg.UserToken = new AsyncUserToken();
|
||||
|
||||
// assign a byte buffer from the buffer pool to the SocketAsyncEventArg object
|
||||
m_bufferManager.SetBuffer(readWriteEventArg);
|
||||
|
||||
// add SocketAsyncEventArg to the pool
|
||||
m_readWritePool.Push(readWriteEventArg);
|
||||
}
|
||||
}
|
||||
|
||||
// Starts the server such that it is listening for
|
||||
// incoming connection requests.
|
||||
//
|
||||
// <param name="localEndPoint">The endpoint which the server will listening
|
||||
// for connection requests on</param>
|
||||
public void Start(IPEndPoint localEndPoint)
|
||||
{
|
||||
// create the socket which listens for incoming connections
|
||||
listenSocket = new Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
|
||||
listenSocket.Bind(localEndPoint);
|
||||
// start the server with a listen backlog of 100 connections
|
||||
listenSocket.Listen(100);
|
||||
|
||||
// post accepts on the listening socket
|
||||
StartAccept(null);
|
||||
|
||||
//Console.WriteLine("{0} connected sockets with one outstanding receive posted to each....press any key", m_outstandingReadCount);
|
||||
Console.WriteLine("Press any key to terminate the server process....");
|
||||
Console.ReadKey();
|
||||
}
|
||||
|
||||
// Begins an operation to accept a connection request from the client
|
||||
//
|
||||
// <param name="acceptEventArg">The context object to use when issuing
|
||||
// the accept operation on the server's listening socket</param>
|
||||
public void StartAccept(SocketAsyncEventArgs acceptEventArg)
|
||||
{
|
||||
if (acceptEventArg == null)
|
||||
{
|
||||
acceptEventArg = new SocketAsyncEventArgs();
|
||||
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
|
||||
}
|
||||
else
|
||||
{
|
||||
// socket must be cleared since the context object is being reused
|
||||
acceptEventArg.AcceptSocket = null;
|
||||
}
|
||||
|
||||
m_maxNumberAcceptedClients.WaitOne();
|
||||
bool willRaiseEvent = listenSocket.AcceptAsync(acceptEventArg);
|
||||
if (!willRaiseEvent)
|
||||
{
|
||||
ProcessAccept(acceptEventArg);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is the callback method associated with Socket.AcceptAsync
|
||||
// operations and is invoked when an accept operation is complete
|
||||
//
|
||||
void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
|
||||
{
|
||||
ProcessAccept(e);
|
||||
}
|
||||
|
||||
private void ProcessAccept(SocketAsyncEventArgs e)
|
||||
{
|
||||
Interlocked.Increment(ref m_numConnectedSockets);
|
||||
Console.WriteLine("Client connection accepted. There are {0} clients connected to the server",
|
||||
m_numConnectedSockets);
|
||||
|
||||
// Get the socket for the accepted client connection and put it into the
|
||||
//ReadEventArg object user token
|
||||
SocketAsyncEventArgs readEventArgs = m_readWritePool.Pop();
|
||||
((AsyncUserToken)readEventArgs.UserToken).Socket = e.AcceptSocket;
|
||||
|
||||
// As soon as the client is connected, post a receive to the connection
|
||||
bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(readEventArgs);
|
||||
if (!willRaiseEvent)
|
||||
{
|
||||
ProcessReceive(readEventArgs);
|
||||
}
|
||||
|
||||
// Accept the next connection request
|
||||
StartAccept(e);
|
||||
}
|
||||
|
||||
// This method is called whenever a receive or send operation is completed on a socket
|
||||
//
|
||||
// <param name="e">SocketAsyncEventArg associated with the completed receive operation</param>
|
||||
void IO_Completed(object sender, SocketAsyncEventArgs e)
|
||||
{
|
||||
// determine which type of operation just completed and call the associated handler
|
||||
switch (e.LastOperation)
|
||||
{
|
||||
case SocketAsyncOperation.Receive:
|
||||
ProcessReceive(e);
|
||||
break;
|
||||
case SocketAsyncOperation.Send:
|
||||
ProcessSend(e);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
|
||||
}
|
||||
}
|
||||
|
||||
// This method is invoked when an asynchronous receive operation completes.
|
||||
// If the remote host closed the connection, then the socket is closed.
|
||||
// If data was received then the data is echoed back to the client.
|
||||
//
|
||||
private void ProcessReceive(SocketAsyncEventArgs e)
|
||||
{
|
||||
// check if the remote host closed the connection
|
||||
AsyncUserToken token = (AsyncUserToken)e.UserToken;
|
||||
if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
|
||||
{
|
||||
//increment the count of the total bytes receive by the server
|
||||
Interlocked.Add(ref m_totalBytesRead, e.BytesTransferred);
|
||||
Console.WriteLine("The server has read a total of {0} bytes", m_totalBytesRead);
|
||||
|
||||
//echo the data received back to the client
|
||||
e.SetBuffer(e.Offset, e.BytesTransferred);
|
||||
bool willRaiseEvent = token.Socket.SendAsync(e);
|
||||
if (!willRaiseEvent)
|
||||
{
|
||||
ProcessSend(e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseClientSocket(e);
|
||||
}
|
||||
}
|
||||
|
||||
// This method is invoked when an asynchronous send operation completes.
|
||||
// The method issues another receive on the socket to read any additional
|
||||
// data sent from the client
|
||||
//
|
||||
// <param name="e"></param>
|
||||
private void ProcessSend(SocketAsyncEventArgs e)
|
||||
{
|
||||
if (e.SocketError == SocketError.Success)
|
||||
{
|
||||
// done echoing data back to the client
|
||||
AsyncUserToken token = (AsyncUserToken)e.UserToken;
|
||||
// read the next block of data send from the client
|
||||
bool willRaiseEvent = token.Socket.ReceiveAsync(e);
|
||||
if (!willRaiseEvent)
|
||||
{
|
||||
ProcessReceive(e);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CloseClientSocket(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseClientSocket(SocketAsyncEventArgs e)
|
||||
{
|
||||
AsyncUserToken token = e.UserToken as AsyncUserToken;
|
||||
|
||||
// close the socket associated with the client
|
||||
try
|
||||
{
|
||||
token.Socket.Shutdown(SocketShutdown.Send);
|
||||
}
|
||||
// throws if client process has already closed
|
||||
catch (Exception) { }
|
||||
token.Socket.Close();
|
||||
|
||||
// decrement the counter keeping track of the total number of clients connected to the server
|
||||
Interlocked.Decrement(ref m_numConnectedSockets);
|
||||
|
||||
// Free the SocketAsyncEventArg so they can be reused by another client
|
||||
m_readWritePool.Push(e);
|
||||
|
||||
m_maxNumberAcceptedClients.Release();
|
||||
Console.WriteLine("A client has been disconnected from the server. There are {0} clients connected to the server", m_numConnectedSockets);
|
||||
}
|
||||
}
|
||||
|
||||
internal class AsyncUserToken
|
||||
{
|
||||
public System.Net.Sockets.Socket Socket { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<ShowAllFiles>true</ShowAllFiles>
|
||||
</PropertyGroup>
|
||||
</Project>
|
Loading…
Reference in New Issue
Block a user