那位高手帮我看看这个socket多线程 服务器 的问题..... ( 积分: 200 )

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

baiduan

Unregistered / Unconfirmed
GUEST, unregistred user!
代码: server unit;
unit G_unit;

interface
uses
Windows, Messages, SysUtils, Variants, Classes, Controls,StdCtrls,idwinsock2;

{主线程}
type
TmainThread =class (Tthread)
private
mainFMhandle:Thandle;
{ Private declarations }
public
procedure Execute;override;
constructor create(CreateSuspended: Boolean;HD:Thandle);
end;

{工作线程}
TworkThread=class(Tthread)
private
mainFMhandle:Thandle;
clientSock:Tsocket;
public
function setClientSock(c:Tsocket):integer;
procedure Execute;override;
constructor create(CreateSuspended: Boolean;HD:Thandle);
end;

type
threadSet=record
count:integer;
list:array of TworkThread;
end;

{管理线程}
type
TmangerThread=class(Tthread)
private
mainFMhandle:Thandle;
workThreadlist:ThreadSet;
public
constructor create(createSuspended:boolean;HD:thandle;ls:ThreadSet);
procedure Execute;override;
end;




function insertFDset(fds:Tfdset;sock:Tsocket):integer;
function clearSocket(FDs:Tfdset;csock:Tsocket):integer;
var
CsockNum:integer;
Sockset:Tfdset;
sock:Tsocket;
ghmutex:Thandle;
implementation


function clearSocket(FDs:Tfdset;csock:Tsocket):integer;
var
i:integer;
dresult:Dword;
begin
result:=-1;
while true do
begin
dresult:=WaitForSingleObject(ghmutex,INFINITE);
if dresult=WAIT_OBJECT_0 then
begin
for i:=0 to fd_setsize-1 do
begin
if fds.fd_array=csock then
begin
fd_clr(csock,fds);
closesocket(csock);
result:=i+1;
exit;
end;
end;
end;
end;
end;


function insertFDset(fds:Tfdset;sock:Tsocket):integer;
var
i:integer;
dresult:Dword;
begin
result:=-1;
while true do
begin
dresult:=WaitForSingleObject(ghmutex,INFINITE);
if dresult=WAIT_OBJECT_0 then
begin
for i:=0 to fd_setsize-1 do
begin
if fds.fd_array=invalid_socket then
begin
fd_set(sock,fds);
result:=i+1;
exit;
end;
end;
end;
end;
end;


{ TmainThread }
//=======================================================
//主线程负责接受socket,如果连接数64则关闭连接
//=======================================================
constructor TmainThread.create(CreateSuspended: Boolean; HD: Thandle);
begin
self.mainFMhandle:=hd;
inherited create(createSuspended);
end;

procedure TmainThread.Execute;
var
qutoSock:word;
RU:integer;
wdate:TWSADATA;
hostName:array [0..255]of char;
HEnt:PHOSTENT;
ipstr:string;
sdr:psockaddr;
Csock:Tsocket;
buf:array [0..2048] of char;
i:integer;
s:string;
begin
try
qutosock:=makeword(2,2);
RU:=WSAStartup(qutosock,wdate);
if ru<>0 then
begin
s:='WSAStartup 初始化失败';
postmessage(self.mainFMhandle,wm_char,0,integer(pchar(s)) );
Exit;
end;
{if (LOBYTE(wdate.wVersion )<>2) then
begin
s:='WSAStartup 版本不一致';
postmessage(self.mainFMhandle,wm_char,0,integer(pchar(s)));
exit;
end;}
RU:=gethostname(@hostname,256);
if RU<>0 then
begin
s:='Gethostname 失败';
postmessage(self.mainFMhandle,wm_char,0,integer(pchar(s)));
exit;
end;
HEnt:=gethostbyname(@hostname);
ipstr:=inet_ntoa(Pinaddr(HEnt.h_address_list^)^);//取得pcIP地址
sdr:=new(psockaddr);
sdr.sin_addr.S_addr:=inet_addr(Pchar(ipstr));
sdr.sin_family:=2;
sdr.sin_port:=htons(27815);
fd_zero(sockSet);

for I:=0 to fd_setsize-1 do
begin
sockSet.fd_array:=invalid_socket;
end;

sock:=socket(2,1,0);

bind(sock,sdr,sizeof(Tsockaddr));
listen(sock,5);

while true do
begin
csock:=accept(sock,nil,nil);
// RU:=insertFDset(sockSet,csock);
fd_set(csock,sockset);
if RU=-1 then
begin
s:='服务器忙';
postmessage(self.mainFMhandle,wm_char,0,integer(pchar(s)));
closesocket(csock);
end else
begin
s:='客户端进入任务序列';
postmessage(self.mainFMhandle,wm_char,0,integer(pchar(s)));
end;
end;
// PostThreadMessage
{ while(true) do
begin
Csock:=accept(sock,nil,nil);
if (Csock=0)or(Csock=-1) then
begin
continue;
end;
RU:=1;
while RU>0 do
begin
RU:=recv(Csock,buf,sizeof(buf),0);
showmessage(strpas(buf));
end;
exit;
end;}
finally
closeSocket(sock);
wsaCleanup();
end;
end;

{ TmangerThread }
//====================================================================
//管理者线程负责select可用的套接字,然后选择一个工作者线程让他完成工作
//====================================================================
constructor TmangerThread.create(createSuspended: boolean; HD: thandle;
ls: ThreadSet);
begin
self.mainFMhandle:=hd;
self.workThreadlist:=ls;
inherited create(createSuspended);
end;

procedure TmangerThread.Execute;
var
i,j:integer;
qutoSock:word;
RU:integer;
wdate:TWSADATA;
s:string;
begin
try
qutosock:=makeword(2,2);
RU:=WSAStartup(qutosock,wdate);
if ru<>0 then
begin
s:='WSAStartup 初始化失败';
postmessage(self.mainFMhandle,wm_char,0,integer(pchar(s)) );
Exit;
end;
{if (LOBYTE(wdate.wVersion )<>2) then
begin
s:='WSAStartup 版本不一致';
postmessage(self.mainFMhandle,wm_char,0,integer(pchar(s)));
exit;
end;}
while not Terminated do
begin
select(0,@sockSet,nil,nil,nil);
while true do
begin

for j:=0 to fd_setsize-1 do
begin
if (sockSet.fd_array[j]=invalid_socket) or
(sockSet.fd_array[j]=-1 )then
begin
continue;
end;
if j>=fd_setsize-1 then break;

I:=0;
while true do
begin
if (workThreadlist.list.Suspended) then
begin
workThreadlist.list.setClientSock(sockSet.fd_array[j]);
workThreadlist.list.Resume;
break;
end;
inc(i);
if i>=workThreadlist.count-1 then
begin
sleep(1000);
i:=0;
end;
end;
sleep(3000);
break;
end;
end;
end;
finally
wsaCleanup();
end;
end;

{ TworkThread }
//=================================================================
//工作者线程主要接受accept,完成后并将FD_set相应的socket置为无效
//=================================================================
constructor TworkThread.create(CreateSuspended: Boolean; HD: Thandle);
begin
self.mainFMhandle:=hd;
inherited create(createSuspended);
end;

procedure TworkThread.Execute;
var
qutoSock:word;
RU:integer;
wdate:TWSADATA;
s:string;
i:integer;
buf:array [0..2048] of char;
begin
try
qutosock:=makeword(2,2);
RU:=WSAStartup(qutosock,wdate);
if ru<>0 then
begin
self.Terminate;
end;
{if (LOBYTE(wdate.wVersion )<>2) then
begin
self.Terminate;
end; }
//CreateMutex
while not Terminated do
begin
if (self.clientSock=0) or (self.clientSock=-1) then
begin
clearSocket(sockset,self.clientsock);
self.Suspend;
end;
{如果在列表里}
if fd_isset(self.clientSock,sockset) then
begin
RU:=1;
while RU>0 do
begin
RU:=recv(self.clientSock,buf,sizeof(buf),0);
s:=strpas(buf);
postmessage(self.mainFMhandle,wm_char,0,integer(pchar(s)) );
if RU<=0 then
begin
clearSocket(sockset,self.clientSock);
s:='客户端'+inttostr(self.clientSock)+'结束';
postmessage(self.mainFMhandle,wm_char,0,integer(pchar(s)) );
closeSocket(self.clientSock);
end;
end;
end;
self.Suspend;
end;
finally
wsaCleanup();
end;

end;


function TworkThread.setClientSock(c: Tsocket): integer;
begin
self.clientSock:=c;
end;

end.
======================================分割================================
客户端:
var
FStream:TFileStream;
begin
//
try
c1.Connect(5000);
if c1.Connected then
begin
try
FStream:=TfileStream.Create('C:/1.txt',fmOpenRead);
FStream.Position:=0;
FStream.Seek(0,0);
c1.WriteStream(Fstream,true,false);
finally
FStream.Free;
c1.Disconnect;
end;
end
else
showmessage('连接失败');
except
showmessage('error'+' '+inttostr(getlasterror));
end;
end;
==========================分割==========================================
问题:
开了5个workThread,1 mainThread,1 mangerThread,
第一次传输文件走通,然后关闭后重新打开客户端就出现
10057错误.
那为老大知道为什么啊....
 
你的代码太简单,建议加上日志, 便于分析问题, 应在代码各SOCKET操作每一步骤加上相关成功或异常日志,以进行跟踪分析。
10057表示socket未连接, accept成功后应输出加上对端的IP和端口
在收到FD_CLOSE应主动断链, 看你代码主要是收到消息应断链,
短连接在客户端和服务端之间不必加上心跳消息处理, 不过还是应该完善一下其它

客户端第一次退出,可在服务端先检查一下客户端连接是否存在?

还有:
1)写服务端程序不应该使用sleep, 尽量避免这种用法, 效率上有很大的问题
2)在WINDOWS上你使用的SELECT模型,这种方式在UNIX中开发用得比较多,但是WINDOWS上是最土且效率不高的一种方式, 应该避免, 最次也要用异步选择模型
 
终于有人回答了,兴奋.....

to htw 大侠:
第一次写这个东西,很多东西都不懂,select 和FD_SET还是上周参考了东西,才明白的,
fd_close是第一次看见,我再去msdn查看下.
还有一些不明白.
1 短连接在客户端和服务端之间不必加上心跳消息处理?
就是说我这里还不用+吧.我就是传输个文件只类的.
2 收到消息应断链?
怎么收消息 ,断链=CloseSocket()?
3 sleep是用在管理线程中的,这样也会影响效率吗?
4 一般windows用什么模型?就是异步?为什么select 在unix可以,windows不行?
如果问题太复杂,有什么参考书或资料介绍下也好啊...
 
WINDOWS网络编程技术
这个书不错.
 
异步函数WSAAsyncSelect 看了,是个类似触发的函数.
不过来不及了.先做好这个,下次更新时改写好了。

现在问题是本机上要等待一会儿才能client才能重新连接server
否则Error.而client 在局域网其他机器上就1直Error,
Error code =10057...

比较头疼..
 
要快,就直接使用INDY等控件,专注于业务实现好了。
 
to 东兰
不,用控件不能了解深一点的东西,长此以往,就只能用控件了.

select 果真效率不行啊,我本以为它能阻塞的,结果没有,+了日志
才看出来,管理线程写的垃圾了....
 
终于搞定.哎,走的太快回忽略很多东西,所以到后头就难走了,
还是小步前进比较扎实.
 
都是先用后写。不了解一样东西,也就没法自己写出来的。
要追求性能,或有特殊要求的才自己写,什么都自己写,也是写不出来的。
 
哎,客户端每次要等待一会(几分钟)才能在次连接服务器,怎么回事啊.
我重新开贴,请两位关注下....
 
多人接受答案了。
 
后退
顶部