#unity/日常积累
反馈
命名空间:
程序集:
System.Net.Sockets.dll
Source:
表示异步套接字操作。
public class SocketAsyncEventArgs : EventArgs, IDisposable
继承
SocketAsyncEventArgs
实现
下面的代码示例为使用 SocketAsyncEventArgs 类的套接字服务器实现连接逻辑。 接受连接后,从客户端读取的所有数据都发送回客户端。 继续读取并回显到客户端模式,直到客户端断开连接。 此示例使用的 BufferManager 类显示在 方法的代码示例 SetBuffer(Byte[], Int32, Int32) 中。 此示例中使用的 SocketAsyncEventArgsPool 类显示在构造函数的代码示例 SocketAsyncEventArgs 中。
// 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);
// 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
SocketAsyncEventArgs acceptEventArg = new SocketAsyncEventArgs();
acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
StartAccept(acceptEventArg);
//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)
{
// loop while the method completes synchronously
bool willRaiseEvent = false;
while (!willRaiseEvent)
{
m_maxNumberAcceptedClients.WaitOne();
// socket must be cleared since the context object is being reused
acceptEventArg.AcceptSocket = null;
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);
// Accept the next connection request
StartAccept(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();
readEventArgs.UserToken = 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);
}
}
// 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
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);
Socket socket = (Socket)e.UserToken;
bool willRaiseEvent = 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
Socket socket = (Socket)e.UserToken;
// read the next block of data send from the client
bool willRaiseEvent = socket.ReceiveAsync(e);
if (!willRaiseEvent)
{
ProcessReceive(e);
}
}
else
{
CloseClientSocket(e);
}
}
private void CloseClientSocket(SocketAsyncEventArgs e)
{
Socket socket = (Socket)e.UserToken;
// close the socket associated with the client
try
{
socket.Shutdown(SocketShutdown.Send);
}
// throws if client process has already closed
catch (Exception) { }
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);
}
}
类 SocketAsyncEventArgs 是 类的一组增强的一部分,这些增强 System.Net.Sockets.Socket 功能提供可由专用的高性能套接字应用程序使用的替代异步模式。 此类专为需要高性能的网络服务器应用程序而设计。 例如,当接收大量数据) 时,应用程序可以独占或仅在目标热区域使用增强型异步模式 (。
这些增强功能的主要功能是避免在大容量异步套接字 I/O 期间重复分配和同步对象。 类当前实现的 System.Net.Sockets.Socket Begin/End 设计模式要求为每个异步套接字操作分配对象 System.IAsyncResult 。
在新的 System.Net.Sockets.Socket 类增强中,异步套接字操作由应用程序分配和维护的可重用 SocketAsyncEventArgs 对象描述。 高性能套接字应用程序非常清楚必须维持的重叠套接字操作的数量。 该应用程序可创建所需的 SocketAsyncEventArgs 对象数量。 例如,如果服务器应用程序需要随时有 15 个未完成的套接字接受操作以支持传入客户端连接速率,则它可以为此分配 15 个可重用 SocketAsyncEventArgs 对象。
使用此类执行异步套接字操作的模式包括以下步骤:
分配一个新的 SocketAsyncEventArgs 上下文对象,或从应用程序池中获取一个空闲对象。
将上下文对象的属性设置为即将 (完成回调方法执行的操作、数据缓冲区、缓冲区中的偏移量以及要传输的最大数据量,例如) 。
调用适当的套接字方法 (xxxAsync) 以启动异步操作。
如果异步套接字方法 (xxxAsync) 返回 true,请在回调中查询完成状态的上下文属性。
如果异步套接字方法 (xxxAsync) 返回 false,则操作同步完成。 可查询上下文属性获取操作结果。
重新使用上下文进行另一项操作,将其放回池中,或放弃它。
新的异步套接字操作上下文对象的生存期由应用程序代码和异步 I/O 引用的引用决定。 作为参数提交给异步套接字操作方法之一后,应用程序不必保留对异步套接字操作上下文对象的引用。 完成回调返回之前,应用程序会继续引用它。 但是,应用程序最好保留对上下文的引用,以便将来的异步套接字操作可以重复使用该引用。