[转载].NET中高效能的socket编程
异步服务器端的代码。
代码class AsynchronousIoServer
{
private Socket _serverSocket;
private int _port;
public AsynchronousIoServer(int port_num)
{
_port = port_num;
}
private class ConnectionInfo
{
public Socket Socket;
public byte[] Buffer;
}
private List<ConnectionInfo> _connections =
new List<ConnectionInfo>();
public void Start()
{
SetupServerSocket();
for (int i = 0; i < 10; i++)
_serverSocket.BeginAccept(
new AsyncCallback(AcceptCallback), _serverSocket);
}
private void SetupServerSocket()
{
IPHostEntry localMachineInfo =
Dns.GetHostEntry(Dns.GetHostName());
IPEndPoint myEndpoint = new IPEndPoint(
localMachineInfo.AddressList[0], _port);
_serverSocket = new Socket(myEndpoint.Address.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
_serverSocket.Bind(myEndpoint);
_serverSocket.Listen((int)SocketOptionName.MaxConnections);
}
private void AcceptCallback(IAsyncResult result)
{
ConnectionInfo connection = new ConnectionInfo();
try
{
Socket s = (Socket)result.AsyncState;
connection.Socket = s.EndAccept(result);
connection.Buffer = new byte[255];
lock (_connections) _connections.Add(connection);
connection.Socket.BeginReceive(connection.Buffer, 0,
connection.Buffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveCallback), connection);
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback),
result.AsyncState);
}
catch (SocketException exc)
{
CloseConnection(connection);
Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
}
catch (Exception exc)
{
CloseConnection(connection);
Console.WriteLine("Exception: " + exc);
}
}
private void ReceiveCallback(IAsyncResult result)
{
ConnectionInfo connection = (ConnectionInfo)result.AsyncState;
try
{
int bytesRead = connection.Socket.EndReceive(result);
if (0 != bytesRead)
{
lock (_connections)
{
foreach (ConnectionInfo conn in _connections)
{
if (connection != conn)
{
conn.Socket.Send(connection.Buffer, bytesRead,
SocketFlags.None);
}
}
}
connection.Socket.BeginReceive(connection.Buffer, 0,
connection.Buffer.Length, SocketFlags.None,
new AsyncCallback(ReceiveCallback), connection);
}
else CloseConnection(connection);
}
catch (SocketException exc)
{
CloseConnection(connection);
Console.WriteLine("Socket exception: " + exc.SocketErrorCode);
}
catch (Exception exc)
{
CloseConnection(connection);
Console.WriteLine("Exception: " + exc);
}
}
private void CloseConnection(ConnectionInfo ci)
{
ci.Socket.Close();
lock (_connections) _connections.Remove(ci);
}
}
socket客户端程序
服务器端已经反应灵敏,承载量也很大,现在需要一个高效的客户端程序。相比较而言,这个就相对简单了。在客户端程序里,首先要构造一个socket。可以选择让该socket与特定的地址和端口绑定;如果没有绑定的话,网络层就会提供一个它认为最好的地址与其绑定,同时还有一个特定的端口,这个端口号在1024到5000之间。
之所以需要绑定客户端程序的socket到一定的地址和端口,是因为,在一个有多个网卡的电脑终端上,而且每个网卡的地址不同,此时就可以选择客户端所要使用的网卡。
接下来就要使用socket连接服务器端。连接也可以采用同步或者异步的方式。当你需要连接到多个服务器端的时候,异步是非常有用的方法。每个socket不必等待前面的socket连接建立完成,程序可以同时开始多个连接。
即使服务器端可用,使用客户端连接的时候依然有可能遇到WSAECONNREFUSED 的错误,这在服务器端的队列已满的情况下有可能发生。在这种情况下,需要客户端每个一定时间尝试重新连接。
更过的改进
第一个方面是接收连接。有三种基本接受异步连接的方法:只接收连接,接收连接并接收连接发送的前几个字节的数据,用特殊提供的socket接收连接并接收连接发送的前几个字节的数据。.NET框架1.x版本里只能使用第一种方法,而在.NET2.0里面,三个方法都可以适用。
前面的服务器端的代码使用的第一种异步方法。只接收连接的好处在于可以将接收逻辑和消息处理逻辑隔离开来。这是一个非常简单的编程模型,但是并不能提供最好的性能。
接收连接并且接收数据方法的好处在于对第一个消息处理的性能的显著提升,这主要因为基本上不需要调用核心处理程序。当使用特定协议,并已知协议头的情况下,这种方法非常好。不好的地方在于只有数据接受完毕后采用调用BeginAccept 的处理函数,这可能产生一些程序根本都不知所以的连接。该方法与.NET2.0里面的Socket.Disconnect 方法相结合后有了很大优势。在以前的socket程序中,如果socket用不到了,就需要关闭并且删除该实例。有了Socket.Disconnect方法,你就可以重用该实例,这是通过程序本身的一个socket实例池实现的。在某些方案里面采用这种控制的话会导致性能的极大提升。
程序运行时,首先告诉操作系统监听连接,它就会保持一个特定长度的连接队列。假定最大数目为200,那就意味着如果有超过200个客户端同时连接时,队列之外的客户端会因为服务器端的拒接而发生WSAECONNREFUSED 错误。解决该问题的一个方案是使用多个接收处理。使用该方法就可以不断从排队连接里面接收处理连接,就算就在处理的连接正在处理尚未提交接收请求。这就大大增多了监听队列的厚度。
尽管如此,多异步操作是有代价的。连接处理增多就会导致性能的下降,而承载量的增大也是以性能的下降为代价的。