上篇文章介绍了自己开发的基于TCP协议的服务器,也具有一定的扩展性;既然做了TCP的,那自然就得把UDP的也研究下,因此本篇文章将探讨下基于UDP协议的服务器。
废话少说,下面开始介绍思路,由于UDP是无连接的,所以相对于TCP协议的服务器就简单很多,不需要去监听客户端,也不需要对会话进行维护,只需要对数据进行相应的处理就可以;因此整体思路就很明显了,用异步方式来接收客户端的数据,将数据放入队列中,用一个线程组来处理队列中的数据,每次从队列中取一个数据包进行处理,对于线程组中线程数可根据实际情况设定,首先给出成员定义:
View Code
private Socket server = null;
private bool serverStart = false;//服务器是否启动
private int serverPort = 3130;//服务器端口
private int clientPort = 3131;//远程客户端端口
private IPAddress localIp;//服务器绑定的IP地址
private string brodcastIp;//广播IP地址
private byte[] tempReceiveData = null;//临时数据接收器
private EndPoint tempRemotePoint = null;//远程客户端
private Queue<BufferData> receivedBuffer;//数据缓冲区
private Thread[] threadDataHandler;//数据处理线程
private int threadDataHandlerNum = 1;//数据处理线程数
private IDataHandler iRceivedDataHandler = null;//数据处理接口
private Mutex m_ServerMutex; // 只能有一个服务器
启动服务器后,绑定端口,启动数据处理线程组,开始异步接收数据:
View Code
/// <summary>
/// 启动服务器
/// </summary>
public void Start()
{
if (this.serverStart)
{
return;//服务器已经启动
}
this.server = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
this.tempReceiveData = new byte[this.server.ReceiveBufferSize];
IPAddress[] ip = Dns.GetHostAddresses(Dns.GetHostName());
IPEndPoint addr = new IPEndPoint(ip[0], this.serverPort);
this.localIp = ip[0];
//算出广播IP地址
string str = this.localIp.ToString();
int len = str.LastIndexOf('.');
str = str.Substring(0, len + 1);
this.brodcastIp = str + "255";
//绑定IP,端口
this.server.Bind(addr);
this.serverStart = true;
this.threadDataHandler = new Thread[this.threadDataHandlerNum];
for (int i = 0; i < this.threadDataHandlerNum; i++)
{
this.threadDataHandler[i] = new Thread(new ThreadStart(this.HandlerReceivedData));
this.threadDataHandler[i].Start();
}
this.AsyncBeginReceive();//开始接收数据
}
异步接收数据及其回调函数:
View Code
/// <summary>
/// 异步接收数据
/// </summary>
private void AsyncBeginReceive()
{
try
{
if (this.serverStart)
{
this.server.BeginReceiveFrom(this.tempReceiveData, 0, this.server.ReceiveBufferSize, SocketFlags.None, ref this.tempRemotePoint, this.AsyncReceiveCallback, this);
}
}
catch { }
}
/// <summary>
/// 异步接收数据的回调函数
/// </summary>
/// <param name="iar"></param>
private void AsyncReceiveCallback(IAsyncResult iar)
{
try
{
iar.AsyncWaitHandle.Close();
if (this.serverStart)
{
AsyncBeginReceive();
int byteReadLen = this.server.EndReceiveFrom(iar, ref this.tempRemotePoint);
byte[] temp = new byte[byteReadLen];
Array.Copy(this.tempReceiveData, temp, byteReadLen);
BufferData buffer = new BufferData(this.tempRemotePoint, temp);
this.receivedBuffer.Enqueue(buffer);
}
}
catch
{ }
}
异步发送数据及其回调函数:
View Code
/// <summary>
/// 开始异步发送数据
/// </summary>
/// <param name="buffer"></param>
private void AsyncBeginSend(IPAddress ip, string data)
{
try
{
byte[] byteData = Encoding.ASCII.GetBytes(data);
EndPoint iep = (EndPoint)new IPEndPoint(ip, this.clientPort);
this.server.BeginSendTo(byteData, 0, byteData.Length, SocketFlags.None, iep, new AsyncCallback(this.AsyncSendCallback), this);
}
catch { }
}
/// <summary>
/// 开始异步发送数据
/// </summary>
/// <param name="ip"></param>
/// <param name="data"></param>
private void AsyncBeginSend(string ip, int port, string data)
{
try
{
byte[] byteData = Encoding.ASCII.GetBytes(data);
EndPoint iep = (EndPoint)new IPEndPoint(IPAddress.Parse(ip), port);
this.server.BeginSendTo(byteData, 0, byteData.Length, SocketFlags.None, iep, new AsyncCallback(this.AsyncSendCallback), this);
}
catch { }
}
/// <summary>
/// 异步发送数据的回调
/// </summary>
/// <param name="iar"></param>
private void AsyncSendCallback(IAsyncResult iar)
{
try
{
this.server.EndSendTo(iar);
iar.AsyncWaitHandle.Close();
}
catch
{ }
}
缓冲区数据处理函数:
View Code
/// <summary>
/// 处理缓冲区中的数据
/// </summary>
/// <summary>
/// 处理缓冲区中的数据
/// </summary>
private void HandlerReceivedData()
{
while (this.serverStart)
{
lock (this.receivedBuffer)
{
if (this.receivedBuffer.Count == 0) continue;
BufferData buffer = this.receivedBuffer.Dequeue();
if (this.iRceivedDataHandler != null) this.iRceivedDataHandler.ReceivedDataHandler(buffer);
}
}
}
数据处理线程组中的每个线程都会调用该函数,所以加个锁是必要的,对于数据的解析,给出了相应的数据接口,只要实现该接口即可实现其数据的处理,如向数据库存储,对数据的判断,向客户端回复等,都可以在该接口中去实现,因此具有一定的灵活性。
数据接口很简单:
public interface IDataHandler
{
/// <summary>
/// 数据处理接口
/// </summary>
/// <param name="buffer"></param>
void ReceivedDataHandler(BufferData buffer);
}
对于该接口,朋友们可以自己根据需要进行扩展。
指定客户端发送,广播,多播的实现:
View Code
/// <summary>
/// 向指定客户端发送数据
/// </summary>
/// <param name="ip">客户端IP</param>
/// <param name="data">数据</param>
public void SendToClient(IPAddress ip, string data)
{
if (this.serverStart)
{
this.server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
this.AsyncBeginSend(ip, data);
}
}
/// <summary>
/// 向指定客户端发送数据
/// </summary>
/// <param name="ip"></param>
/// <param name="data"></param>
public void SendToClient(string ip, string data)
{
this.SendToClient(IPAddress.Parse(ip), data);
}
/// <summary>
/// 向指定IP,指定端口的客户机发送数据
/// </summary>
/// <param name="ip">Ip地址</param>
/// <param name="port">端口号</param>
/// <param name="data">数据</param>
public void SendToClient(string ip, int port, string data)
{
if (this.serverStart)
{
this.server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
this.AsyncBeginSend(ip, port, data);
}
}
/// <summary>
/// 广播
/// </summary>
/// <param name="data"></param>
public void Broadcast(string data)
{
this.SendToClient(this.brodcastIp, data);
}
/// <summary>
/// 在指定端口上广播数据
/// </summary>
/// <param name="data">数据</param>
/// <param name="port">端口号</param>
public void Broadcast(string data,int port)
{
this.SendToClient(this.brodcastIp, port, data);
}
/// <summary>
/// 多路广播
/// </summary>
/// <param name="ipAddress"></param>
public void Multicast(string data)
{
if (this.serverStart)
{
this.server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
this.AsyncBeginSend(IPAddress.Broadcast, data);
}
}
/// <summary>
/// 指定端口进行多路广播
/// </summary>
/// <param name="data"></param>
/// <param name="port"></param>
public void Multicast(string data, int port)
{
if (this.serverStart)
{
this.server.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
this.AsyncBeginSend(IPAddress.Broadcast.ToString(), port, data);
}
}
希望以上代码会对有需要的朋友有些帮助,也希望和大家一起共同进步,呵呵,加油! 源代码下载: