在网络游戏中,多路复用的方式是最常见的服务器端解决方案,程序结构如图:
程序代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Net.Sockets;
//保存客户端信息的类
class ClientState
{
public Socket socket;
public byte[] readbuff = new byte[1024];
}
class Server
{
//新建一个字典用于socket和状态查询
static Dictionary<Socket,ClientState> clients=new Dictionary<Socket,ClientState>();
static void Main(string[] args)
{
//建立Socket并绑定
Socket 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("服务器启动成功!");
//新建一个check列表用来保存socket信息
List<Socket> checkRead = new List<Socket>();
//循环调用检测
while(true)
{
//把所有的socket信息存入checkRead表中
checkRead.Clear();
checkRead.Add(listenfd);
foreach(ClientState s in clients.Values)
{
checkRead.Add(s.socket);
}
//Select检测列表中Socket状态
Socket.Select(checkRead, null, null, 1000);
//判断Socket状态
foreach(Socket s in checkRead)
{
//判断监听Socket是否可读,可读表明有客户端连接,调用Accept回应
//否则处理消息
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 e)
{
clientfd.Close();
clients.Remove(clientfd);
Console.WriteLine("receive socket error" + e.ToString());
return false;
}
//客户端关闭,receive返回值为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[] sendbyte = System.Text.Encoding.Default.GetBytes(sendstr);
foreach(ClientState s in clients.Values)
{
s.socket.Send(sendbyte);
}
return true;
}
}