对于基于socket的udp协议通讯,丢包问题大家应该都见怪不怪了,但我们仍然希望在通讯方面使用Udp协议通讯,因为它即时,消耗资源少,响应迅速,灵活性强无需向Tcp那样建立连接消耗很长的时间等等很有优势的理由让我们对Udp通讯寄予了厚望。但它也存在一个不好的特点,经常丢包是时常发生的事。可能各位大侠已经有了很好的解决方案,本人在这也只是本着大家共同学习的目的,提供自己的解决方式。
解决思路:模拟tcp三次握手协议,通过使用Timer定时器监视发送请求后接受数据的时间,如果一段时间内没有接受到数据包则判定丢包,并重新发送本次请求。
下面我们将通过在客户端向服务器端发送文件以及请求下载文件…将该例子是如何解决Udp丢包的问题。
个人优化:基于上章项目的Tcp协议下通讯,模拟聊天的同时发送文件。我们开辟两个端口,一个基于Tcp协议下进行聊天通讯,一个基于Udp协议下传送文件通讯。
项目大致思路如下:
发送文件:1.tcp客户端(文件发送端)先在本端初始化Udp服务端,并绑定好相应的端口,并将Ip地址以及端口号发送到tcp服务器端(文件接收端)。
2.tcp服务端(文件接收端)接收到发送文件请求后,初始化Udp客户端,并向指定的Udp服务端发送已准备完毕信息。
3.Udp服务器端接收到Udp客户端已准备好的信息后,初始化要发送文件对象,获取文件的基本参数(文件名、文件大小、数据大小、数据包总数)。
4.Udp客户端收到文件的基本参数后想Udp服务端发送开始发送文件,以及此时接受到文件之后的偏移量,告诉Udp服务端应该从文件的哪个部分发送数据。此时启动Timer定时器。
5.Udp服务器端收到文件继续发送的请求后,设置文件的偏移量,然后发送对应的数据包。此时关闭Timer定时器。
6.循环4-5过程直到文件发送完毕后,Udp客户端向Udp服务端发送文件接受完毕消息,并与3秒后关闭Udp客户端套接字。
7.Udp接受到文件接受完毕的消息后,关闭该套接字。
8.Udp客户端一段时间没接受到消息则出发Timer的定时触发事件,重新发送本次请求。
接收文件:1.tcp客户端(文件发送端)先在本端初始化Udp服务端,并绑定好相应的端口,并将Ip地址以及端口号发送到tcp服务器端(文件接收端)。
2.tcp服务端(文件接收端)接收到发送文件请求后,初始化Udp客户端,并根据文件名初始化要发送文件对象,获取文件的基本参数(文件名、文件大小、数据大小、数据包总数),并将此信息转成协议信息发送到Udp服务端。
后面的过程与上类似,我就不再马字了…
下面的过程主要针对发送文件进行讲解:
对于文件信息以及各种其他确认请求通过自定义协议发送后并解析的情况,在上一章都已经展示给大家看了。
Udp服务端以及Udp客户端继承UdpCommon公用抽象类,主要存放两端共同所需对象及方法:
/// <summary>
/// Udp异步通讯公有类
/// 实现server与client相同方法
/// </summary>
public abstract class UdpCommon : IDisposable
{
#region 全局成员
protected Socket worksocket;//当前套接字
protected EndPoint sendEP;//发送端remote
protected EndPoint reciveEP;//接受端remote
protected int buffersize = 1024;//数据缓冲区大小
protected RequestFile requestFile;//文件请求
protected FileSendManager sendmanager;//文件发送管理
protected FileReciveManager recivemanager;//文件接收管理
protected HandlerMessage handlerMsg = new HandlerMessage();//消息协议处理类
protected delegate void ReciveCallbackDelegate(int length, byte[] buffer);
protected event ReciveCallbackDelegate ReciveCallbackEvent;//消息接收处理事件
protected delegate void TimeOutHandlerDelegate(MutualMode mode, byte[] buffer);
protected event TimeOutHandlerDelegate TimeOutHandlerEvent;//超时处理
#endregion
#region 构造函数
/// <summary>
/// 构造函数
/// </summary>
/// <param name="_requestFile">请求信息</param>
public UdpCommon(RequestFile _requestFile)
{
requestFile = _requestFile;
}
#endregion
#region 抽象方法
/// <summary>
/// 接受buffer处理
/// </summary>
/// <param name="length"></param>
/// <param name="buffer"></param>
public abstract void ReciveBufferHandler(int length, byte[] buffer);
#endregion
#region 异步接受/发送buffer
/// <summary>
/// 异步接受buffer
/// </summary>
protected void AsynRecive()
{
byte[] buffer = new byte[buffersize];
worksocket.BeginReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref reciveEP,
asyncResult =>
{
if (asyncResult.IsCompleted)//信息接收完成
{
int length = worksocket.EndReceiveFrom(asyncResult, ref sendEP);
if (TimeOutHandlerEvent != null)
{
TimeOutHandlerEvent.Invoke(MutualMode.recive, null);
}
ReciveCallbackEvent.Invoke(length, buffer);
}
}, null);
}
/// <summary>
/// 异步发送buffer
/// </summary>
/// <param name="buffer"></param>
protected void AsynSend(byte[] buffer)
{
worksocket.BeginSendTo(buffer, 0