做了个socket的实验,却得出意想不到的结果。大家来看看呀。说得好我会再加分。(100分)

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

lncd

Unregistered / Unconfirmed
GUEST, unregistred user!
//server端我是这样写的。
procedure TForm1.ServerSocket1ClientRead(Sender:TObject;
Socket:TCustomWinSocket);
var
GetText:string;
begin
GetText:=Socket.ReceiveText;
memo1.lines.add('接收了来自:'+Socket.RemoteAddress+'的消息:'+GetText);
end;

//Client端我是这样写的。
procedure TForm1.Button2Click(Sender: TObject);
var
i:integer;
begin
for i:=0 to strtoint(edit2.text) do//我会在edit2中填一个数字,以确定循环的次数
begin
ClientSocket1.Socket.SendText('第'+inttostr(i)+'条消息。');
//sleep(0); //加上这一句就不会丢包了。
end;
end;

//问题来了。
当edit2.text>1000时。从server的memo1中观察到会显示许多包没有被打印出来 。
我想象中应该是:
接收了来自:127.0.0.1的消息:第0条消息。
接收了来自:127.0.0.1的消息:第1条消息。
接收了来自:127.0.0.1的消息:第2条消息。
接收了来自:127.0.0.1的消息:第3条消息。
接收了来自:127.0.0.1的消息:第4条消息。
...
实际上却是:
接收了来自:127.0.0.1的消息:第0条消息。第1条消息。第2条消息。第3条消息。...
也就是说,server端一次显示了由client多次发送的消息。
而且在600左右条以后开始丢失部分消息了。

我做这个试验的初衷是想试一下当用户很多时(这种测试方法正确吗?)。会是什么情况。却得到了这样的结果。
希望这方面的行家能给我解释一下这个现象。我对socket方面不太了解。
包括与此相关的一些知识原理都可以谈。说得好我会再加分的。
 
WinSocket传输就是有这个Bug. 看看VC知识就知道了,那里对 Socket将的比较详细:Socket传输数据包括数据文报套接字和数据流传输。下午见
 
不应该出现这样的情况
 
上面的现象是完成正确的。
对client端,socket只是往tcp/ip的底层缓冲里写数据,对srv端,则是从缓冲里读数据
所以在srv端取数据是,并不知道客户端是几次发送的数据,能读多少是多少。tcp/ip只能保证数据的顺序和完整性,但不能保证client和srv每次发送和接收的量一样(可以想象的
出来,因为winsock中是可以控制发送量和接收的量的),如果你想得到想象中的效果,
则需要加时间延时或者发送一个数据包后断开后再连接发送。

对于你的client端的发送,需要判断发送是否成功,因为如果就算连接的socket没有错误
异常,但是如果是缓冲区满了,发送应该会返回失败的(一般是send time out)。
 
我知道怎么解决了,
在client端:
clientType设为ctNonBlocking时,会丢消息。
clientType设为ctBlocking时,就不丢消息。
在server端:
servertype设为stNonBlocking收得到消息。
servertype设为stThreadBlocking收不到消息。

//但我想知道不不仅是如何解决这个问题,
我想知道这现象背后的原理,以及涉及到的一些更深层的原理.
请高手指教呀。或者指点一下哪里能找到这些资料。
 
在。NET 的CSDN里可以查找到相关详细原理说明。数据报传输不安全。如果想用,可以经过验证后再发下一个数据包。改用流传输比较好。
 
对于 CSocket 客户端对象,除非需要数据文报套接字,否则通常应使用默认参数来 Create 该对象。对于 CSocket 服务器对象,则必须在 Create 调用中指定端口。

注意 CArchive 不适用于数据文报套接字。如果想将 CSocket 用于数据文报套接字,必须像使用 CAsyncSocket 那样使用该类,即不带存档。因为数据文报是不可靠的(不保证送达,并且可能重复或顺序不对),它们不能通过存档与序列化兼容。而您期望序列化操作可以可靠地、按顺序完成。如果试图将带 CArchive 对象的 CSocket 用于数据文报,则 MFC 断言失败。
 
我先自己试着解释一下,不对的话请多多指教哟。

在server端。每个由TServerSocket建立的socket都有一个缓冲区。
这个缓冲区就象一个容积一定的水果框.
OnClientRead事件会定时查看框,如果框里有苹果,就把框里的所有苹果倒到自己的口袋。
但由于动作太慢。往往从发现有苹果到倒倒苹果时,别人(client端)又放了好些苹果进去。
而Client端,会用Socket.sendText方法往框里放苹果。
如果clientType设为ctNonBlocking;则Socket.sendText会闭着眼睛放苹果。哪怕框满了还是继续。结果苹果掉了一地。
掉出框的苹果,服务器端的OnClientRead当然就取不到了。(这就是我消息丢失的原因)

如果clientType设为ctBlocking;则Socket.sendText会睁着眼睛放苹果。如果框满了,他就等,直到框框有空了才继续往里放。
这时服务器端的OnClientRead就能取到全部苹果了。
 
呵呵,这个比喻真是比较搞笑[:D]
但是这个地方说的不对:
》如果clientType设为ctNonBlocking;则Socket.sendText会闭着眼睛放苹果。哪怕框满了
》还是继续。结果苹果掉了一地。
》掉出框的苹果,服务器端的OnClientRead当然就取不到了。(这就是我消息丢失的原因)

如果是框满了,应该都是装不进去了,发送会直接返回失败。如果发送成功的消息包,除非
对方有问题,否则是绝对不会丢失的,这个是tcp/ip能够保证做的,而udp则不行。

还有就是srv端不是定时去框里取,而是如果筐里有东东,框会通知srv去取的。
 
chenxz:
//如果是框满了,应该都是装不进去了,发送会直接返回失败。如果发送成功的消息包,除非对方有问题,否则是绝对不会丢失的,这个是tcp/ip能够保证做的,而udp则不行。

可是我试了以下方法:。
用try...except
或者OnError事件。都没有得到错误。

你是用什么方法截获错误的呀。
 
很正常,建议改用Sendbuf和Receivebuf,就没有这个问题了
 
是sendtext不成功啊,你看看sendtext的返回值。
 
呵呵,我感觉这个帖子没必要了,Delphi的
TClientSocket和TServerSocket没什么BUG,
只有不懂如何使用的人才说他有BUG,[:D]
 
chenxz:
>>是sendtext不成功啊,你看看sendtext的返回值。

我试过sendtext的返回值了。它无法判断苹果是装进了框子还是掉到了地上。
 
一次发送不对应一次接收
多次发送,如果不大于8192长,一次就可以收完。
一次发送,如果大于8192,在onread里循环接收就可以把一次发送的接收完全。
 
jiajiajia888:
8192你是如何得出来的,是做试验还是在资料上找的。
这是每个socket开的缓冲区的固定大小吗?
 
lncd:这其实和操作系统中的生产者-消费者一样,你可以在客户端发出去以后,一段不长的时间内要求如果服务器端收到就发出一个收到与否的确认信息,如没收到,客户端重新发送;如果收到,就发下一帧。(浊见,请各位指点)
 
后退
顶部