当客户端程序断开后,服务端程序为什么会报错?(50分)

  • 主题发起人 主题发起人 tsedlinux
  • 开始时间 开始时间
T

tsedlinux

Unregistered / Unconfirmed
GUEST, unregistred user!
这是一个客户/服务程序,当建立连接,并进行密码检查后,如果断开它们的连接,则会出严重的错误,使服务端程序当掉

客户端断开连接后服务端通过以下方法关闭SERVER,
server.socket.close;
server.Close;
Server.active:=false;
但任意一种方法都会出错,为什么?
如果不进行密码检查,则断开连接不会出问题,也就是没有给LOGGED赋值时。
下面是报的错误,有两种:
1、access violation as address 010d378b . write of address 1A7F6F0A

2、project1.exe raised exception class ESocketError with message ' windows socket error : (10038), on API 'closesocket' '.



以下是源程序
服务端程序
unit Unit1;
interface
uses
Windows,Messages, tlhelp32,SysUtils, Classes,
Controls, Forms, ComCtrls, StdCtrls,ExtCtrls, ScktComp, Dialogs;
type
Tproject = class(TForm)
server : TServerSocket;
procedure FormCreate(Sender: TObject);
procedure ServerClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure ServerClientRead(Sender: TObject; Socket: TCustomWinSocket);
procedure ServerClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);

published
procedure command(sender:Tobject;socket:TCustomwinsocket;s:string);
function leftstr(s:string;num:integer):string;

private
num : integer;
logged : string;
port : integer;

public
ccc : bool;//密码认证 密码为a
ddd : bool;//通过验证标志

{ Public declarations }
end;

var
project : Tproject;

implementation

{$R *.DFM}
//************限定命令串的长度为NUM长度*****************************************************************
function tproject.leftstr(s:string;num:integer):string;
var
str:string;
begin
str:=s;
setlength(str,num);
leftstr:=str;
end;

//*********窗体初始化********************************************************************
procedure Tproject.FormCreate(Sender: TObject);
begin
port := 0;
logged := '';
num := 0;
ccc := false;
ddd := false;
server.Active:=true;
end;

//*********客户端断开连接后,引发此函数*************************************************************
procedure Tproject.ServerClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
if socket.RemoteAddress=logged then
begin
logged := '';
port := 0;
ccc := false;
ddd := false;
server.socket.close; 使用这三种方法都会出错,为什么?
// server.Close;
// Server.active:=false; //此处出错
end;
end;

//***********客户端发来消息******************************************************************
procedure Tproject.ServerClientRead(Sender: TObject;Socket: TCustomWinSocket);
var
s : string;
leng : integer;
buffer : array[0..9999] of char;

begin
leng := socket.ReceiveBuf(buffer,socket.ReceiveLength()); //接收数据包并读入缓冲区内
buffer[leng] := #0;
s := buffer;

command(Sender,Socket,s);
end;

//***************命令类子处理程序**************************************************************
procedure tproject.command(sender:Tobject;socket:TCustomwinsocket;s:string);
var
temp : integer;
str : string;
leng : integer;
long_string : integer;
str_tran : string;
begin
leng:=length(s);
for temp:=1 to leng-1 do
begin
s[temp]:=s[temp+1];
end;
setlength(s,leng-1);
str:=s;
for temp:=1 to leng-3 do
str[temp]:=s[temp+2];
setlength(str,leng-3);
long_string:=length(s);
str_tran:=(leftstr(s,long_string)); //调用函数,将S中取出LONG_STRING个长度的字串


if str_tran='a' then
ccc:=true;

if not(ddd) then
begin
if ccc=true then
begin
ddd:=true;
logged:=server.socket.connections[0].RemoteAddress;
port:=server.Socket.connections[0].RemotePort;
socket.SendText('m00')
end;
end;

if ddd=true then //通过验证后
begin
showmessage('成功登录');
end
else socket.SendText('m03');
end;

//***********发生错误******************************************************************
procedure Tproject.ServerClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
ErrorCode:=0;
ccc:=false;
ddd:=false;

end;

//************退出程序*************************************************************
procedure Tproject.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin

if server.Active=true then //关闭连接
server.Active:=false;


end;
end.



客户端程序


unit Unit1;

interface

uses
Windows, Graphics,SysUtils, Forms,
StdCtrls,ExtCtrls, ComCtrls,
ToolWin,Dialogs, Controls, ScktComp,
Buttons,Classes;

type
TForm1 = class(TForm)
Client: TClientSocket;
Edit2: TEdit;
connectserver: TSpeedButton;
Edit3: TEdit;
disconnect: TSpeedButton;
login: TButton;

procedure ClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure ClientRead(Sender: TObject; Socket: TCustomWinSocket);

procedure ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);


procedure FormCreate(Sender: TObject);

procedure connectserverClick(Sender: TObject);
procedure disconnectClick(Sender: TObject);

procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure loginClick(Sender: TObject);

procedure ClientConnect(Sender: TObject; Socket: TCustomWinSocket);


private

{ Private declarations }
public

result:integer;

{ Public declarations }
end;

var
buffer:array[0..9999] of char;
Form1: TForm1;


implementation

//uses Unit3, Unit6;

{$R *.DFM}

//******************************************************************************
procedure TForm1.ClientError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
begin

application.MessageBox('发生错误','',MB_OK+MB_ICONinformation);
client.active:=false;
errorcode:=0;
connectserver.Enabled:=true;
disconnect.Enabled:=false;
end;

//******************************************************************************
procedure TForm1.ClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
leng : integer;
sign_tran_ser : char; //从服务端传过来的信息取第一个字符做标志
frm_string : string;//窗框内容

begin
leng:=client.Socket.ReceiveBuf(buffer,client.socket.ReceiveLength()); //接收数据包并读入缓冲区内
begin
buffer[leng]:=#0;
sign_tran_ser:=buffer[0];
leng:=strtoint(copy(buffer,2,2));

case leng of
0: frm_string:=' 成功登录 ';
1: frm_string:=' 发送出错!';
3: frm_string:=' 权限错!';
else
end;
form1.Caption:=frm_string;
end;

end;




//******************************************************************************
procedure TForm1.ClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin

connectserver.Enabled:=true;
disconnect.Enabled:=false;

end;

//******************************************************************************
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin

if client.Active=true then
begin
if application.MessageBox('还未断开连接,真的退出?','确信要退出吗?',MB_ICONINFORMATION+MB_YESNO) =idYes then
begin
client.Close();
end
else
end;

end;

//******************************************************************************
procedure TForm1.FormCreate(Sender: TObject);
begin
connectserver.Enabled:=true;
disconnect.Enabled:=false;
end;


//****************************************************************************8
procedure TForm1.connectserverClick(Sender: TObject);
begin
client.address:=edit2.Text;
client.Active:=true;
edit3.SetFocus;
end;

//****************断开连接*************************************************************
procedure TForm1.disconnectClick(Sender: TObject);
begin
client.Active:=false;
end;

//******************************************************************************
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
client.active:=false;
close;
end;


//******************************************************************************
procedure TForm1.loginClick(Sender: TObject);
begin
client.socket.SendText('c'+edit3.text);
end;

//******************************************************************************
procedure TForm1.ClientConnect(Sender: TObject; Socket: TCustomWinSocket);
begin
connectserver.Enabled:=false;
disconnect.Enabled:=true;
end;
end.


 
没有人知道吗?还是分少?
 
代码太长,不想看!:)
 
难道大富翁里的高手不屑于这个小问题吗?
 
你最好等发生断开连接之后再用
closesocket函数
 
我知道了,哈哈,我刚才查到错误10038
因为发生ServerClientDisconnect事件的时候,对应的客户套接字就已经是
无效的,所以关闭的时候出错,你在关闭之间判断一下这个套接字是否有效,
Client<>INVALID_SOCKET再关闭,
 
//*********客户端断开连接后,引发此函数*************************************************************
procedure Tproject.ServerClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
if socket.RemoteAddress=logged then
begin
logged := '';
port := 0;
ccc := false;
ddd := false;
// 既然已经进入了 ServerClientDisconnect 事件,
// 那么 Socket.Connected 已经为 False了,
// 也就是说连接已经断开,不用再手动断开了。
end;
end;
 
另外,
2、project1.exe raised exception class ESocketError with message ' windows socket error : (10038), on API 'closesocket' '.
这个错误其实是Delphi调试状态捕捉的控件自定义错误,仅在调试时出现,编译成EXE就没有了。
 
谢谢各位,我已经知道报错的原因了,可是当客户端程序断开与服务端的连接时,服务端还是不能关闭它自己打开的端口,客户端程序还是可以连上去,我想让这个程序实现当客户端断开连接后服务端关闭端口,客户端不能再连接服务器,当一定的条件满足时,比如到一定的时间再SERVER.ACTIVE:=TRUE。
 
我建议你断开前判断连接数目,如果当前连接书目为0,再关闭server
 
我觉得没有这个必要。服务器频繁的开关端口对程序的性能有影响,建议你增加一个
变量标志[SocketClose:Boolean],在OnClientConnect中判断
if SocketClose then Socket.Close;
在ServerClientDisconnect中赋值:SocketClose :=True;
然后在你认为适当的时候 SocketClose :=False;
这样就可以很方便的控制客户端的连接。
 
To: pengle
这种方法还不如进行OnClientConnect和OnClientDisconnect记数,你这样做万一有多个Client连接就惨了。
 
谢谢各位的关心,问题我已经解决了
就是加一个计数器

//*********客户端断开连接后,引发此函数*************************************************************
procedure Tproject.ServerClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
if socket.RemoteAddress=logged then
begin
logged := '';
port := 0;
ccc := false;
ddd := false;
timer1.Enabled :=true;
end;
end;


procedure Tproject.Timer1Timer(Sender: TObject);
begin
timer1.Enabled:=false;
if server.Socket.ActiveConnections=0 then
begin
server.Active:=False;
server.socket.close; //这句不知道是不是真有用
end;
end;
 
多人接受答案了。
 
后退
顶部