巨大的问题------------udp数据报,分包的组合 !

lucbesson 2005-04-20 12:13:42
发送数据:

private void send()
{

// **STEP 1**创建一个UdpClient实例
udp= new UdpClient();

ip=IPAddress.Parse("192.168.1.255");
endpoint=new IPEndPoint(ip,serverport);

try
{
udp.Connect(endpoint);

string str=this.richTextBox1.Text ;
byte[] bytes =Encoding.Unicode.GetBytes(str);
//总包的大小
int totalSize=bytes.Length;
//***********小于mtu的数据包做标记直接发送****************
//如果总包超过udp的最大传输自己即MTU,则进行分包
if(totalSize<60000)
{ //数据小于60000字节,进行标记,方便Client接收
byte[] b1=new byte[totalSize+1];
b1[0]=1;
//给小于MTU的数据包做标记
Array.Copy(bytes,0,b1,1,totalSize);
udp.Send(b1,b1.Length);
}
//**************大于mtu的数据包进行分包,然后发送**********
else
{


//分包的大小
int PacketSize=60000;
//分包的数量
int PacketCount=0;
//分包个数的控制
//totalSize/PacketSize*PacketSize 只有两中情况,等于totalsize 或者是大于

if(totalSize/PacketSize*PacketSize<totalSize)
{ PacketCount=totalSize/PacketSize+1; }
else
{ PacketCount=totalSize/PacketSize; }



//数据包
byte[] data=new byte[PacketSize+1];
//开始循环发送数据包
for(int i=0;i<PacketCount;i++)
{
//分包的起始位置
int start=i*PacketSize;
//最后一个包的发送
if(i==PacketCount-1)
{
//最后一个包的大小
int LastDataPacket=(int)(totalSize-((long)(PacketSize*PacketCount)));
//最后一个数据包的起始位置
int LastDataPacketBegin=totalSize-lastDataPacket;

//给最后一个数据包的前两个自己打标
data[0]=0;
data[1]=i;
Array.Copy(bytes,LastDataPacketBegin,data,2,LastDataPacket)
udp.Send(lastdata,lastdata.Length);
}
//发送分包
else
{
//分包的标记
data[0]=i;
//填充数据包;从bytes源数组提取数据。
Array.Copy(bytes,start,data,1,data.Length);
//发送做过标记的数据包
udp.Send(data,data.Length);
}

}

catch( Exception e)
{
MessageBox.Show(e.ToString());
}
}


----------------------

数据接收:

while(true)
{
RemoteIpEndPoint=null;
try
{

byte[] receiveBytes=receive.Receive(ref RemoteIpEndPoint);

//数据小于30000字节的,处理掉标记后进行输出
if(receiveBytes[0]==1)
{
//长度减1
byte[] b1=new byte[receiveBytes-1] ;
//除去标记
Array.Copy(receiveBytes,1,b1,0,receiveBytes.Length);
//转换数据为string 并显示
string returnData=Encoding.Unicode.GetString(b1,0,b1.Length);
this.richTextBox.Text=returenData;
}
else
{
// ************************
//取得分包的数据

//并组合数据
//************************

}

}

catch(Exception e)
{
MessageBox.Show(e.ToString());
}
}


-----------------------------
由于能力有限,在数据分包上还有很多的错误。请高手指正 !
问题是 : 在接收端,我无发把数据包再组合起来啦 。因为那是个阻塞的方式 !

当然先排除数据包丢失和顺序错误不考虑,怎么样单纯的把数据分包组合起来 ?
下一 步再解决数据包的顺序问题。谢谢啦 !

--------------------
http://blog.china-pub.com/more.asp?name=immelman&id=11692
这个是主程序的代码 。
...全文
393 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
12345_ 2005-07-30
  • 打赏
  • 举报
回复
mark
cnming 2005-04-22
  • 打赏
  • 举报
回复
昨晚闲着没事,测试了一下


按照以上的代码,同一次可以传递一篇小文章,其中包括中文、英文、标点符号、换行等等的文本文章
cnming 2005-04-22
  • 打赏
  • 举报
回复
#region methods

private void Listen()
{
int port = 11000;

UdpClient listener = new UdpClient(port);
int m_intSerial = 0;
int m_intBlocks = 0; //数据分割块数
int m_intLength = 0; //数据长度
int m_intMessageLength = 0;
int place = 0;
int m_intGetCount = 0;
IPEndPoint ipep = null;

try
{
while(true)
{
byte[] bytesF = listener.Receive(ref ipep);

place = 0;
m_intSerial = BitConverter.ToInt32(bytesF,place);
place += 4;
m_intBlocks = BitConverter.ToInt32(bytesF,place);
place += 4;
m_intLength = BitConverter.ToInt32(bytesF,place);
place += 4;
m_intMessageLength = BitConverter.ToInt32(bytesF,place);
place += 4;

byte[] data = new byte[m_intMessageLength]; //申明数据的总长

Array.Copy(bytesF, 16, data, m_intBlockLength * m_intSerial, m_intLength); //复制数据

m_intGetCount = m_intGetCount + 1;
Application.DoEvents();

if(m_intBlocks > 1)
{
while(true)
{
byte[] bytes = listener.Receive(ref ipep);

place = 0;
m_intSerial = BitConverter.ToInt32(bytes,place);
place += 4;
m_intBlocks = BitConverter.ToInt32(bytes,place);
place += 4;
m_intLength = BitConverter.ToInt32(bytes,place);
place += 4;
m_intMessageLength = BitConverter.ToInt32(bytes,place);
place += 4;

Array.Copy(bytes, 16, data, m_intBlockLength * m_intSerial, m_intLength); //复制数据

m_intGetCount = m_intGetCount + 1;

Application.DoEvents();

if (m_intGetCount >= m_intBlocks)
{
break;
}
}
}
//string message = Encoding.ASCII.GetString(data, 0, data.Length);
//string message = Encoding.Unicode.GetString(data, 0, data.Length);
string message = System.Text.Encoding.Default.GetString(data, 0, data.Length);
string date = DateTime.Now.ToString();
string history = date + " From: " +
ipep.ToString() + " \r\n" +
message + "\r\n";
this.rtxtHistory.AppendText(history);
this.txtMsg.Select(); // 设置控件焦点
Console.WriteLine("Closed!");

if(message == "Exit")
{
break;
}

}
listener.Close();
}
catch (System.Exception pe)
{
Console.WriteLine(pe.ToString());
}
}
private void Send(string message)
{
IPAddress ip = IPAddress.Parse("255.255.255.255");
int port = 11000;
UdpClient sender = new UdpClient();
IPEndPoint ipep = new IPEndPoint(ip, port);


//byte[] BTmp = Encoding.ASCII.GetBytes(message);
//byte[] BTmp = Encoding.Unicode.GetBytes(message);
byte[] BTmp = System.Text.Encoding.Default.GetBytes(message);
int m_intMessageLength = BTmp.Length;
int m_intSerial = 0;

int m_intBlocks = Convert.ToInt32(Math.Ceiling(Convert.ToDouble(m_intMessageLength)/Convert.ToDouble(m_intBlockLength))); //数据分割块数

while (m_intMessageLength > 0)
{
try
{
int m_intLength = m_intBlockLength; //数据长度

if (m_intMessageLength < m_intLength)
{
m_intLength = m_intMessageLength;
}

byte[] data = new byte[m_intLength+16];

int place = 0;
Buffer.BlockCopy(BitConverter.GetBytes(m_intSerial), 0, data,place,4); //顺序戳
place += 4;
Buffer.BlockCopy(BitConverter.GetBytes(m_intBlocks), 0, data,place,4); //数据总块数
place += 4;
Buffer.BlockCopy(BitConverter.GetBytes(m_intLength), 0, data,place,4); //数据长度
place += 4;
Buffer.BlockCopy(BitConverter.GetBytes(BTmp.Length), 0, data,place,4); //数据总长度
place += 4;
Array.Copy(BTmp, m_intSerial * 1000, data, 16, m_intLength); //复制数据


sender.Send(data, data.Length, ipep);

m_intSerial = m_intSerial + 1;
m_intMessageLength = m_intMessageLength - m_intLength;
}
catch (System.Exception pe)
{
Console.WriteLine(pe.ToString());
}
}
string date = DateTime.Now.ToString();
string history = date + " To: " +
ipep.ToString() + " \r\n" + message + "\r\n";
this.rtxtHistory.AppendText(history);

sender.Close();
}


#endregion

#region events

private void frmBroadcast_Load(object sender, System.EventArgs e)
{
this.mThred = new Thread(new ThreadStart(Listen));
this.mThred.Start();
}
private void frmBroadcast_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
this.Send("exit");
}
private void frmBroadcast_Closed(object sender, System.EventArgs e)
{
try { this.mThred.Abort(); }
catch(System.Exception pe) { Console.WriteLine(pe.ToString()); }
}
private void txtMsg_TextChanged(object sender, System.EventArgs e)
{
// 控制 buttonSend 的 Enabled 属性
this.buttonSend.Enabled = this.txtMsg.Text != "";
}
private void rtxtHistory_TextChanged(object sender, System.EventArgs e)
{
// 设置 rtxtHistory 控件的文本字体
this.rtxtHistory.Font = this.txtMsg.Font;
}
private void buttonSend_Click(object sender, System.EventArgs e)
{
// 发送网络消息并显示历史信息
this.Send(this.txtMsg.Text);
this.txtMsg.Text = "";
this.txtMsg.Select(); // 设置控件焦点
}


#endregion


}
}
cnming 2005-04-22
  • 打赏
  • 举报
回复
完整的代码:

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace CSDNSample
{
/// <summary>
/// Form1 的摘要说明。
/// </summary>
public class frmBroadcast : System.Windows.Forms.Form
{

#region objects

/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.Container components = null;
private System.Windows.Forms.RichTextBox rtxtHistory;
private System.Windows.Forms.TextBox txtMsg;
private System.Windows.Forms.Button buttonSend;

// 自定义对象
private Thread mThred = null;

private int m_intBlockLength = 1000; //每一个数据包的大小

#endregion

#region constructors

public frmBroadcast()
{
// Windows 窗体设计器支持所必需的
InitializeComponent();
this.txtMsg.Select(); // 设置控件焦点
}


#endregion

#region dispose

/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}


#endregion

#region initialize

/// <summary>
/// 设计器支持所需的方法 - 不要使用代码编辑器修改
/// 此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.rtxtHistory = new System.Windows.Forms.RichTextBox();
this.txtMsg = new System.Windows.Forms.TextBox();
this.buttonSend = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// rtxtHistory
//
this.rtxtHistory.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.rtxtHistory.AutoWordSelection = true;
this.rtxtHistory.Location = new System.Drawing.Point(8, 8);
this.rtxtHistory.Name = "rtxtHistory";
this.rtxtHistory.ReadOnly = true;
this.rtxtHistory.Size = new System.Drawing.Size(408, 264);
this.rtxtHistory.TabIndex = 0;
this.rtxtHistory.Text = "";
this.rtxtHistory.TextChanged += new System.EventHandler(this.rtxtHistory_TextChanged);
//
// txtMsg
//
this.txtMsg.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.txtMsg.Location = new System.Drawing.Point(8, 280);
this.txtMsg.Multiline = true;
this.txtMsg.Name = "txtMsg";
this.txtMsg.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.txtMsg.Size = new System.Drawing.Size(320, 68);
this.txtMsg.TabIndex = 1;
this.txtMsg.Text = "";
this.txtMsg.TextChanged += new System.EventHandler(this.txtMsg_TextChanged);
//
// buttonSend
//
this.buttonSend.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.buttonSend.Enabled = false;
this.buttonSend.Location = new System.Drawing.Point(336, 280);
this.buttonSend.Name = "buttonSend";
this.buttonSend.Size = new System.Drawing.Size(80, 68);
this.buttonSend.TabIndex = 2;
this.buttonSend.Text = "发送(&S)";
this.buttonSend.Click += new System.EventHandler(this.buttonSend_Click);
//
// frmBroadcast
//
this.AcceptButton = this.buttonSend;
this.AutoScaleBaseSize = new System.Drawing.Size(6, 14);
this.ClientSize = new System.Drawing.Size(424, 357);
this.Controls.Add(this.buttonSend);
this.Controls.Add(this.txtMsg);
this.Controls.Add(this.rtxtHistory);
this.Name = "frmBroadcast";
this.Text = "网络消息广播";
this.Closing += new System.ComponentModel.CancelEventHandler(this.frmBroadcast_Closing);
this.Load += new System.EventHandler(this.frmBroadcast_Load);
this.Closed += new System.EventHandler(this.frmBroadcast_Closed);
this.ResumeLayout(false);

}


#endregion

cnming 2005-04-22
  • 打赏
  • 举报
回复
纠正:

Listen中的下面的这个语句有误
Array.Copy(bytesF, 16, data, m_intLength * m_intSerial, m_intLength); //复制数据
针对给你的那个Project,应该改为
Array.Copy(bytesF, 16, data, 1000 * m_intSerial, m_intLength);

此外,byte码的获取最好修改为如下代码
byte[] BTmp = System.Text.Encoding.Default.GetBytes(message);
相对应
string message = System.Text.Encoding.Default.GetString(data, 0, data.Length);
lucbesson 2005-04-22
  • 打赏
  • 举报
回复
谢谢老师 cnming(cnming)

lucbesson 2005-04-20
  • 打赏
  • 举报
回复
问题是:如何把分包的数据组合起来 ?

其他的因素先放一下,一步一步来,初次写网络的东西,还有很多的不足。

这次的问题就是把分包组合起来 。
大雨仔 2005-04-20
  • 打赏
  • 举报
回复
如果有可能的话,我都是建议用WebService来完成的,如果真的要用UPD的的话,要自己合理的定义一下通信协议,这是非常重要的
lucbesson 2005-04-20
  • 打赏
  • 举报
回复
由于能力有限也是初次接触网络编程,还希望朋友多指点一下 !

谢谢
lucbesson 2005-04-20
  • 打赏
  • 举报
回复
再顶

别沉啦
TonyTonyQ 2005-04-20
  • 打赏
  • 举报
回复
问题是:如何把分包的数据组合起来 ?
===================================

可以考虑使用临时文件或内存块来保存各个分包,然后再按顺序依次组合起来。
就是把你的 receiveBytes 变量以二进制方式写到一个临时文件,文件名最好以分包的序号来命名,这样就有很多个临时文件,在完成全部接收或者发现接收到连续的分包时,就可以把这些临时文件合并起来。

不建议你直接转换为 String 来处理,主要是 String 比较占内存,而且大部分二进制数据显示成 String 都是乱码(ASCII的除外)。
zlopen 2005-04-20
  • 打赏
  • 举报
回复
mark
singlepine 2005-04-20
  • 打赏
  • 举报
回复
学习
cnming 2005-04-20
  • 打赏
  • 举报
回复
把你的程序整成一个demo给我好了

cnming@163.com
lih163 2005-04-20
  • 打赏
  • 举报
回复
学习
lucbesson 2005-04-20
  • 打赏
  • 举报
回复
qzj(SoldierQ)说的很好,又学到了一点。

继续收集意见和解决方法 !

谢谢
MikroTik RouterOS是一种路由操作系统,并通过该软件将标准的PC电脑变成专业路由器,在软件RouterOS 软路由图的开发和应用上不断的更新和发展,软件经历了多次更新和改进,使其功能在不断增强和完善。特别在无线、认证、策略路由、带宽控制和防火墙过滤等功能上有着非常突出的功能,其极高的性价比,受到许多网络人士的青睐。 桥接功能   RouterOS能将多张网卡组建为一个桥模式,使路由器变成一个透明的桥设备,同样也实行三层交换的作用,MAC层的以太网桥、EoIP 、Prism、Atheros和RadioLAN 等都是支持的。所有802.11b和802.11a 客户端的无线网卡(如station模式的无线)受802.11 的限制无法支持桥模式,但可以通过EoIP协议的桥接方式实现。   为防止环路出现在网络中,可以使用生成树协议(STP) ,这个协议同样使冗余线路成为可能。   包括特征如下:   l 生成树协议(STP)   l 多桥接接口功能   l 该协议能选择转发或者丢弃   l 能实时监控MAC地址   l 桥防火墙   l 多线路支持   RouterOS基于策略的路由为网络管理者提供了比传统路由协议对文的转发和存储更强的控制能力,路由器用从路由协议派生出来的路由表,根据目的地址进行文的转发。   在负载均衡下也可以根据带宽的比例调整两条线路的流量。RouterOS提供了多种方式的路由功能,使其路由功能更强大,更灵活。RouterOS的路由功能主要为:   l 基于源地址的路由   l 基于目标地址的路由   l 基于端口的路由   l 基于定义用户类的路由   l 基于负载均衡的路由   l 基于端口的负载均衡   l 隧道协议   RouterOS支持多种隧道协议如PPP、PPPoE、PPTP、EoIP、IPIP以及IPsec,这些隧道协议可以为远程资源访问和企业间的连接提供很好的解决方案,如:   l 通过PPTP或IPIP实现通网络资源互用   l EoIP或PPTP的远程局域网解决方案   l 支持PPPoE服务器   l Hotspot热点认证服务   热点服务认证系统是一种web的认证方式,在此种认证方式中,用户可以通过自设IP地址或DHCP获得一个地址,打开浏览器,无论输入一个什么地址,都会被强制到一个认证界面,要求用户进行认证,认证通过后,就可以访问其他站点了。主要特征:   l 用户通过时间与流量认证计费   l Cookie (存储用户的账号和密码) 带宽控制功能   l 定额控制(连接超时时间, 下载/上传传输限制)   l 实时用户状态信息显示   l 自定义认证HTML页(可以由你自己设计认证页)   l DHCP服务器分配IP地址   l 简单的RAIUS客户端配置   l RouterOS 能与PPTP隧道、IPsec以及其它的一些功能配合使用。   l 可以通过Access Point与以太网接入用户。   l 定时广播指定的URL链接   l 脚本控制   RouterOS提供了可以编写的脚本功能,脚本的加入使RouterOS在处理很多网络方案、自动检查故障和动态生成策略等,都可以通过脚本很好的解决。使得在处理很多网络问题上更加的灵活和智能化。   具体功能:   TCP/IP协议组:   l Firewall和NAT–包状态过滤;P2P协议过滤;源和目标NAT;对源MAC、IP地址、端口、IP协议、协议(ICMP、TCP、MSS等)、接口、对内部的数据包和连接作标记、ToS 字节、内容过滤、顺序优先与数据频繁和时间控制、包长度控制...   l 路由 – 静态路由;多线路平衡路由;基于策略的路由(在防火墙中分类); RIP v1 / v2, OSPF v2, BGP v4   l 数据流控制 – 能对每个IP、协议、子网、端口、防火墙标记做流量控制;支持PCQ, RED, SFQ, FIFO对列; Peer-to-Peer协议限制   l HotSpot – HotSpot认证网关支持RADIUS验证和记录;用户可用即插即用访问网络;流量控制功能;具备防火墙功能;实时信息状态显示;自定义HTML登录页;支持iPass;支持SSL安全验证;支持广告功能。   l 点对点隧道协议 – 支持PPTP, PPPoE和L2TP访问控制和客户端; 支持PAP, CHAP, MSCHAPv1和MSCHAPv2 验证协议; 支持RADIUS验证和记录;MPPE加密;PPPoE压缩;数据流控制;具备防火墙功能;支持PPPoE按需拨号。   l 简单隧道 – IPIP隧道、EoIP隧道 (Ethernet over IP)   l IPsec – 支持IP安全加密AH和ESP协议;  

110,533

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧