怎么实现UDP数据包的拆包组包发送与接收?在线等待,期待高手(Up、gz均有分)......

tongdings 2002-09-17 03:45:45
//交叉链接,同题的,呵呵 :)

http://www.csdn.net/expert/topic/1026/1026938.xml?temp=.10693

//以下是本人的实现,只能传输2K数据流,大于2k发送不成功,为什么?
//也可自己实现,贴出源代码,谢先...

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ComCtrls, ExtCtrls, NMUDP;

type
TForm1 = class(TForm)
Memo1: TMemo;
Splitter1: TSplitter;
Splitter2: TSplitter;
Memo2: TMemo;
StatusBar1: TStatusBar;
Btn_Send: TButton;
Button2: TButton;
NMUDP1: TNMUDP;
OpenDialog1: TOpenDialog;
procedure Button2Click(Sender: TObject);
procedure NMUDP1StreamInvalid(var handled: Boolean; Stream: TStream);
procedure NMUDP1InvalidHost(var handled: Boolean);
procedure NMUDP1Status(Sender: TComponent; status: String);
procedure Btn_SendClick(Sender: TObject);
procedure NMUDP1DataSend(Sender: TObject);
procedure NMUDP1DataReceived(Sender: TComponent; NumberBytes: Integer;
FromIP: String; Port: Integer);
procedure Memo1KeyPress(Sender: TObject; var Key: Char);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

const
bufsize = 1024 * 3; //2048, 3072, 4096

{$R *.DFM}

procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end;

procedure TForm1.NMUDP1StreamInvalid(var handled: Boolean;
Stream: TStream);
begin
StatusBar1.Panels[0].Text := 'Invalid Stream: Stream contains no data';
end;

procedure TForm1.NMUDP1InvalidHost(var handled: Boolean);
var
S: String;
begin
S := NMUDP1.RemoteHost;
if InputQuery('Invalid host', 'Specify valid hostname: ', S) then
begin
NMUDP1.RemoteHost := S;
handled := TRUE;
end;
end;

procedure TForm1.NMUDP1Status(Sender: TComponent; status: String);
begin
StatusBar1.Panels[1].Text := status;
end;

procedure TForm1.Btn_SendClick(Sender: TObject);
var
i,count: Integer;
s: string;
SendStream: TMemoryStream;
MyStream: TMemoryStream;
leftsize,sendsize: Longint;
//buf: array[0..bufsize-1] of char;
begin
SendStream := TMemoryStream.Create;
MyStream := TMemoryStream.Create;

Memo1.SetFocus;

try
if OpenDialog1.Execute then
Memo1.Lines.LoadFromFile(OpenDialog1.FileName);

s:= Memo1.Lines.text;
if s = '' then
begin
MessageDlg('Invalid message! Please Add it...',mtError,[mbOK],0);
exit;
end;

NMUDP1.RemoteHost := '127.0.0.1';
NMUDP1.ReportLevel := Status_Basic;
NMUDP1.RemotePort := 6668;
Nmudp1.LocalPort := 6668;

MyStream.LoadFromFile(OpenDialog1.FileName);
Mystream.Position := 0;
leftsize := Mystream.Size;
//leftsize := length(s);

if leftsize > bufsize then
begin
count := leftsize div bufsize; //´ò°ü³Éÿ°ü2048Bytes ʱµÄ´ò°ü´ÎÊý
for i := 1 to count do
begin
//sendsize := bufsize;
sendstream.Write(s[1],bufsize);
NMUDP1.SendStream(sendstream);
leftsize := Mystream.Seek((bufsize * i), soFromBeginning);
if leftsize = 0 then
break;
end; //end of [for i := 1 to count do]
end
else
begin
sendsize := leftsize;
SendStream.Write(s[1],sendsize);
NMUDP1.SendStream(sendstream);
end;

finally
Sendstream.Free;
Mystream.Free;
end;

end;

procedure TForm1.NMUDP1DataSend(Sender: TObject);
begin
StatusBar1.Panels[2].Text := 'Data From Memo1 to Memo2 Sent...';
end;

procedure TForm1.NMUDP1DataReceived(Sender: TComponent;
NumberBytes: Integer; FromIP: String; Port: Integer);
var
recStream,resultstream: TMemoryStream;
buf: array[0..10000] of char;
begin
recStream := TMemoryStream.Create;
resultstream := TMemoryStream.Create;
try
NMUDP1.ReadStream(recStream);
recstream.Position := 0; //ÖÃÇ°
recstream.ReadBuffer(buf,NumberBytes); //read data from recstream to buf;
if NumberBytes > bufsize then
begin
resultstream.WriteBuffer(buf,bufsize);
//resultstream.Position := 0;
Memo2.Lines.LoadFromStream(resultstream);
Statusbar1.Panels[3].Text := 'Data received '+IntToStr(NumberBytes)+' bytes from '
+FromIP+' on port '+IntToStr(Port);
numberbytes := numberbytes - bufsize;
end
else //NumberBytes <= bufsize
begin
resultstream.WriteBuffer(buf,numberbytes);
resultstream.Position := 0;
Memo2.Lines.LoadFromStream(resultstream);
Statusbar1.Panels[3].Text := 'Data received '+IntToStr(NumberBytes)+' bytes from '
+FromIP+' on port '+IntToStr(Port);
end;
finally
recStream.Free;
end;
end;

procedure TForm1.Memo1KeyPress(Sender: TObject; var Key: Char);
begin
if key = #13 then
self.Btn_SendClick(self);
end;

end.

//end of program
...全文
899 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
wddarkknight 2002-09-20
  • 打赏
  • 举报
回复
各位还是使用id的udp吧,也不麻烦的。完全可以发送>2k的内容,建议尝试一下。
直接发(>2k的内容)就可以了,client直接收即可
tongdings 2002-09-20
  • 打赏
  • 举报
回复
to HHBB
我想应该是算法有问题吧,跟踪过,不得解阿.
求助了各位
HHBB 2002-09-20
  • 打赏
  • 举报
回复
改一下bufsize的值看看。
tongdings 2002-09-20
  • 打赏
  • 举报
回复
在各位的协助下,特别要指出的是lvxq(吕歌)大哥,通过E-Mail联系,基本实现UDP数据包的发送与接收问题,在此表示感谢!
其实我最近上次的代码已基本正确,只不过不能把发送与接收事件同时写在同一过程中,这点很重要,其实当时我把它们写在一起,是为了测试求方便,其实想想这种态度很不好 :)

tongdings 2002-09-20
  • 打赏
  • 举报
回复
在各位的协助下,特别要指出的是lvxq(吕歌)大哥,通过E-Mail联系,基本实现UDP数据包的发送与接收问题,在此表示感谢!
其实我最近上次的代码已基本正确,只不过不能把发送与接收事件同时写在同一过程中,这点很重要,其实当时我把它们写在一起,是为了测试求方便,其实想想这种态度很不好 :)

dcndcn 2002-09-19
  • 打赏
  • 举报
回复
gz
fish21cn 2002-09-19
  • 打赏
  • 举报
回复
gz
tongdings 2002-09-19
  • 打赏
  • 举报
回复
下面是实现基于UDP包数据流发送源码。还存在一个问题就是大于2K包会存在一部分数据丢失现象,2K之内要点击两次才能发送成功,不知为什么。跟踪了N次。恳求解答,谢谢!

********************************************************************

unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
NMUDP, StdCtrls, Buttons, ExtCtrls, ComCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Memo2: TMemo;
StatusBar1: TStatusBar;
Splitter1: TSplitter;
SendBtn: TBitBtn;
NMUDP1: TNMUDP;
OpenDialog1: TOpenDialog;
procedure NMUDP1StreamInvalid(var handled: Boolean; Stream: TStream);
procedure NMUDP1InvalidHost(var handled: Boolean);
procedure NMUDP1Status(Sender: TComponent; status: String);
procedure SendBtnClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
const bufsize = 1024 * 2;
{$R *.DFM}
procedure TForm1.NMUDP1StreamInvalid(var handled: Boolean;
Stream: TStream);
begin
showmessage('Invalid Stream: Stream contains no data');
end;
procedure TForm1.NMUDP1InvalidHost(var handled: Boolean);
var
S: String;
begin
S := NMUDP1.RemoteHost;
if InputQuery('Invalid host', 'Specify valid hostname: ', S) then
begin
NMUDP1.RemoteHost := S;
handled := TRUE;
end;
end;
procedure TForm1.NMUDP1Status(Sender: TComponent; status: String);
begin
statusbar1.SimpleText := 'Status: ' + status;
end;
//***********************************************************************************
//******* this procedure include 'send' amd ' receive' two actions with UDP ******
//*** Const type: ******************************************************************
//*** bufsize = 2 * 1024 *********************************************************
//*** variant type: *****************************************************************
//*** My: Source Stream(Memo1's text) ***************************************
//*** Send: the stream being to sent *****************************************
//*** temp: the temporary stream being to received ****************************
//*** i: circulation Integer variant **************************************
//*** leftsize: the total left size being to sent *********************************
//*** buf: buffer variant(2048 bytes) ****************************************
//***********************************************************************************
procedure TForm1.SendBtnClick(Sender: TObject);
var
My,send: TMemoryStream;
tmp: TmemoryStream;
i: Integer;
//count: Integer;
leftsize: Integer;
buf: array[0..bufsize] of char;
begin
My := TMemorystream.Create;
Send := TMemorystream.Create;
tmp := TMemorystream.Create;
if Opendialog1.Execute then
My.LoadFromFile(Opendialog1.FileName);
Memo1.Lines.LoadFromStream(My);
//send.SetSize(bufsize);
//tmp.SetSize(bufsize);
leftsize := my.Size;
//count := leftsize div bufsize;
my.Position := 0;
i:=0;
try
While (leftsize > bufsize) do
begin
leftsize := leftsize - bufsize ;
//showmessage('My.position: ' + Inttostr(my.Position)); //......
send.CopyFrom(my,bufsize);
my.Seek(0,sofromcurrent); // .Position := bufsize * i;
//showmessage('My.position: ' + Inttostr(my.Position)); //......
//my.Position := bufsize * (i+1);
send.Position := bufsize * i;
NMUDP1.SendStream(send);
NMUDP1.ReadStream(tmp);
send.Clear;
tmp.Read(buf,bufsize);
Memo2.Lines.Add(buf); // .LoadFromStream(tmp);
//showmessage(Memo2.Lines.Text); //.......
tmp.Clear;
INC(i);
end;
//showmessage('My.position: ' + Inttostr(my.Position)); //.......
if (leftsize > 0) and (leftsize <= bufsize)then
begin
send.SetSize(leftsize);
//showmessage('My.position: ' + Inttostr(my.Position));
send.CopyFrom(my,leftsize);
//showmessage('send.position: ' + Inttostr(send.Position)); //......
//showmessage('My.position: ' + Inttostr(my.Position)); //......
send.Position := 0;
NMUDP1.SendStream(send);
NMUDP1.ReadStream(tmp);
send.Clear;
tmp.Read(buf,bufsize);
Memo2.Lines.Add(buf); // .LoadFromStream(tmp);
tmp.Clear;
end;
Finally
tmp.Free;
send.Free;
my.Free;
end;
StatusBar1.Panels[0].Text := 'Source bytes: ' + Inttostr(length(Memo1.Lines.Text)) +
' bytes; Total received bytes: ' + IntTostr(length(Memo2.Lines.Text)) + ' bytes.';
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Memo1.Clear;
Memo2.Clear;
NMUDP1.RemoteHost := 'LOCALHOST';
NMUDP1.ReportLevel := STATUS_BASIC;
NMUDP1.RemotePort := 5667;
NMUDP1.LocalPort := 5667;
end;
end.

********************************************************************
wisenowa 2002-09-18
  • 打赏
  • 举报
回复
要是用ServerSocket和ClientSocket一次最多可以传输大于不止2k的数据
我的机子上最大为5M左右,与硬件或操作系统有关吧?
UDP没试过

如果用UDP协议,那么你只能把文件分开然后部分的传送
当一部分接受完后,就向对方要求下一部分的数据,直到全部接受完毕

用ServerSocket和ClientSocket的话如果文件不大于1M的话,发送段只需发送一次就行了
而接受端则要不停的接受直到接受完毕
这么做有一个缺点就是发送出去的东西所占的内存在Socket关闭前不能释放
如果要传输几百兆的东西,就得不断的断开、连接以释放内存。

至于接受完毕的标志,你可以在发送端真正发送数据之前,先把要发送的数据的
大小发送给接受端,那么接受端就可以依次为依据看是否接受完毕。
lvxq 2002-09-18
  • 打赏
  • 举报
回复
sorry, 好像是错了,不过你的length(s)还是不好,我建议直接使用leftsize
即:leftsize := leftsize - bufsize ;
而且你的for循环结束后也没有执行剩余部分的发送,令人迷惑。
我想这样看看,不过我这里没有delphi环境,所以没法试

leftsize := my.Size;
my.Position := 0;
i:=0;
try
While leftsize > bufsize do
begin
send.CopyFrom(my,bufsize);
NMUDP1.SendStream(send);
NMUDP1.ReadStream(tmp);
Memo2.Lines.LoadFromStream(tmp);
leftsize := leftsize - bufsize ;
INC(i);
my.Seek((bufsize * i),soFromBeginning);
end;
if leftsize>0 then
begin
send.CopyFrom(my,leftsize);
NMUDP1.SendStream(send);
NMUDP1.ReadStream(tmp);
Memo2.Lines.LoadFromStream(tmp);
end;
Finnally ........

如果,你可以从当前位置开始移动,就可以不要i变量了
del_c_sharp 2002-09-18
  • 打赏
  • 举报
回复
有没有搞错??
xiachedan 2002-09-18
  • 打赏
  • 举报
回复
up
lvxq 2002-09-18
  • 打赏
  • 举报
回复
leftsize := length(s) - bufsize * i;

????
上面一句话很是难解,因为你每次减掉的只需一个bufsize就可以了,因此
leftsize := length(s) - bufsize ;可能是正解

对不起,由于实在是太久没用delphi了,可能是我错,请原谅
tongdings 2002-09-18
  • 打赏
  • 举报
回复
//下面这段为什么传输大于2K文本为何不可?(2k以内通过)
//其中,bufsize = 2048;

procedure TForm1.Btn_TransClick(Sender: TObject);
var
tmp,My,send: TMemoryStream;
i,count: Integer;
leftsize: Integer;
//sendsize: longint;
s: string;
begin
tmp := TMemorystream.Create;
My := TMemorystream.Create;
Send := TMemorystream.Create;

if Opendialog1.Execute then
My.LoadFromFile(Opendialog1.FileName);
Memo1.Lines.LoadFromStream(My);
s := Memo1.Lines.Text;

leftsize := my.Size;
my.Position := 0;
try
if leftsize > bufsize then
begin
count := leftsize div bufsize;
for i := 1 to count do
begin
send.CopyFrom(my,bufsize);
NMUDP1.SendStream(send);
NMUDP1.ReadStream(tmp);
Memo2.Lines.LoadFromStream(tmp);
leftsize := length(s) - bufsize * i;
my.Seek((bufsize * i),soFromBeginning);
end;
end
else //leftsize <= bufsize
begin
send.CopyFrom(my,leftsize);
NMUDP1.SendStream(send);
NMUDP1.ReadStream(tmp);
Memo2.Lines.LoadFromStream(tmp);
end;
finally
tmp.Free;
send.Free;
my.Free;
end;
end;
lvxq 2002-09-18
  • 打赏
  • 举报
回复
对于udp协议来说,它的一次的传送字节就是不能超过两k,所以你必须自己在发送端完成对流的分割,对于怎么分割,我建议你去找别的贴子,我记得以前有关于怎么在局域网中传输别的计算机的屏幕截屏图片的,例子很简单,也很实用,你参考一下吧,无论是读还是写,请注意指针的位置就可以了。
tongdings 2002-09-18
  • 打赏
  • 举报
回复
to wisenowa(127.0.0.1)
我想用UDP传送实时数据吧。
tongdings 2002-09-17
  • 打赏
  • 举报
回复
to sandraphh(我在云端)

我始终相信这里有高手的。
期待...
sandraphh 2002-09-17
  • 打赏
  • 举报
回复
我问过同样的问题,没人告诉我!
tongdings 2002-09-17
  • 打赏
  • 举报
回复
大家畅谈噢!~
kakiyawen 2002-09-17
  • 打赏
  • 举报
回复
up,gzing。
加载更多回复(8)

5,386

社区成员

发帖
与我相关
我的任务
社区描述
Delphi 开发及应用
社区管理员
  • VCL组件开发及应用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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