在網絡遊戲中,多路複用的方式是最常見的服務器端解決方案,程序結構如圖:
程序代碼:
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;
}
}