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;
|
int m_numConnectedSockets;
|
||||||
|
|
||||||
|
bool shutdown = false;
|
||||||
IListenerDispatcher _requestDispatcher;
|
IListenerDispatcher _requestDispatcher;
|
||||||
Socket listenSocket;
|
Socket listenSocket;
|
||||||
|
|
||||||
|
@ -38,6 +39,7 @@ namespace FastTunnel.Core
|
||||||
|
|
||||||
public void Listen(IListenerDispatcher requestDispatcher)
|
public void Listen(IListenerDispatcher requestDispatcher)
|
||||||
{
|
{
|
||||||
|
shutdown = false;
|
||||||
_requestDispatcher = requestDispatcher;
|
_requestDispatcher = requestDispatcher;
|
||||||
|
|
||||||
listenSocket.Listen(100);
|
listenSocket.Listen(100);
|
||||||
|
@ -45,7 +47,30 @@ namespace FastTunnel.Core
|
||||||
StartAccept(null);
|
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)
|
if (acceptEventArg == null)
|
||||||
{
|
{
|
||||||
|
@ -78,6 +103,7 @@ namespace FastTunnel.Core
|
||||||
// Accept the next connection request
|
// Accept the next connection request
|
||||||
StartAccept(e);
|
StartAccept(e);
|
||||||
|
|
||||||
|
// 将此客户端交由Dispatcher进行管理
|
||||||
_requestDispatcher.Dispatch(accept);
|
_requestDispatcher.Dispatch(accept);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -90,21 +116,5 @@ namespace FastTunnel.Core
|
||||||
{
|
{
|
||||||
ProcessAccept(e);
|
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