其实我的做法跟楼上的出发点是基本一致的因为我考虑到一台主机可以同时有几个终端的,不同用户在共享一个网卡的情况 所以不同用户间的通讯主要是根据唯一用户编号来识别 (当然从理论上可以说Socket.Handle是唯一的,但是消息发送方不可能在发送消息之前就需要预先知道对方的Socket.Handle) 所以我维护了这么一个数组 type PInfoClient=^TInfoClient; TInfoClient=record Handle: integer; //客户端套接字句柄 Socket:TCustomWinSocket; //客户端套接字 Name:string; //客户端计算机名称 Address:string; //客户端计算机IP地址 UserNO:string;//用户编号 Used: boolean; //客户端联机标志 end; type TInfoClientList=array of TInfoClient;访问该数组的时候使用独立线程来操着,用TMultiReadExclusiveWriteSynchronizer来实现线程同步就会收到高效简单的效果其中属性UserNO是通过在TClientSocket.OnConnect中发过去,TServerSocket.OnClientRead中写入的发一个今天封装的类供大家探讨 GetInfo为从传输文本从截取相关命令字符窜的函数unit SvrClt;interface uses Communication,D7ScktComp,ExtCtrls,SysUtils,Classes,Forms,Define; type TSvrClt=class(TServerSocket) private FMaxConnectCount:integer; //最大连接数 FCurrConnectCount:integer;//当前连接数目 //FClients:TList;使用该方法只用使用ip进行转发不适合多用户共享一网卡的情况 procedure ClientSocketConnect(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocketError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);//virtual;abstract; protected public constructor create(const ServerPort:integer; MaxConnectCount:integer;AOwner: TComponent); destructor destroy;override; function SendMsg(const pktStr:string):boolean; //property Clients: TList read GetClients; end; implementation constructor TSvrClt.create(const ServerPort:integer; MaxConnectCount:integer;AOwner: TComponent); begin inherited create(AOwner); FMaxConnectCount:=MaxConnectCount; Port:=ServerPort; OnClientConnect:=ClientSocketConnect; OnClientRead:=ClientSocketRead; OnClientError:=ClientSocketError; OnClientDisconnect:=ClientSocketDisconnect; Active:=true; end;destructor TSvrClt.destroy; begin Active:=False; Close; inherited destroy; end;procedure TSvrClt.ClientSocketConnect(Sender: TObject; Socket: TCustomWinSocket); var s:string; i:integer; begin for i:=0 to FMaxConnectCount-1 do begin if not m_infosys[i].Used then //looking for the idle TCustomWinSocket ,and put down this TCustomWinSocket begin m_infosys[i].Socket:=Socket; m_infosys[i].Used:=True; m_infosys[i].Name:=''; m_infosys[i].Address:=Socket.RemoteAddress; m_infosys[i].UserNO:=''; exit; end;//end if end;//end for iend;procedure TSvrClt.ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket); var i:integer; begin //release the TCustomWinSocket in array m_infosys of the Logout user`s TCustomWinSocket on server side for i:=0 to FMaxConnectCount-1 do begin if Socket.Handle=m_infosys[i].Socket.Handle then begin m_infosys[i].UserNO:=''; m_infosys[i].Address:=''; m_infosys[i].Handle:=-1; m_infosys[i].Name:=''; m_infosys[i].Address:=''; m_infosys[i].Used:=false; end;//end if end;//end for end;procedure TSvrClt.ClientSocketError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin if not active then active:=true; end; function TSvrClt.SendMsg(const pktStr:string):boolean; begin result:=false; if (not Active)and(Tag=0) then //before sending msg check the active property begin Tag:=1; Active:=true; end; if Active then begin result:=Socket.SendText(pktStr)=Length(pktStr); //write the data packet`s just been sent WriteLog(2,datetimetostr(now) + ' ' + pktStr); end; end; procedure TSvrClt.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket); var s,Msg:string; UniqueKey,uid:string; CommAction:integer; handle,i:integer; newip:string;//if the user has logined on other computer begin newip:=Socket.RemoteAddress; s:=trim(Socket.ReceiveText); s:=GetInfo(s,';'); while s<>'' do begin WriteLog(1,datetimetostr(now) + ' ' + Msg);//write data packet log UniqueKey:=GetInfo(Msg,','); CommAction:=strtoint(GetInfo(Msg,',')); case CommAction of ord(caLogin): //client request to bind its UserNO 客户端请求账号跟Socket绑定 begin ExecClientCaLogin(Msg,newip); end; ord(caTransmit)://转发消息 begin //client-2-client message from user to another user //ShowTransmitMsg(Msg); end; ord(caForceLogOff)://接收到该命令,新连接迫已登陆使同一账号自动离线(这里可能逻辑还没理顺,或者是作为服务器端管理使用) begin // unforturnate,this client is forced to logoff `cause same userno login //ShowForceLogOff(Msg); end; ord(caJump)://令消息接受方从托盘跳到屏幕,这个有点像QQ的窗口抖动提醒对方的意味.(QQ牛,本人不自量力) begin // Jump2Screen; end; end;//end case //hand the message to the end user; Msg:=GetInfo(s,';'); end;//end while end;end.根据该思路,简单测试了一下给出一个比较保守的结果: 500万之内的在线用户数,单纯在服务器端的处理不会超过1秒就能从服务器发出去 内存占用1000用户的TCustomWinSocket数组为2M 用户 需求内存 1w 20M 10w 200M 100w 2000M=2G 1000w 20G 带宽需求还得请教牛人希望能找到更高效的处理办法
结果是需要反复操作数据库
ClientList.AddObject('客户标识',Socket);
转发TCustomWinSocket(ClientList.Objects[i]).SendText(S);个人感觉思路没什么问题,当然了,我对此研究比较少,还请高人指点
所以不同用户间的通讯主要是根据唯一用户编号来识别
(当然从理论上可以说Socket.Handle是唯一的,但是消息发送方不可能在发送消息之前就需要预先知道对方的Socket.Handle)
所以我维护了这么一个数组
type
PInfoClient=^TInfoClient;
TInfoClient=record
Handle: integer; //客户端套接字句柄
Socket:TCustomWinSocket; //客户端套接字
Name:string; //客户端计算机名称
Address:string; //客户端计算机IP地址
UserNO:string;//用户编号
Used: boolean; //客户端联机标志
end;
type TInfoClientList=array of TInfoClient;访问该数组的时候使用独立线程来操着,用TMultiReadExclusiveWriteSynchronizer来实现线程同步就会收到高效简单的效果其中属性UserNO是通过在TClientSocket.OnConnect中发过去,TServerSocket.OnClientRead中写入的发一个今天封装的类供大家探讨
GetInfo为从传输文本从截取相关命令字符窜的函数unit SvrClt;interface
uses
Communication,D7ScktComp,ExtCtrls,SysUtils,Classes,Forms,Define;
type
TSvrClt=class(TServerSocket)
private
FMaxConnectCount:integer; //最大连接数
FCurrConnectCount:integer;//当前连接数目
//FClients:TList;使用该方法只用使用ip进行转发不适合多用户共享一网卡的情况
procedure ClientSocketConnect(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket);
procedure ClientSocketError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);//virtual;abstract;
protected
public
constructor create(const ServerPort:integer; MaxConnectCount:integer;AOwner: TComponent);
destructor destroy;override;
function SendMsg(const pktStr:string):boolean;
//property Clients: TList read GetClients;
end;
implementation
constructor TSvrClt.create(const ServerPort:integer; MaxConnectCount:integer;AOwner: TComponent);
begin
inherited create(AOwner);
FMaxConnectCount:=MaxConnectCount;
Port:=ServerPort; OnClientConnect:=ClientSocketConnect;
OnClientRead:=ClientSocketRead;
OnClientError:=ClientSocketError;
OnClientDisconnect:=ClientSocketDisconnect; Active:=true;
end;destructor TSvrClt.destroy;
begin
Active:=False;
Close;
inherited destroy;
end;procedure TSvrClt.ClientSocketConnect(Sender: TObject;
Socket: TCustomWinSocket);
var
s:string;
i:integer;
begin
for i:=0 to FMaxConnectCount-1 do
begin
if not m_infosys[i].Used then //looking for the idle TCustomWinSocket ,and put down this TCustomWinSocket
begin
m_infosys[i].Socket:=Socket;
m_infosys[i].Used:=True;
m_infosys[i].Name:='';
m_infosys[i].Address:=Socket.RemoteAddress;
m_infosys[i].UserNO:='';
exit;
end;//end if
end;//end for iend;procedure TSvrClt.ClientSocketDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
var
i:integer;
begin
//release the TCustomWinSocket in array m_infosys of the Logout user`s TCustomWinSocket on server side
for i:=0 to FMaxConnectCount-1 do
begin
if Socket.Handle=m_infosys[i].Socket.Handle then
begin
m_infosys[i].UserNO:='';
m_infosys[i].Address:='';
m_infosys[i].Handle:=-1;
m_infosys[i].Name:='';
m_infosys[i].Address:='';
m_infosys[i].Used:=false;
end;//end if
end;//end for
end;procedure TSvrClt.ClientSocketError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
if not active then active:=true;
end;
function TSvrClt.SendMsg(const pktStr:string):boolean;
begin
result:=false; if (not Active)and(Tag=0) then //before sending msg check the active property
begin
Tag:=1;
Active:=true;
end; if Active then
begin
result:=Socket.SendText(pktStr)=Length(pktStr);
//write the data packet`s just been sent
WriteLog(2,datetimetostr(now) + ' ' + pktStr);
end;
end;
procedure TSvrClt.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
var
s,Msg:string;
UniqueKey,uid:string;
CommAction:integer;
handle,i:integer;
newip:string;//if the user has logined on other computer
begin
newip:=Socket.RemoteAddress;
s:=trim(Socket.ReceiveText);
s:=GetInfo(s,';');
while s<>'' do
begin
WriteLog(1,datetimetostr(now) + ' ' + Msg);//write data packet log
UniqueKey:=GetInfo(Msg,',');
CommAction:=strtoint(GetInfo(Msg,','));
case CommAction of
ord(caLogin): //client request to bind its UserNO 客户端请求账号跟Socket绑定
begin
ExecClientCaLogin(Msg,newip);
end; ord(caTransmit)://转发消息
begin
//client-2-client message from user to another user
//ShowTransmitMsg(Msg);
end; ord(caForceLogOff)://接收到该命令,新连接迫已登陆使同一账号自动离线(这里可能逻辑还没理顺,或者是作为服务器端管理使用)
begin
// unforturnate,this client is forced to logoff `cause same userno login
//ShowForceLogOff(Msg);
end; ord(caJump)://令消息接受方从托盘跳到屏幕,这个有点像QQ的窗口抖动提醒对方的意味.(QQ牛,本人不自量力)
begin
// Jump2Screen;
end;
end;//end case
//hand the message to the end user;
Msg:=GetInfo(s,';');
end;//end while
end;end.根据该思路,简单测试了一下给出一个比较保守的结果:
500万之内的在线用户数,单纯在服务器端的处理不会超过1秒就能从服务器发出去
内存占用1000用户的TCustomWinSocket数组为2M
用户 需求内存
1w 20M
10w 200M
100w 2000M=2G
1000w 20G
带宽需求还得请教牛人希望能找到更高效的处理办法