关于数据包丢失的问题(200分)

  • 主题发起人 主题发起人 Sterntaler
  • 开始时间 开始时间
S

Sterntaler

Unregistered / Unconfirmed
GUEST, unregistred user!
使用 ClientSocket 和 ServerSocket 做试验.
当用 ClientSocket 发送缓冲区, 而在 ServerSocket 没有接受缓冲区的处理时, 仅在
第一次发送时 ServerSocket 接收到数据. 后来发的数据包(除了断开连接再连上后发送)
都丢失了. 而且没有任何异常迹象.
我尝试过载服务器端用缓冲区保存一些数据包, 不过只能在数据包的数量不多, 不大
时才有效, 而且不能保证在处理接受数据包的时候没有新的数据包到来.用 TCP 协议或许
能够解决问题的, 但我窃以为速度不合胃口. 若考虑超时重传, 那么又会引起一连串的
问题 ...

希望各位大虾给点建议, 最好也给个例子说明一下如何实现, 方法不限.
感谢过来参加讨论的各位, 谢谢关心, 希望大家都给点建议.
 
会丢吗?完了,以后又要考虑多点了
我先搬个凳子放在第一排听
 
不会吧?我的怎么没有丢过呀?把源码帖上来看看呀!
 
首先,看看你的网络是否有问题:
如果是局域网,那么
ping -t -l 4096 对方的IP地址
应该不会丢一个包的,如果有丢包,应该换换网线
 
其次,程序问题,请你介绍一下主要的收、发的代码
 
对不起, 让大家久等了. 现在送上源码主要部分.
unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ScktComp;

type
TRec = record
Command: Byte;
BufferSize: Integer;
Buffer: PChar;
end;
PRec = ^TRec;

type
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
ClientSocket1: TClientSocket;
ServerSocket1: TServerSocket;
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

function Receiver(const APointer: Pointer): LongInt; stdcall;
{ 功能: 接收数据包 }
{ 接收做成这样的函数, 是为并行处理作准备 }
const
MAX_BUF_LEN: Integer = $F000;
var
Buffer: PChar; // 接收缓冲区
ARec: TRec; // 一个纪录
ReceiveLen, BufferSize: Integer; // 接收到的数据长度, 缓冲区大小
AStream: TMemoryStream; // 用来缓冲接收数据
Socket: TCustomWinSocket; // 接收数据的套接字
begin
Socket := TCustomWinSocket(APointer^);
ReceiveLen := Socket.ReceiveLength;
if ReceiveLen > MAX_BUF_LEN then BufferSize := MAX_BUF_LEN
else BufferSize := ReceiveLen;

GetMem(Buffer, BufferSize);
try
AStream := TMemoryStream.Create;
try
{ 分块缓冲接收数据 }
while ReceiveLen > 0 do
begin
BufferSize := Socket.ReceiveBuf(Buffer^, BufferSize); // 接收一块

AStream.Write(Buffer^, BufferSize);
Dec(ReceiveLen, BufferSize);
end;

AStream.Position := 0;
GetMem(Buffer, AStream.Size);
AStream.Read(Buffer^, AStream.Size);
finally
AStream.Free;
end;

ARec := PRec(Buffer)^;
// GetMem(ARec.Buffer, ARec.BufferSize); // 调用 GetMem 重新分配内存则出错!
Form1.Memo1.Lines.Add(ARec.Buffer);
finally
FreeMem(Buffer);
end;
Result := 0;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
ServerSocket1.Port := 1234;
ServerSocket1.Open;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
ClientSocket1.Close;
ClientSocket1.Port := 1234;
ClientSocket1.Host := '127.0.0.1';
ClientSocket1.Open;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
ARec: TRec;
RecSize: Integer;
begin
ARec.BufferSize := 8;
GetMem(ARec.Buffer, ARec.BufferSize);
ARec.Buffer := 'ABCDasda';

RecSize := SizeOf(ARec) + ARec.BufferSize;

ClientSocket1.Socket.SendBuf(ARec, RecSize);
end;

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
Receiver(Pointer(@Socket));
end;

end.
 
错错错错错错错
TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket);
事件的含义都没有弄清。(看一下socket的资料)

应不断Receiver(Pointer(@Socket));
begin
while (true) do
begin
Receiver(Pointer(@Socket));
if bDone then Exit;
end;
end;
 
to Sterntaler,
你的问题实在很大,
TRec里面使用了pchar,看来你对指针的理解还差好远
接收和发送部分都有问题
先讲发送部分
1、既然GetMem(ARec.Buffer……),ARec.Buffer已经分配了
一个内存块,ARec.Buffer := 'ABCDasda',就指向了别的内存
分配的内存就没用上,而且没办法释放了(针都不见了)
一般的填充内容,可以使用StrCopy拷贝字符串,或者Move拷贝二进制内存
2、不管使用上面的什么办法,你的发送的内容都是不对的
RecSize := SizeOf(ARec) + ARec.BufferSize;
ClientSocket1.Socket.SendBuf(ARec, RecSize);
你发送ARec开始的RecSize字节,可是你要知道,ARec.Buffer
并不是紧跟在AREC后面,它在别的地方,所以你连续发RecSize
字节,其实后面的是别的无关的内容

接收的部分也错!
实际上,你的办法只有Comamnd和BufferSize是对的
Buffer是客户端进程的指针,服务器端怎么能用呢
 
接收大家的指正.
 
to xkliu: --> 谢谢! 我也想应和你说的那样的, 不过我试了一下, 却出 AV 了.
 
to Pipi: --> 非常感谢! 我最近在测试指针的使用. 用得不好, 谢谢指出, 使我少走了冤枉路.

不过我还是有点不理解的, 如果你肯抽空帮我看一下:
: 如果不使用 GetMem 为 重新分配内存的话, 接收的代码
ARec := PRec(Buffer)^;
// GetMem(ARec.Buffer, ARec.BufferSize); // 调用 GetMem 重新分配内存则出错
然后存取 ARec.Buffer
在我的本地机器上运行结果正确(和别的机器上通讯出错). 是否是碰巧的呢?

给出解决的一个方案好不? - 这要求不算过分吧, 太忙的话就算了.
 
在你的本地机器上运行结果正确,是说,客户和服务器是同一个进程?是的话,那当然,同一个进程里面当然指针指向的同一个内存

> // GetMem(ARec.Buffer, ARec.BufferSize); // 调用 GetMem 重新分配内存则出错!
你跟踪一下ARec.BufferSize的值看是不是你预期的
 
to Pipi: --> 原来如此, 太谢谢你了!
对于 ARec.BufferSize 我跟踪过了, 没有出问题. 你在前面不也说它是对的吗?
 
非常感谢 Pipi, xkliu 的解答, 同时也感谢 完颜康, truecat 的关注.
希望下次还有机会见到各位.
 
后退
顶部