在实际的应用中,如果全部采用异步的操作来,会增加代码的复杂程度,某些时候使用Poll/Select来实现单线程多路的I/O复用会更合适一些
一、Poll
原型函数
public bool Poll ( int microSeconds, SelectMode mode )
1:客户端
private void Update()
{
if(socket == null)
{
return;
}
if (socket.Poll(0, SelectMode.SelectRead))
{
byte[] readBuff = new byte[1024];
int count = socket.Receive(readBuff);
string recvStr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
text.text = recvStr;
}
}
注:
使用socket.Poll方法检查套接字是否有可读数据,超时时间为0,即立即返回 ,如果套接字有可读数据,则执行后续操作
设置为不阻塞模式(microSeconds 为0)。比起异步程序,代码简单的多,这是Read接收,还有发送SelectWrite
2:服务端
服务端一直检测监听Socket各个客户端Socket状态,如果收到消息就分别处理
class Main
{
static Socket listenfd;
static Dictionary<Socket, ClientState> clients = new Dictionary<Socket, ClientState>();
public static void Main(string[] args)
{
listenfd = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAdr = IPAddress.Parse("127.0.0.1");
IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);
listenfd.Bind(ipEp);
listenfd.Listen(0);
Console.WriteLine("[服务器]启动成功");
while (true)
{
//检查listenfd
if (listenfd.Poll(0, SelectMode.SelectRead))
{
ReadListenfd(listenfd);
}
//检查clientfd
foreach (ClientState s in clients.Values)
{
Socket clientfd = s.socket;
if (clientfd.Poll(0, SelectMode.SelectRead))
{
if (!ReadClientfd(clientfd))
{
break;
}
}
}
//防止CPU占用过高
System.Threading.Thread.Sleep(1);
}
}
public static void ReadListenfd(Socket listenfd)
{
Console.WriteLine("Accept");
//该方法就是如果接收到了客户端,就返回客户端
Socket clientfd = listenfd.Accept();
ClientState state = new ClientState();
state.socket = clientfd;
clients.Add(clientfd, state);
}
public static bool ReadClientfd(Socket clientfd)
{
ClientState state = clients[clientfd];
//接收
int count = 0;
try
{
count = clientfd.Receive(state.readBuff);
}
//抛出异常
catch (SocketException ex)
{
clientfd.Close();
clients.Remove(clientfd);
Console.WriteLine("Receive SocketException " +
ex.ToString());
return false;
}
//客户端数量为0
if (count == 0)
{
clientfd.Close();
clients.Remove(clientfd);
Console.WriteLine("Socket Close");
return false;
}
//广播
string recvStr = System.Text.Encoding.Default.GetString(state.readBuff,0, count);
Console.WriteLine("Receive" + recvStr);
string sendStr = clientfd.RemoteEndPoint.ToString() + ":" + recvStr;
byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);
//循环给每个客户端发送消息
foreach (ClientState cs in clients.Values)
{
cs.socket.Send(sendBytes);
}
return true;
}
}
注:
若没有收到客户端数据,服务端也一直在循环,浪费了CPU。Poll客户端也是同理,没有数据的时候还总在Update中检测数据
二、Select
原型函数
public static void Select( IList checkRead, IList check Write, IList checkError, int microSeconds )
checkRead:检测是否有可读Socket列表
checkWrite:检测是否有可写Socket列表
checkError:检测是否有出错
1:客户端
List<Socket> checkRead = new List<Socket>();
private void Update()
{
if (socket == null)
{
return;
}
checkRead.Clear();
checkRead.Add(socket);
Socket.Select(checkRead, null, null, 0);
foreach (Socket socket in checkRead)
{
byte[] readBuff = new byte[1024];
int count = socket.Receive(readBuff);
string recvStr = System.Text.Encoding.Default.GetString(readBuff, 0, count);
text.text = recvStr;
}
}
注:
由于程序在Update中不停地检测数据,性能较差。商业上为了做 到性能上的极致,大多使用异步(或使用多线程模拟异步程序)。
2:服务端
class Class
{
static Socket listenfd;
static Dictionary<Socket, ClientState> clients = new Dictionary<Socket, ClientState>();
public static void Main(string[] args)
{
listenfd = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
IPAddress ipAdr = IPAddress.Parse("127.0.0.1");
IPEndPoint ipEp = new IPEndPoint(ipAdr, 8888);
listenfd.Bind(ipEp);
listenfd.Listen(0);
Console.WriteLine("[服务器]启动成功");
List<Socket> checkRead = new List<Socket>();
while (true)
{
//填充checkRead列表
checkRead.Clear();
checkRead.Add(listenfd);
foreach (ClientState s in clients.Values)
{
checkRead.Add(s.socket);
}
//select
Socket.Select(checkRead, null, null, 1000);
//检查可读对象
foreach (Socket s in checkRead)
{
if (s == listenfd)
{
ReadListenfd(s);
}
else
{
ReadClientfd(s);
}
}
}
}
public static void ReadListenfd(Socket listenfd)
{
Console.WriteLine("Accept");
//该方法就是如果接收到了客户端,就返回客户端
Socket clientfd = listenfd.Accept();
ClientState state = new ClientState();
state.socket = clientfd;
clients.Add(clientfd, state);
}
public static bool ReadClientfd(Socket clientfd)
{
ClientState state = clients[clientfd];
//接收
int count = 0;
try
{
count = clientfd.Receive(state.readBuff);
}
//抛出异常
catch (SocketException ex)
{
clientfd.Close();
clients.Remove(clientfd);
Console.WriteLine("Receive SocketException " +
ex.ToString());
return false;
}
//客户端数量为0
if (count == 0)
{
clientfd.Close();
clients.Remove(clientfd);
Console.WriteLine("Socket Close");
return false;
}
//广播
string recvStr = System.Text.Encoding.Default.GetString(state.readBuff, 0, count);
Console.WriteLine("Receive" + recvStr);
string sendStr = clientfd.RemoteEndPoint.ToString() + ":" + recvStr;
byte[] sendBytes = System.Text.Encoding.Default.GetBytes(sendStr);
//循环给每个客户端发送消息
foreach (ClientState cs in clients.Values)
{
cs.socket.Send(sendBytes);
}
return true;
}