Socket通信速率問題,精通的請幫忙!(300分) ( 积分: 100 )

  • 主题发起人 主题发起人 leadyli
  • 开始时间 开始时间
L

leadyli

Unregistered / Unconfirmed
GUEST, unregistred user!
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
Buf: array [0..60000] of byte;
ln: LongInt;
Str: TMemoryStream;
bmp: TBitmap;
tit: array [0..3] of char;
text: string;
begin
str := TMemoryStream.Create;
ln := Socket.ReceiveLength;
Socket.ReceiveBuf(tit,4);
if tit = 'bmp:' then
begin
Socket.ReceiveBuf(Buf,ln-4);
Str.WriteBuffer(Buf,ln-4);
bmp := TBitmap.Create;
Str.Position := 0;
bmp.LoadFromStream(Str);
Image1.Picture.Bitmap.Assign(bmp);
Image1.Picture.SaveToFile('E:/1.bmp');
Socket.SendText('已接收');
end else
if tit = 'meo:' then
begin
text := Socket.ReceiveText;
Memo1.Lines.Add(Socket.RemoteAddress + ':' + Text);
Socket.SendText('已接收');
end;
end;

如題!我的Email: leadyli@163.com
 
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
Buf: array [0..60000] of byte;
ln: LongInt;
Str: TMemoryStream;
bmp: TBitmap;
tit: array [0..3] of char;
text: string;
begin
str := TMemoryStream.Create;
ln := Socket.ReceiveLength;
Socket.ReceiveBuf(tit,4);
if tit = 'bmp:' then
begin
Socket.ReceiveBuf(Buf,ln-4);
Str.WriteBuffer(Buf,ln-4);
bmp := TBitmap.Create;
Str.Position := 0;
bmp.LoadFromStream(Str);
Image1.Picture.Bitmap.Assign(bmp);
Image1.Picture.SaveToFile('E:/1.bmp');
Socket.SendText('已接收');
end else
if tit = 'meo:' then
begin
text := Socket.ReceiveText;
Memo1.Lines.Add(Socket.RemoteAddress + ':' + Text);
Socket.SendText('已接收');
end;
end;

如題!我的Email: leadyli@163.com
 
因为服务器端的ServerSocket1ClientRead出发了两次
ReceiveLength这个不是你客户端发送的数据的长度,而是当前接受缓冲区中数据的长度,所以最好你的客户端将待发数据长度也发送过来
 
應該不是這個問題.
客戶端即使發一個字符也會收到二條'已接收'信息。
在服務端發現收到的信息也是兩條,第一條是正確的,第二條是空的。
 
那看你客户端怎么发的,或者可能是粘包
 
程序好像没问题啊。
 
问题在这里:
end else
if tit = 'meo:' then

最后是发 0 结束的,所以.......
Socket.SendText('已接收1');
Socket.SendText('已接收2');
 
客戶端是用SendText('meo:'+Memo2.Text);
卻不知服務端會觸發兩次。
 
再問個問題:
我用Socket寫個個通信程序,用的是NonBlocking方式,采用一問一答數據包模式,我限制包長度為4K,可是在Internet測試效果不好,速度非常慢,請教有沒有好的方案,有用另開貼300分為報,謝謝!
 
TCP的话这种情况是正常的,流式的都这样。
自己定一下简单协议来保证数据的完整性,至于触发几次不要关心!
 
贴主的问题比较少见:

想发送图象和文件;
根据前4字节区分;

接收BUFFER不能分2次;
要么用文本接收,要么用十六进制接收;
 
To:leaber
我發送才300多K的圖像文件要10多分鐘?實在是太慢了。
To:jlyin
我現在改為以包的形式發送。可是測試發現速度太慢。

各位幫幫忙,一直以來都沒弄過這種東東。
 
发图象我没作过,不过作过多线连接;麻烦的是经常有断线情况;换线
包括GPRS通讯;你的应是只有一个客户端;
下面是我写的简单聊天室服务器接收(ServerSocket1.可省略)
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
i,j:Word;
begin
for j:=0 to linkcount-1 do
begin
Tempstr:=ServerSocket1.Socket.Connections[j].ReceiveText;
if Tempstr<>'' then
begin
memo1.Lines.Add(InttoStr(j+1)+'&amp;ordm;&amp;Aring;&amp;Iuml;&amp;szlig;&amp;frac12;&amp;Oacute;&amp;Ecirc;&amp;Otilde;&amp;pound;&amp;ordm;'+Tempstr);
break;
end;
end;
if pos('DisConnect',Tempstr)=1 then
begin
listbox1.Items.Delete(j);
if j<linkcount-1 then
for i:=j+1 to linkcount-1 do
begin
TempStr:='line'+Inttostr(i);
ServerSocket1.Socket.Connections.SendText(TempStr);
memo1.Lines.Add(Inttostr(i+1)+'&amp;ordm;&amp;Aring;&amp;Iuml;&amp;szlig;·&amp;cent;&amp;Euml;&amp;Iacute;&amp;pound;&amp;ordm;'+TempStr);
end;
for i:=0 to linkcount-1 do
ServerSocket1.Socket.Connections.SendText(NameArr[j]+'&amp;Iuml;&amp;Acirc;&amp;Iuml;&amp;szlig;');
end else
if pos('UserName:',Tempstr)=1 then
begin
NameArr[j]:=copy(Tempstr,10,Length(Tempstr)-9);
if listbox1.Items.IndexOf(NameArr[j])<0 then
listbox1.Items.Add(NameArr[j]);
for i:=0 to linkcount-1 do
ServerSocket1.Socket.Connections.SendText(NameArr[j]+'&amp;micro;&amp;Ccedil;&amp;Acirc;&amp;frac14;&amp;sup3;&amp;Eacute;&amp;sup1;&amp;brvbar;');
end else
for i:=0 to linkcount-1 do
if i<>j then
ServerSocket1.Socket.Connections.SendText(NameArr[j]+':'+Tempstr);
end;
 
procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
s1:string;
desk:tcanvas;
bitmap:tbitmap;
jpg:tjpegimage;
begin
s:=socket.ReceiveText;
if s='gets' then //file://客户端发出申请
begin
try
m1:=tmemorystream.Create;
bitmap:=tbitmap.Create;
jpg:=tjpegimage.Create;
desk:=tcanvas.Create; //以下代码为取得当前屏幕图象
desk.Handle:=getdc(hwnd_desktop);
with bitmap do
begin
width:=screen.Width;
height:=screen.Height;
canvas.CopyRect(canvas.cliprect,desk,desk.cliprect);
end;
jpg.Assign(bitmap); //file://将图象转成JPG格式
jpg.CompressionQuality:=100;//文件压缩大小设置
//m1.clear;
jpg.SaveToStream(m1); //file://将JPG图象写入流中
jpg.free;
m1.Position:=0;
s1:=inttostr(m1.size);
Socket.sendtext(s1); //file://发送图象大小
finally
bitmap.free;
desk.free;
end;
end;
if s='okok' then //file://客户端已准备好接收图象
begin
m1.Position:=0;
Socket.SendStream(m1); //file://发送JPG图象
end;
end;

客户:
procedure TForm1.Button1Click(Sender: TObject);
begin
try
clientsocket1.Close;
clientsocket1.Host:=edit1.text;
clientsocket1.Open; //连接服务端
except
showmessage(edit1.text+#13#10+'未开机或未安装服务程序');
end;
end;

procedure TForm1.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
begin
//showmessage(edit1.text+'通讯成功!');
StaticText1.Caption:=edit1.text+'通讯成功!';
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
if button2.enabled then
begin
clientsocket1.Socket.SendText('gets'); //发送申请,通知服务端需要屏幕图象
end;
end;

procedure TForm1.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
caption:='连接'+edit1.text+'失败';
showmessage(edit1.text+#13#10+'未开机或未安装服务程序');
errorcode:=0;
end;

procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
buffer:array [0..10000] of byte; //设置接收缓冲区
len:integer;
ll:string;
b:tbitmap;
j:tjpegimage;
begin
if c=0 then //C为服务端发送的字节数,如果为0表示为尚未开始图象接收
begin
ll:=socket.ReceiveText;
c:=strtoint(ll); //设置需接收的字节数
clientsocket1.Socket.SendText('okok'); //通知服务端开始发送图象
end else
begin //以下为图象数据接收部分
len:=socket.ReceiveLength; //读出包长度
label1.Caption:=inttostr(c div 1024);
socket.ReceiveBuf(buffer,len); //接收数据包并读入缓冲区内
m.Write(buffer,len); //追加入流M中
if m.Size>=c then //如果流长度大于需接收的字节数,则接收完毕
begin
m.Position:=0;
b:=tbitmap.Create;
j:=tjpegimage.Create;
try
j.LoadFromStream(m); //将流M中的数据读至JPG图像对象J中
b.Assign(j); //将JPG转为BMP
Image1.Picture.Bitmap.Assign(b); //分配给image1元件
finally //以下为清除工作
b.free;
j.free;
clientsocket1.Active:=false;
clientsocket1.Active:=true;
m.Clear;
c:=0;
end;
end;
end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
m:=TMemorystream.Create; //建立内存流
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
m.free;
ClientSocket1.Close;
end;
 
关注此问题!
靠SERVER
 
前段时间关注了这个问题,现在简单说说我的理解吧!
1.对于TCP发送数据方面要注意“粘包”问题,因为在TCP层上有个著名的算法(具体什么名字倒是忘记了^_^),为了传输效率,它会将较短时间里面多次发送的数据包组合后再发送出去。对于这种问题的解决方法应该是在TCP成之上在实现自己的简单协议,可以自己采用一问一答方式!◎当然,简单的就不需要这么复杂了,一般使用的场合是传输数据结构的时候可能会出现几个结构分布在不同的包中,所以要采用确认才可以完整读取相应的信息
2.ReceiveLength的问题,在delphi传输文件或者大数据的时候一定不能使用这个函数来确定接收数据的大小,所以上面朋友写的:
len:=socket.ReceiveLength; //读出包长度
label1.Caption:=inttostr(c div 1024);
socket.ReceiveBuf(buffer,len); //接收数据包并读入缓冲区内
m.Write(buffer,len); //追加入流M中
肯定会出错(也许这次运气好,但不能保证运气一直都那么好^_^),改写后应该这样才可以:
len:=4096;//随便定义,为了效率,还是要仔细斟酌这个值的,因为Receive事件是只要缓存有数据存在(没有取干净),就会触发的!不要担心会取不完的。
len:=socket.ReceiveBuf(buffer,len); //这里返回的len是实际上接收的数据大小,可能与ReceiveLength返回的不同哟^_^
m.Write(buffer,len); //追加入流M中
//文件的结束已否不能通过len<4096来判断,我曾经在这里犯过错误的^_^(因为可能这个包数据读取完毕了,当然它的数据可能符合len<4096,但还有很多包没有来呢^_^)。而应该在开始的时候告诉对方文件大小。然后对方来确认流m的大小判断文件是否传输完毕,这应该是比较保险的方式!
3.使用的一些忠告:如果你想在C/S中间传输大量的自定义数据结构,那么你应该在TCP上实现一个简单的协议(应对粘包问题,当然还有其他一些解决方案,但我认为这种解决方案不错),MIDAS中scktsrvr的实现方式也是如此,它一旦发送一个结构后马上就会进入一个接收过程并试图按对应结构将接收来的数据解析。
如果只是在C/S中传输文件或者其他一类的数据,没有必要实现自己的协议,只要告诉对方文件名称和大小,然后直接传送/接收即可!
4.还有一个问题:就是发送与接收动作并非一一对应的。也就是说:当你有10次发送数据动作时候,也许接收端只是触发一次接收事件(当然,如果你不将缓存区数据取走的话,系统将会在下次进入调度的时候再次发出通知告诉你有数据了。),当然这是在TCP协议中(具体原因就是那个我忘记名字的优化算法了^_^,可以通过setsockopt函数来取消这个算法,它也只是可以确保你每个send动作都会把数据及时传输出去,但并不是说你客户端就会对应一个接收动作的,可能系统等多个包到达后才通知你呢!这与接收端使用的IO模型有关!)。而在UDP中应该是两边一一对应的。
简单说这么多了。前段时间为了做一个文档管理,中间也遇到很多问题,慢慢全部解决了,所以出来说说,高手就不要骂我啦^_^
 
首先感謝各位提供的意見。可能是我沒有說清楚,所以大家的回答好象對不上我的問題。
我自己定義的數據包如下:
type
TPagType = (Info,Files);//類型

type
TMyDataPag = record
PagType: TPagType;
FileType: array [0..9] of Char ;//文件類型
FileName: array [0..20] of Char;//文件名
BufSize: LongWord;//文件長度
SelSize: Word;//本次發送長度
Count: Cardinal;//第幾個包
Buf: array [0..4095] of Char;//文件內容
DateTime: array [0..18] of Char;//時間
end;

type
TMyPacket = packed record
Data: TMyDataPag;
end;

假如文件的大小大於Buf(4K),那麼我發送時就分包發送,而且采用一問一答的方式,直到服務器端處理完第一個包反饋信息再通知客戶端發送第二個包...直到服務器端接收到的內存流大小等於文件的BufSize時才表示完成。

在局域網上測試通過。而且速度很快,10M多的Exe文件只要1分多鐘。

然后在Internet上測試,6K的文件只要2秒鐘,然后傳300多K的文件就出現問題了,速度非常慢,要10多分鐘。這是不能接受的速度,因為我要做的是大陸與美國之間的信息、文件的傳送。

不知道是哪個地方沒處理好,在程序中我沒有使用任何線程。我懷疑一問一答的方式當有很多包的時候是不是很會影響速度?
 
不要采用一问一答的方式
效率很低的
在LAN内也许行得通 但是WEB上绝对不行
因为TCP本身就是个效率低 可靠性高的协议 它需要N次握手
而且你本身也采用了应答机制
我以前就犯过这个错误
我建议使用UDP 或者如果水平高自己开发一个协议
如果用UDP 要注意好 数据的组织 划分合理的包 组织到内存中
因为很可能丢失数据, 在丢失包的时候 要通知服务器重发此包
 
我的经验是:(作数据通讯)

客户端连上后不容易断线;
但多包协议不用应答式:
比如原MODEM通讯有发下个包:重发当前包;
而SOCKET将协议改成总包数+包序数;
有接收数据,就将BUFFERLENGTH长全部移到动态数组(记录);
最后统一解包;

建议你用此方式,并可更改包大小测试速度;
 
看来楼主(leadyli)还是没有仔细看我回的帖子啊。
都已经说了:避免使用结构传递,我看了你的结构,其实就是文件名称和一些信息,很多信息都是不要的。你可以使用状态机来控制两边的通讯,先是传递结构,由于信息量小,你可以采用一问一答方式来确认,当确认后就可以传送文件了,这个时候就不需要使用一问一答形式了,你只要发送文件出去就可以了,底层机制将会使你文件的完整性得到保证!
也就是说采用的是一种混合的方式传送文件,我这里测试过,局域网18M文件传输只要2.8秒(我使用delphi自带的ServerSocket/ClientSocket,使用效果比delphi带的Indy应该不会差的!),广域网没有试过!
 
后退
顶部