.NET Sockets I/O模型
word里面的文本贴进来,格式又乱了,大家将就看吧。等全文写完后,我会贴出PDF文档的。
1.1 .NET Sockets I/O模型
.NET Sockets I/O模型有三种:1.阻塞式I/O、2.选择(Select)I/O、3.异步I/O
1.1.1 阻塞式I/O模型
正如阻塞式I/O模型的名字,这种I/O模型在进行I/O操作时会阻塞调用者线程。阻塞式I/O是最简单最直接的I/O模型,使用阻塞式I/O模型时,用户按照顺序建立连接、发送数据、接收数据……每一步操作都需要进入等待状态。
一个典型的使用阻塞I/O模型的代码段如下所示(为了简单起见,我省略了异常处理):
using System;
using System.Text;
using System.Net.Sockets;
namespace NCindy.Articles.Samples
{
public class BlockingIODemo
{
static void
{
Console.WriteLine("Begin.");
try
{
Demo();
}
catch (Exception e)
{
Console.WriteLine(e);
}
Console.WriteLine("End.");
}
static void Demo()
{
//创建一个TCP socket
Socket sock = new Socket(
AddressFamily.InterNetwork, //地址族为IPv4
SocketType.Stream, //Socket类型为流类型,UDP为数据报类型(Dgram)
ProtocolType.Tcp);
//建立连接
sock.Connect("talk.google.com", 5222);
byte [] sendBuffer = Encoding.Default.GetBytes("Hello world.");
//发送数据
int sentBytes = sock.Send(sendBuffer);
Console.WriteLine("Sent bytes: " + sentBytes);
//分配一个k的接收缓冲区
byte [] recvBuffer = new byte[1024 * 4];
//接收数据
int recvBytes = sock.Receive(recvBuffer);
//将收到的字节数组转化成对象
string recvString = Encoding.Default.GetString(recvBuffer, 0, recvBytes);
Console.WriteLine("Received data: \r\n-----\r\n" + recvString);
//停止Socket的接收和发送
sock.Shutdown(SocketShutdown.Both);
//关闭Socket,释放系统资源
sock.Close();
}
}
}
这段代码按顺序做了这么几样工作:1.创建Socket对象;2.连接到远端服务;3.发送数据;4.接收数据。并且在整个执行过程中,阻塞了主线程的执行,直到Demo方法执行完成Main方法才继续执行。
与其它两种I/O模型相比,阻塞式I/O模型最大的优点在于使用简单,符合直觉的思维方式。缺点也很明显,每个线程只能同时为一个连接服务,如果有大量连接,就要启动大量线程,而大量线程的上下文切换对系统效率会是不小的影响。当然,阻塞式I/O模型配合线程池机制,在处理连接数量较少的情况下也可以达到非常理想的效率。
1.1.2 选择(Select)I/O模型
在阻塞式I/O模型中,每个线程同时只能处理一个连接。如果需要同时处理多个连接则需要多个线程。为了在一个线程中同时处理多个Socket连接,我们可以使用Select模型。这个模型主要思路为:使用Socket的Select静态方法来从多个Sockets中将可以进行某种操作的Sockets查询出来然后逐个进行处理。
一个典型的使用选择I/O模型的代码段如下所示(为了简单起见,我省略了异常处理):
using System;
using System.Net.Sockets;
using System.Collections;
using System.Net;
using System.IO;
using System.Threading;
namespace NCindy.Articles.Samples
{
class SelectIOSample
{
static bool stopEchoServer = false;
static void Main(string[] args)
{
Console.WriteLine("Begin");
Thread thread = new Thread(new ThreadStart(EchoServerThreadProc));
thread.Start();
Console.WriteLine("Press 'Enter' to quit.");
Console.ReadLine();
stopEchoServer = true;
//等待DoEchoServer线程退出
thread.Join(2000);
Console.WriteLine("End");
}
private static void EchoServerThreadProc()
{
EchoServer echoServer = new EchoServer();
try
{
echoServer.Start();
while (!stopEchoServer)
{
//选择出可以进行读操作的Sockets进行处理
echoServer.ProcessRead();
//选择出可以进行写操作的Sockets进行处理
echoServer.ProcessWrite();
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
finally
{
echoServer.Stop();
}
}
}
internal class ClientData
{
private MemoryStream memStream = new MemoryStream();
public void Write(byte[] data, int count)
{
memStream.Write(data, 0, count);
}
public byte[] Read()
{
byte[] buf = new byte[memStream.Position];
int count = (int)memStream.Position;
memStream.Position = 0;
memStream.Read(buf, 0, count);
memStream.Position = 0;
return buf;
}
}
internal class EchoServer
{
private readonly Socket listener;
private readonly ArrayList clients;
private readonly Hashtable clientsDataMap;
public EchoServer()
{
clients = new ArrayList();
clientsDataMap = new Hashtable();
listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream,
ProtocolType.Tcp);
}
public void Start()
{
//监听本机所有IP的端口
listener.Bind(new IPEndPoint(IPAddress.Any, 6666));
//开始进行监听
//backlog是指最大允许的未决连接数量。
//未决连接是指服务器已经接受到远端的连接请求,但是应用还没有调用accept接受的连接
//如果未决连接超过这个backlog指定的数量,接下来连接会被直接拒绝
listener.Listen(200); //
}
public void Stop()
{
//关闭监听socket
listener.Close();
//关闭所有的客户端sockets
foreach (Socket s in this.clients)
{
s.Shutdown(SocketShutdown.Both);
s.Close();
}
}
private void ProcessError(ArrayList errorList)
{
if (errorList.Count == 0)
{
return;
}
foreach (Socket s in errorList)
{
Console.WriteLine(string.Format("{0} error.", s.RemoteEndPoint));
this.clients.Remove(s);
}
}
public void ProcessWrite()
{
ArrayList errorList = new ArrayList(this.clients);
ArrayList writeList = new ArrayList(this.clients);
if (writeList.Count == 0)
{
return;
}
int begin = Environment.TickCount;
Socket.Select(null, writeList, errorList, 1000 * 1000);
int end = Environment.TickCount;
System.Diagnostics.Trace.WriteLine(string.Format("select write use {0}ms", end - begin));
this.ProcessError(errorList);
foreach (Socket s in writeList)
{
try
{
if (this.clientsDataMap.ContainsKey(s))
{
ClientData cd = this.clientsDataMap[s] as ClientData;
byte[] sendBuf = cd.Read();
s.Send(sendBuf);
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
public void ProcessRead()
{
try
{
ArrayList errorList = new ArrayList(this.clients);
ArrayList readList = new ArrayList(this.clients);
readList.Add(this.listener); //Listener Socket的读操作相当于有尚未accept的连接进入
Socket.Select(readList, null, errorList, 1000 * 1000);
this.ProcessError(errorList);
//分配接收缓冲
byte[] recvBuffer = new byte[1024];
foreach (Socket s in readList)
{
//如果listener可以读,说明可以无阻塞的执行accept
if (s == this.listener)
{
Socket incomingSocket = s.Accept();
Console.WriteLine(string.Format("{0} connected.",
incomingSocket.RemoteEndPoint));
this.clients.Add(incomingSocket);
}
else
{
int recvCount = s.Receive(recvBuffer);
//如果接收到的字节数为,则为远端连接shutdown
if (recvCount == 0)
{
Console.WriteLine(string.Format("{0} disconnected.",
s.RemoteEndPoint));
this.clients.Remove(s);
}
//取出连接对应的客户数据对象
ClientData cd;
if (this.clientsDataMap.ContainsKey(s))
{
cd = this.clientsDataMap[s] as ClientData;
}
else
{
cd = new ClientData();
this.clientsDataMap.Add(s, cd);
}
//将读取到的数据放入客户数据对象中,等待可以发送的时候发送
cd.Write(recvBuffer, recvCount);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
}
1.1.3 异步I/O模型
异步I/O模型是.NET中最有效的I/O模型,因为在Windows NT 4和以上的系统中.NET异步I/O在内部使用了IO完成端口。
posted on 2006-10-31 18:40 iceboundrock 阅读(2927) 评论(7) 编辑 收藏 举报