在实际的应用中,如果全部采用异步的操作来,会增加代码的复杂程度,某些时候使用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){//检查listenfdif (listenfd.Poll(0, SelectMode.SelectRead)){ReadListenfd(listenfd);}//检查clientfdforeach (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;}//客户端数量为0if (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);}//selectSocket.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;}//客户端数量为0if (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;}