老题新问:关于SetSockOpt的问题,恳请Pipi大侠出手……在线等待(150分)

  • 主题发起人 beyondair
  • 开始时间
B

beyondair

Unregistered / Unconfirmed
GUEST, unregistred user!
背景:
正在用TServerSocket和ClientSocket进行大量数据的传输
Server端用阻塞式
传送数据为结构体,其中包括了长度在1000以上的Double类型数组
方案:
因为大数据量传输受底层Socket缓冲区的限制,打算用SetSockOpt来改
变底层缓冲区大小(觉得用流方法一段一段收太麻烦)
问题:
在线程创建时写代码:
Res:=Setsockopt(ClientSocket.SocketHandle,SOL_SOCKET,SO_RCVBUF,
PChar(@Self.Svr_ReceiveParam),SizeOf(Self.Svr_ReceiveParam));
直接运行,不起作用,只能收到不到一半的数组,但在此语句时设断点,当程序执行至此
句后,再继续执行,则可以收全。初步分析Setsockopt执行需要时间,尝试在此句之后加
Sleep(xxx)延时,不起作用,在此句之前延时,则多收到100个DOUBLE类型数据。
百般不解,请大侠们不吝赐教,小生在此谢过!
 
如果是1000个DOUBLE已解决,可以在ReceiveBuf前加上sleep(10),但上了2000多,
出问题,而且上面的停一下再执行也不起作用了,不知SetSockOpt到底能否改变系统的Socket
缓冲区大小,效果究竟如何,MSDN上面说的也不是很明白,有大侠能指点一下么?
分不够可以再加。
 
虽然这个问题对我来说已经不急,但还是想知道答案,我项》》》》》
 
不会吧,有试过的么,
 
通过SOCKET传送,不可超过2K,SetSockOpt我是没用过,不过我遇到大数据量的传送时都是分段的
http://boyeah.cqcnc.com/delphi/soft/chat.rar
我以前做的一个小例子,是传送大文件的,但愿有邦助
 
谁可以给我发一份有关TServerSocket和ClientSocket的资料啊,多谢你们了。
 
(*************************************************************************
* *
* 无所居—服务器端 *
* 版本:1.0 *
* 版权所有(C)无所居 *
* *
*************************************************************************)

unit main;
{*************************************************************************
模块名称:聊天、传文件工具
功能描述:可以多人在局域网内或广域网内聊天、文件的传输
作 者:熊忠国
创建时间:2002/07/02
更新历史:
**************************************************************************}
interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ActnList, ScktComp, ExtCtrls, Menus, StdCtrls, ComCtrls,winsock;

type
POnlineinf=^TOnlineinf;
TOnlineinf=record
username:string[255];
soc:Tsocket;
strIP:string;
end;
TfrmMain = class(TForm)
statusb: TStatusBar;
Lonlines: TListBox;
Rtalk: TRichEdit;
MMenu: TMainMenu;
Aoptions: TActionList;
N1: TMenuItem;
N2: TMenuItem;
N3: TMenuItem;
N4: TMenuItem;
N5: TMenuItem;
N6: TMenuItem;
N7: TMenuItem;
N8: TMenuItem;
N9: TMenuItem;
Panel1: TPanel;
ServerS: TServerSocket;
Abeginser: TAction;
Astopser: TAction;
Aexitpro: TAction;
Aparaset: TAction;
Aabout: TAction;
procedure AstopserExecute(Sender: TObject);
procedure AbeginserExecute(Sender: TObject);
procedure ServerSClientRead(Sender: TObject; Socket: TCustomWinSocket);
procedure ServerSClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure LonlinesDblClick(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure AexitproExecute(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCreate(Sender: TObject);
private
Onlines:Tlist;
{ Private declarations }
public
{ Public declarations }
end;

var
frmMain: TfrmMain;

implementation
const contrallen=16;
{$R *.dfm}

procedure TfrmMain.AstopserExecute(Sender: TObject);
begin
if ServerS.Active then
begin
Servers.Active:= false;
Statusb.SimpleText:='服务已经停止!';
while(Onlines.count<>0) do
begin
dispose(Onlines.First);
onlines.Delete(0);
end;
Lonlines.Clear;
end
else
begin
Messagebox(handle,'服务已停止!','提示',MB_OK);
end;
end;

procedure TfrmMain.AbeginserExecute(Sender: TObject);
begin
if not Servers.Active then
begin
Servers.Active:=true;
statusb.SimpleText:='服务器已激活!';
end
else
begin
Messagebox(handle,'服务器已激活!','提示!',MB_OK);
end;
end;
以下不真的不是很明白啊,浪里白条高手,请指点!我是个新手。
我想利用socket传输信息,信息格式自己定义,比如说
数据长度+数据内容+命令格式等等,然后由接受端分析,根据不同的命令格式来
执行不同的操作等等,我以前用vc做多一点,delphi我刚刚学,我该如何做呢?
请多多指教。
procedure TfrmMain.ServerSClientRead(Sender: TObject;Socket: TCustomWinSocket);
type
nowinfor=(ISININFO,ISOUTINFO,ISTRANSINFO,ISTRANSFILE,INCEPTFILE,REJECTFILE,NONE);
procedure proininfo(inbuf:array of byte;len:integer;s:Tsocket);
var
tempinf:pOnlineinf;
templen:integer;
begin
if inbuf[len-1]<>0 then exit;
tempinf:= new(POnlineinf);
templen:= len-contrallen-1;
tempinf.username[0]:=char(lo(templen));
//showmessage(char(lo(templen)));
copymemory(@tempinf.username[1],@inbuf[contrallen],templen);
tempinf.soc:=s;
tempinf.strIP:=socket.RemoteAddress;
onlines.Add(tempinf);
lonlines.Items.Add('用户名:'+tempinf.username+'连接socket:'+inttostr(s));
Lonlines.OnDblClick(Lonlines);
end;
procedure prooutinfo(inbuf:array of byte;len:integer;s:Tsocket);
var
tempinf:TOnlineinf;
templen:integer;
i:integer;
tempcount:integer;
begin
if inbuf[len-1]<>0 then exit;
templen:=len-contrallen-1;
tempinf.username[0]:=char(lo(templen));
copymemory(@tempinf.username[1],@inbuf[contrallen],templen);
tempinf.soc:= s;
tempcount:= onlines.Count-1;
for i:=0 to tempcount do
if (ponlineinf(Onlines.Items).username=tempinf.username)
and (POnlineinf(onlines.Items).soc= tempinf.soc)
then
begin
dispose(Onlines.Items);
onlines.Delete(i);
break;
end;
tempcount:=Lonlines.Items.Count-1;
for i:= 0 to tempcount do
begin
if ('用户名:'+tempinf.username+'连接socket:'+inttostr(s))=Lonlines.Items
then
begin
Lonlines.Items.Delete(i);
break;
end;
end;
Lonlines.OnDblClick(lonlines);
end;
procedure protransinfo(inbuf:array of byte;len:integer;s:tsocket);
var
i:integer;
sourcename,targetname,talkstr:string;
istransinf:boolean;
idnum:integer;
templen:integer;
basepos:integer;
begin
istransinf:= true;
for i:= 0 to contrallen-1 do
begin
if ((i mod 2 )=0) then istransinf:=istransinf and (inbuf=$F)
else istransinf:= istransinf and (inbuf=$A);
if not istransinf then break;
end;
if not istransinf then
begin
rtalk.Lines.Add('出现不明确的信息包格式,被忽略!');
exit;
end;

for i:= 0 to onlines.Count -1 do
if (ponlineinf(onlines.Items).soc=s) then
begin
sourcename:=POnlineinf(Onlines.Items).username;
break;
end;

if sourcename='' then
sourcename:='某位不知名的聊天者(可能已经下线)';

basepos:=contrallen;
idnum:=Pinteger(@inbuf[basepos])^;
for i:=0 to onlines.count-1 do
begin
if(POnlineinf(onlines.Items).soc=idnum) then
begin
targetname:= POnlineinf(onlines.Items).username;
break;
end;
end;
if targetname='' then
targetname:='某位不知名的聊天者(可能已经下线)';

talkstr:= sourcename+'对'+targetname+'说:';
Pinteger(@inbuf[basepos])^:=s;
inc(basepos,sizeof(integer));
while(basepos<len) and (inbuf[basepos]<>0) do
inc(basepos);

if basepos>=len then
begin
talkstr:='错误的传输流格式,忽略!';
Rtalk.Lines.Add(talkstr);
exit;
end;
templen:=length(talkstr);
setlength(talkstr,templen+basepos-(contrallen+sizeof(integer)));
copymemory
(@talkstr[templen+1],@inbuf[contrallen+sizeof(integer)],basepos-(contrallen+
sizeof(integer)));
rtalk.Lines.Add(talkstr) ;

if (targetname<>'某位不知名的聊天者(可能已经下线)') then
send(idnum,inbuf[0],length(inbuf),0);
end;
procedure protransfile(inbuf:array of byte;len:integer;s:Tsocket);
var
i:integer;
SourceName:string;
TargetName: string;
idnum: integer;
begin
for i:=0 to Onlines.Count -1 do
begin
if(POnlineinf(ONlines.Items).soc=s) then
begin
SourceName:= POnlineinf(Onlines.Items).username;
break;
end;
end;
if SourceName='' then
SourceName:= '某位不知名的聊天者(可能已下线)';
idnum:= Pinteger(@inbuf[contrallen])^;
for i:=0 to Onlines.Count-1 do
begin
if (POnlineinf(Onlines.Items).soc=idnum) then
begin
targetname:= POnlineinf(Onlines.Items).username;
break;
end;
end;
if targetname='' then
targetname:='某位不知名的聊天者(可能已下线)';
Rtalk.Lines.Add(SourceName+'请求向'+targetname+'发送文件');
Pinteger(@inbuf[contrallen])^:= s;
if (targetname<>'某位不知名的聊天者(可能已下线)') then
send(idNum,inbuf[0],length(inbuf),0);
end;
procedure proinceptfile(inbuf:array of byte;len:integer;s:Tsocket);
var
UserId: integer;
TargetName: string;
SourceName: string;
tempstr: string;
i: integer;
begin
UserId:= Pinteger(@inbuf[contrallen])^;
for i:=0 to contrallen-1 do
begin
if POnlineinf(Onlines.Items).soc=UserId then
begin
SourceName:= POnlineinf(Onlines.Items).username;
break;
end;
end;
for I:=0 to contrallen -1 do
begin
if POnlineinf(Onlines.Items).soc=s then
begin
TargetName:= POnlineinf(Onlines.Items).username;
break;
end;
end;
tempstr:=TargetName+'同意接收'+SourceName+'所传送的文件';
Rtalk.Lines.Add(tempstr) ;
send(Userid,inbuf[0],len,0);
end;
procedure prorejectfile(inbuf:array of byte;len:integer;s:Tsocket);
var
UserId: integer;
TargetName: string;
SourceName: string;
tempstr: string;
i: integer;
begin
UserId:= Pinteger(@inbuf[contrallen])^;
for i:=0 to contrallen-1 do
begin
if POnlineinf(Onlines.Items).soc=UserId then
begin
SourceName:= POnlineinf(Onlines.Items).username;
break;
end;
end;
for I:=0 to contrallen -1 do
begin
if POnlineinf(Onlines.Items).soc=s then
begin
TargetName:= POnlineinf(Onlines.Items).username;
break;
end;
end;
tempstr:=TargetName+'不同意接收'+SourceName+'所传送的文件';
Rtalk.Lines.Add(tempstr) ;
send(Userid,inbuf[0],len,0);
end;


var
buf:array of byte;
len:integer;
i:integer;
tag:nowinfor;
nowtag:boolean;
begin
len:=socket.ReceiveLength;
setlength(buf,len);
socket.ReceiveBuf(buf[0],len) ;

if len<contrallen then exit;

nowtag:=true;

for i:=0 to contrallen -1 do
begin
if ((i mod 2)=0 )then nowtag:=nowtag and (buf=$E)
else nowtag:=nowtag and (buf=$A);
if not nowtag then break;
end;

if nowtag then tag:=ISININFO
else
begin
nowtag:= true;
for i:=0 to contrallen-1 do
begin
if ((i mod 2) =0)then
nowtag:=nowtag and (buf=$A)
else nowtag:=nowtag and (buf=$E);
if not nowtag then break;
end;

if nowtag then tag:=ISOUTINFO
else
begin
nowtag:=true;
for i:=0 to contrallen-1 do
begin
if ((i mod 2)=0 ) then
nowtag := nowtag and (buf=$F)
else nowtag:= nowtag and (buf=$A);
if not nowtag then break;
end;
if nowtag then tag:=ISTRANSINFO else
begin
nowtag:= true;
for i:=0 to contrallen-1 do
begin
if ((i mod 2)=0) then nowtag:=nowtag and (buf=$C)
else nowtag:=nowtag and (buf=$D);
if not nowtag then break;
end;
end ;
if nowtag then tag:=ISTRANSFILE else
begin
nowtag:= true;
for i:=0 to contrallen-1 do
begin
if ((i mod 2)=0) then nowtag:=nowtag and (buf=$D)
else nowtag:=nowtag and (buf=$C);
if not nowtag then break;
end;
if nowtag then tag:=INCEPTFILE else
begin
nowtag:= true;
for i:=0 to contrallen-1 do
begin
if ((i mod 2)=0) then nowtag:=nowtag and (buf=$B)
else nowtag:=nowtag and (buf=$A);
if not nowtag then break;
end;
if nowtag then tag:=REJECTFILE else
tag:= NONE;
end;
end;
end;
end;
case tag of
ISININFO:proininfo(buf,len,socket.sockethandle);
ISOUTINFO:prooutinfo(buf,len,socket.SocketHandle);
ISTRANSINFO:protransinfo(buf,len,socket.SocketHandle);
ISTRANSFILE:protransfile(buf,len,socket.SocketHandle);
INCEPTFILE:proinceptfile(buf,len,socket.sockethandle);
REJECTFILE:prorejectfile(buf,len,socket.SocketHandle);
NONE:Rtalk.Lines.Add('出现不明确的信息包格式,被忽略!') ;
end;
end;

procedure TfrmMain.ServerSClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
var
i:integer;
temstr:string;
tempcount:integer;
begin
tempcount:= onlines.Count-1;
for i:= 0 to tempcount do
begin
if (POnlineinf(onlines.Items).soc=socket.SocketHandle) then
begin
dispose(onlines.items);
onlines.Delete(i);
break;
end;
end;
temstr:='连接socket:'+inttostr(socket.sockethandle);
tempcount:=Lonlines.Items.Count-1 ;
for i:=0 to tempcount do
begin
if pos(temstr,Lonlines.items)<>0 then
begin
Lonlines.Items.Delete(i);
break;
end;
end;
Lonlines.OnDblClick(Lonlines);
end;

procedure TfrmMain.LonlinesDblClick(Sender: TObject);
var
I:integer;
templen,len:integer;
buf:array of byte;
lenIP:integer;
tempIP:string;
tempUsername:string;
begin
setlength(buf,contrallen+sizeof(integer));
for i:= 0 to contrallen-1 do
if ((i mod 2)=0 ) then buf:=$A
else buf:=$F;
Pinteger(@buf[contrallen])^:=Onlines.Count;
for i:=0 to onlines.count-1 do
begin
tempUsername:=POnlineinf(Onlines.Items).username;
len:=length(tempUsername);
templen:=length(buf);
tempIP:=POnlineinf(Onlines.Items).strIP;
lenIP:= length(tempIP);
setlength(buf,templen+sizeof(integer)+len+sizeof(integer)+sizeof(integer)+lenIP);
Pinteger(@buf[templen])^:=len;
copymemory(@buf[templen+sizeof(integer)],@tempUsername[1],len);
Pinteger(@buf[templen+sizeof(integer)+len])^:=POnlineinf(onlines.Items).soc;
Pinteger(@buf[templen+sizeof(integer)+len+sizeof(integer)])^:=lenIP;
copymemory(@buf[templen+sizeof(integer)+len+sizeof(integer)+sizeof(integer)],
@tempIP[1],lenIP);
end;

len:=length(buf);

for i:= 0 to onlines.Count-1 do
send(POnlineinf(onlines.Items).soc,buf[0],len,0);
end;
procedure TfrmMain.FormDestroy(Sender: TObject);
begin
while (onlines.count>0) do
begin
dispose(onlines.first);
onlines.Delete(0);
end;
onlines.Free;
end;

procedure TfrmMain.AexitproExecute(Sender: TObject);
begin
close;
end;

procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if ServerS.Active then
servers.Active:= false;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
onlines:=tlist.Create;
end;

end.
 
>>浪里白条
谢谢你的回答,也很感谢你的例子,虽然这和我的要求有所不同,
在默认状态下,一次能发送的数据应该是8192字节左右,确切的应该
是4倍的TCP Data Size 或与8192接近的TCP Data Size的偶数倍值,
在以太网环境下,默认值为8760字节,如有不对的地方还请指正。
>>andy_yans
你要的东西基本上介绍DELPHI网络编程的书上都会有的,我手头
是没有电子文档的,所以爱莫能助了,不过关于你说的要发送命令格
式的问题,如果你发送的数据不太大(小于8K)的话,我建议你直接发
送结构体比较好
var MyRecord:TMyrecord
socket.SendBuf(MyRecord,SizeOf(MyRecord));
socket.ReceiveBuf(MyRecord,SizeOf(MyRecord));
另外能不能请你把贴出来的代码删掉,太长啦,

我的问题也查到了一些眉目,但没能最终搞定,还请知道的大侠不吝赐教,
呵呵,DFW的精神应该是交流无止境吧

一次能传送的最大数据量,涉及到TCP滑窗的概念,本来TCP传输可靠的网络
协议,它在发送端发送数据一个数据包之后要进行确认,回发一个ACK(命令
正确应答),引入TCP滑窗的概念就是为了不用每个包都要确认,而且是先发
N个包(N小于等于TCP滑窗所能容纳的包的个数),再等待ACK,如果发送端
没有收到ACK,是不会再发送数据的,如超时则重发上一组包。但以我个人的
理解,这只是一个概念,真正的实现可能还是要用Buffer吧,所以和Socket
的缓冲区有莫大的关联。
如果想改变一次能接收的最大数据量,有两种方法(或许更多,不过我不知道了)
1.在注册表中改变这个系统中默认的滑窗的大小
TcpWindowSize
Key: Tcpip/Parameters
Value Type: REG_DWORD - Number of bytes
Valid Range: 0 - 0xFFFF
Default: The smaller of 0xFFFF
OR
(The larger of four times the maximum TCP data size on the network
OR
8192 rounded up to an even multiple of the network TCP data size.)
The default is 8760 for Ethernet.
Description: This parameter determines the maximum TCP receive window
size offered by the system. The receive window specifies the number of
bytes a sender may transmit without receiving an acknowledgment. In general,
larger receive windows will improve performance over high (delay * bandwidth )
networks. For highest efficiency, the receive window should be an even multiple
of the TCP Maximum Segment Size (MSS).
这是系统的全局变量,对所有的基于TPC的操作都有影响
2.用SetSockOpt来改变具体的某一个Socket的缓冲区
这个就是我上面问的问题了。
这是个局部变量

在Windows2000里,所有于与TCP有关的参数都在下面这两个键中

HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services
Tcpip/Parameters
Adapter Name/Parameters/Tcpip

可是我没找到TcpWindowSize这个键值,不知道是不是要自己创建才行,
请高手指点一下。
另外还是上面那个使用SetSockOpt的问题,也请知道的大侠帮帮忙,在此多谢了。
具体更多信息,大家可以在MSDN里搜索TcpWindowSize,希望大家热烈讨论


 
有用过的没》》》》》》》》》》》》》》》
 
再过两天没有结果就结束贴子了
 
接受答案了.
 
顶部