delphi中Socket用法大讨论,不讨论初级问题,欢迎各位捧场,呵呵(300分)

  • 主题发起人 主题发起人 jinchi
  • 开始时间 开始时间
J

jinchi

Unregistered / Unconfirmed
GUEST, unregistred user!
在通讯中,socket用的很多,但比较普遍的可能就是delphi自带的tclientsocket和tserver
socket,但是它们的难用是可见一斑的(这里的难用不是没有调用什么errorcode:=0没有处理
什么的,而是类似于:http://www.delphibbs.com/delphibbs/dispq.asp?lid=1276648这种状况)

而ics的wsocket据说非常好,但是帮助很少,只能看demo,并且demo好像也不完善.indy没有
用过,偶就不说了.

欢迎各位在这里把这个问题集中讨论一把.不欢迎那种就是把一般书上的demo解释一把用法.
而是一些在应用过程中碰到的书上很少提及的问题提出来,最好能给出解决方法.类似于偶前面
给的那种问题.特别是win98平台好像问题比较多.

这里的重点是delhpi自带的tclientsocket和tserversocket以及ics的wsocket和wserversocket
.最好能顺便给出问题以及解决方法的测试程序(部分代码也可以).有价值偶一定给分,呵呵.

另外,偶听说ics做server好像有些问题,希望能有人说明问题并作出解答.

Ok,闲话少说,delphi的socket大讨论现在开始,欢迎发言,^_^

 
我先说一则信息:[:)]
Delphi7已经把tclientsocket和tserversocket抛弃掉了,建议用INDY的代替:
(/Program Files/Borland/Delphi7/readme.txt)
Borland is deprecating the use of the TServerSocket
and TClientSocket from the unit ScktComp. It is
recommended that you use the Indy components for
socket operations. The TServerSocket and
TClientSocket will no longer be installed on the
component palette by default. If you require the
use of these components then you can install
the design time package named dclsockets70.bpl,
found in your bin directory. For deployment with
runtime packages, you will need to deploy rtl70.bpl
and any other required packages.
 
Re楼上的:
抛弃!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.......................
....................................哭了.......................

怎么不把ics控件集成进去,嘿嘿,indy好像也不是最好吧?

那么讨论再加上Indy的socket控件吧,呵呵,偶看看indy去.
 
偶先把偶的一个碰到问题,后在大富翁论坛的网友帮助下解决的问题介绍一下,呵呵
//////////////////////////////////////////////////////////////////////////
问题:
用一个clientsocket控件连接一个已经在网络上的ip或者不在网上的ip,但对方
主机没有开.那么必然是连不上的,只要你在clientsocket的error事件中:ErrorCode:=0
,那么就不会出现错误报警框.
理论上是这样,但若你连不上后再次打开,连个100-200次,就会发现,出现非法报警框.
socket错误:1055(错误代码的意义是:缓冲区不足).并且这种错误即使你加上try..except
依然没法子避免.
并且这种现象只出现在clientsocket在win98下用的情况,而win2k则没关系
///////////////////////////////////////////////////////////////////////
原因:
通过判断active=false就认为连接失败不正确.Active = false 时,很可能正处
于尝试连接的过程中...这时候没有Close 就 Active := true 会造成重复分配
socket 资源,Close 以后就会把 Socket.handle 句并清空,即 INVALID_SOCKET(-1).
这里有个有趣的现象,很容易犯想当然的错误:
Active 是个属性,赋值时调用的是方法,比如
ClientSocket1.Active:=True;
执行完这句后,Active 还是 false,不会马上等于 True,单步执行可以看到这个有趣
的现象所以说,这不算是 ClientSocket 的 Bug,是使用时要注意的问题。
而为了确保重新打开client是已经释放资源的,可以用下面方法:(uses要加winsock)
if (ClientSocket1.Active=False) and (ClientSocket1.Socket.SocketHandle = INVALID_SOCKET) then
ClientSocket1.active:=true;

而在Onerror事件中,再次确保释放资源可以:
procedure TForm1.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
ErrorCode:=0;
ClientSocket1.Active:=False;
ClientSocket1.Socket.Close;
Shutdown(ClientSocket1.Socket.SocketHandle,0);
end;
/////////////////////////////////////////
注明:上面这个问题的解决方法是轻松虎网友介绍的,呵呵



 
ICS TWSocket 简介:
Windows消息驱动(跟 ClientSocket 同类),支持 TCP /UDP,支持 socks 代理
发送有 Sendstr SendText(采用string 参数,两者一样),RecieveStr方法
还支持换行符分包形式,对于自定义的非二进制的请求应答协议非常方便(取个完整的包,不用自己收集,等待,断包)。。。
关键是 bug 少,稳定,没发现内存泄漏等问题,用在客户端挺好,
ICS的服务端写的一般般
~~~~~~~~~~~~~~~~~~~~~为什么?那位可否介绍一下?

另外那位用过ics的socket控件的,介绍一下用法以及有完善的测试程序的话的贴一把?呵呵
关键是错误处理等等.
 
ClientSocket1.Active:=true;
这个时候TSocket去调用connect();由于你是用异步模式,当然这个connect会马上返回
你根本无法通过马上判断active属性来知道是否连接成功,而应该用过
连接成功事件和套接子错误事件来判断连接的成功和失败,在连接失败的时候释放套接子
ClientSocket1.Active:=False;

INDY的BUG多,
ICS的难用,如果熟练以后绝对是首选,
DELPHI自己带的,也是难以使用,对初学者来说补满陷阱,因此很多人说
BUG多
 
听听课。
顺便也问一下。我抄袭的这个代理服务器程序,为什么很多网页打不开?
出现什么好像是“不能返回更多的数据“的错误。下载东西的时候也总是出错。
谁能解决这个问题300分,谢谢。

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, StdCtrls, Menus, ExtCtrls;
type
session_record = record
Used: boolean; {会话记录是否可用}
SS_Handle: integer; {代理服务器套接字句柄}
CSocket: TClientSocket; {用于连接远程的套接字}
Lookingup: boolean; {是否正在查找服务器}
LookupTime: integer; {查找服务器时间}
Request: boolean; {是否有请求}
request_str: string; {请求数据块}
client_connected: boolean; {客户机联机标志}
remote_connected: boolean; {远程服务器连接标志}
end;
type
TForm1 = class(TForm)
ClientSocket1: TClientSocket;
ServerSocket1: TServerSocket;
Memo1: TMemo;
Timer1: TTimer;
Timer2: TTimer;
Label1: TLabel;
Edit1: TEdit;
PopupMenu1: TPopupMenu;
N01: TMenuItem;
N21: TMenuItem;
N11: TMenuItem;
procedure Timer2Timer(Sender: TObject);
procedure N11Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure N21Click(Sender: TObject);
procedure N01Click(Sender: TObject);
procedure ServerSocket1ClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure ServerSocket1ClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure ServerSocket1ClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
procedure ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
procedure ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
procedure ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
procedure ClientSocket1Write(Sender: TObject;
Socket: TCustomWinSocket);
procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);
procedure ServerSocket1Listen(Sender: TObject;
Socket: TCustomWinSocket);
procedure AppException(Sender: TObject; E: Exception);
procedure Timer1Timer(Sender: TObject);
private
{ Private declarations }
public
Service_Enabled: boolean; {代理服务是否开启}
session: array of session_record; {会话数组}
sessions: integer; {会话数}
LookUpTimeOut: integer; {连接超时值}
InvalidRequests: integer; {无效请求数}
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.ServerSocket1ClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
tmp,line,host: string;
i,j,port: integer;
begin
for i:=1 to sessions do {判断是哪一个会话}
if session[i-1].Used and (session[i-1].SS_Handle=socket.sockethandle) then
begin
session[i-1].request_str:=socket.ReceiveText; {保存请求数据}
tmp:=session[i-1].request_str; {存放到临时变量}
memo1.lines.add(tmp);
j:=pos(char(13)+char(10),tmp); {一行标志}
while j>0 do {逐行扫描请求文本,查找主机地址}
begin
line:=copy(tmp,1,j-1); {取一行}
delete(tmp,1,j+1); {删除一行}
j:=pos('Host',line); {主机地址标志}
if j>0 then
begin
delete(line,1,j+5); {删除前面的无效字符}
j:=pos(':',line);
if j>0 then
begin
host:=copy(line,1,j-1);
delete(line,1,j);
try
port:=strtoint(line);
except
port:=80;
end;
end
else
begin
host:=trim(line); {获取主机地址}
port:=80;
end;
if not session[i-1].remote_connected then {假如远征尚未连接}
begin
session[i-1].Request:=true; {置请求数据就绪标志}
session[i-1].CSocket.host:=host; {设置远程主机地址}
session[i-1].CSocket.port:=port; {设置端口}
session[i-1].CSocket.active:=true; {连接远程主机}
session[i-1].Lookingup:=true; {置标志}
session[i-1].LookupTime:=0; {从0开始计时}
end {假如远程已连接,直接发送请求}
else
session[i-1].CSocket.socket.sendtext(session[i-1].request_str);
break; {停止扫描请求文本}
end;
j:=pos(char(13)+char(10),tmp); {指向下一行}
end;
break; {停止循环}
end;
end;

procedure TForm1.ClientSocket1Read(Sender: TObject;
Socket: TCustomWinSocket);
var
i,j: integer;
rec_bytes: integer; {传回的数据块长度}
rec_Buffer: array[0..2047] of char; {传回的数据块缓冲区}
begin
for i:=1 to sessions do
if (session[i-1].CSocket.tag=socket.SocketHandle) and session[i-1].Used then
begin
rec_bytes:=socket.ReceiveBuf(rec_buffer,2048); {接收数据}
for j:=1 to serversocket1.Socket.ActiveConnections do
if serversocket1.Socket.Connections[j-1].SocketHandle=session[i-1].SS_Handle then
begin
serversocket1.Socket.Connections[j-1].SendBuf(rec_buffer,rec_bytes); {发送数据给客户浏览器}
break;
end;
break;
end;
end;
procedure TForm1.ClientSocket1Connect(Sender: TObject;
Socket: TCustomWinSocket);
var
i: integer;
begin
for i:=1 to sessions do
if (session[i-1].CSocket.socket.sockethandle=socket.SocketHandle) and session[i-1].Used then
begin
session[i-1].CSocket.tag:=socket.SocketHandle;
session[i-1].remote_connected:=true; {置远程主机已连通标志}
session[i-1].Lookingup:=false; {清标志}
break;
end;
end;


procedure TForm1.N01Click(Sender: TObject);
begin
Close; {退出程序}
end;

procedure TForm1.AppException(Sender: TObject; E: Exception);
begin
inc(invalidrequests);
end;

procedure TForm1.ClientSocket1Disconnect(Sender: TObject;
Socket: TCustomWinSocket);
var
i,j,k: integer;
begin
for i:=1 to sessions do
if (session[i-1].CSocket.tag=socket.SocketHandle) and session[i-1].Used then
begin
session[i-1].remote_connected:=false; {置为未连接}
if not session[i-1].client_connected then
session[i-1].Used:=false {假如客户机已断开,则置释放资源标志}
else
for k:=1 to serversocket1.Socket.ActiveConnections do
if (serversocket1.Socket.Connections[k-1].SocketHandle=session[i-1].SS_Handle) and session[i-1].used then
begin
serversocket1.Socket.Connections[k-1].Close;
break;
end;
break;
end;
j:=sessions;
k:=0;
for i:=1 to j do
begin
if session[j-i].Used then
break;
inc(k);
end;
if k>0 then {修正会话数组}
begin
sessions:=sessions-k;
setlength(session,sessions);
end;
edit1.text:=inttostr(sessions);
end;

procedure TForm1.ClientSocket1Error(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
var
i,j,k: integer;
begin
for i:=1 to sessions do
if (session[i-1].CSocket.tag=socket.SocketHandle) and session[i-1].Used then
begin
socket.close;
session[i-1].remote_connected:=false; {置为未连接}
if not session[i-1].client_connected then
session[i-1].Used:=false {假如客户机已断开,则置释放资源标志}
else
for k:=1 to serversocket1.Socket.ActiveConnections do
if (serversocket1.Socket.Connections[k-1].SocketHandle=session[i-1].SS_Handle) and session[i-1].used then
begin
serversocket1.Socket.Connections[k-1].Close;
break;
end;
break;
end;
j:=sessions;
k:=0;
for i:=1 to j do
begin
if session[j-i].Used then
break;
inc(k);
end;
errorcode:=0;
if k>0 then {修正会话数组}
begin
sessions:=sessions-k;
setlength(session,sessions);
end;
edit1.text:=inttostr(sessions);
end;


procedure TForm1.ClientSocket1Write(Sender: TObject;
Socket: TCustomWinSocket);
var
i: integer;
begin
for i:=1 to sessions do
if (session[i-1].CSocket.tag=socket.SocketHandle) and session[i-1].Used then
begin
if session[i-1].Request then
begin
socket.SendText(session[i-1].request_str); {假如有请求,发送}
session[i-1].Request:=false; {清标志}
end;
break;
end;
end;


procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
timer1.Enabled:=false; {关闭定时器}
if Service_Enabled then
serversocket1.Active:=false; {退出程序时关闭服务}
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
Service_Enabled:=false;
timer2.Enabled:=true; {窗口建立时,打开定时器}
end;

procedure TForm1.N11Click(Sender: TObject);
begin
serversocket1.Active:=true; {开启服务}
end;

procedure TForm1.N21Click(Sender: TObject);
begin
serversocket1.Active:=false; {停止服务}
N11.Enabled:=True;
N21.Enabled:=False;
Service_Enabled:=false; {标志清零}
end;

procedure TForm1.ServerSocket1ClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
var
i,j: integer;
begin
j:=-1;
for i:=1 to sessions do {查找是否有空白项}
if not session[i-1].Used and not session[i-1].CSocket.active then
begin
j:=i-1; {有,分配它}
session[j].Used:=true; {置为在用}
break;
end
else
if not session[i-1].Used and session[i-1].CSocket.active then
session[i-1].CSocket.active:=false;
if j=-1 then
begin {无,新增一个}
j:=sessions;
inc(sessions);
setlength(session,sessions);
session[j].Used:=true; {置为在用}
session[j].CSocket:=TClientSocket.Create(nil);
session[j].CSocket.OnConnect:=ClientSocket1Connect;
session[j].CSocket.OnDisconnect:=ClientSocket1Disconnect;
session[j].CSocket.OnError:=ClientSocket1Error;
session[j].CSocket.OnRead:=ClientSocket1Read;
session[j].CSocket.OnWrite:=ClientSocket1Write;
session[j].Lookingup:=false;
end;
session[j].SS_Handle:=socket.socketHandle; {保存句柄,实现绑定}
session[j].Request:=false; {无请求}
session[j].client_connected:=true; {客户机已连接}
session[j].remote_connected:=false; {远程未连接}
edit1.text:=inttostr(sessions);
end;


procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; //
Socket: TCustomWinSocket);
var
i,j,k: integer;
begin
for i:=1 to sessions do
if (session[i-1].SS_Handle=socket.SocketHandle) and session[i-1].Used then
begin
session[i-1].client_connected:=false; {客户机未连接}
if session[i-1].remote_connected then
session[i-1].CSocket.active:=false {假如远程尚连接,断开它}
else
session[i-1].Used:=false; {假如两者都断开,则置释放资源标志}
break;
end;
j:=sessions;
k:=0;
for i:=1 to j do {统计会话数组尾部有几个未用项}
begin
if session[j-i].Used then
break;
inc(k);
end;
if k>0 then {修正会话数组,释放尾部未用项}
begin
sessions:=sessions-k;
setlength(session,sessions);
end;
edit1.text:=inttostr(sessions);
end;


procedure TForm1.ServerSocket1ClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
var
i,j,k: integer;
begin
for i:=1 to sessions do
if (session[i-1].SS_Handle=socket.SocketHandle) and session[i-1].Used then
begin
session[i-1].client_connected:=false; {客户机未连接}
if session[i-1].remote_connected then
session[i-1].CSocket.active:=false {假如远程尚连接,断开它}
else
session[i-1].Used:=false; {假如两者都断开,则置释放资源标志}
break;
end;
j:=sessions;
k:=0;
for i:=1 to j do
begin
if session[j-i].Used then //
break; //
inc(k); //
end;
if k>0 then
begin
sessions:=sessions-k;
setlength(session,sessions);
end;
edit1.text:=inttostr(sessions);
errorcode:=0;
end;


procedure TForm1.ServerSocket1Listen(Sender: TObject;
Socket: TCustomWinSocket);
begin
Service_Enabled:=true; {置正在服务标志}
N11.Enabled:=false;
N21.Enabled:=true;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
i,j: integer;
begin
for i:=1 to sessions do
if session[i-1].Used and session[i-1].Lookingup then {假如正在连接}
begin
inc(session[i-1].LookupTime);
if session[i-1].LookupTime>lookuptimeout then {假如超时}
begin
session[i-1].Lookingup:=false;
session[i-1].CSocket.active:=false; {停止查找}
for j:=1 to serversocket1.Socket.ActiveConnections do
if serversocket1.Socket.Connections[j-1].SocketHandle=session[i-1].SS_Handle then
begin
serversocket1.Socket.Connections[j-1].Close; {断开客户机}
break;
end;
end;
end;
end;


procedure TForm1.Timer2Timer(Sender: TObject);
begin
timer2.Enabled:=false; {关闭定时器}
sessions:=0; {会话数=0}
Application.OnException := AppException; {为了屏蔽代理服务器出现的异常}
invalidRequests:=0; {0错误}
LookUpTimeOut:=60000; {超时值=1分钟}
timer1.Enabled:=true; {打开定时器}
n11.Enabled:=false; {开启服务菜单项失效}
n21.Enabled:=true; {关闭服务菜单项有效}
serversocket1.Port:=968; {代理服务器端口=968}
serversocket1.Active:=true; {开启服务}
end;
end.

窗体文件
object Form1: TForm1
Left = 267
Top = 128
Width = 342
Height = 255
Caption = 'Form1'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
FormStyle = fsStayOnTop
OldCreateOrder = False
PopupMenu = PopupMenu1
OnClose = FormClose
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object Label1: TLabel
Left = 4
Top = 204
Width = 37
Height = 13
Caption = 'Session'
end
object Memo1: TMemo
Left = 0
Top = 0
Width = 334
Height = 189
Align = alTop
ScrollBars = ssVertical
TabOrder = 0
end
object Edit1: TEdit
Left = 44
Top = 200
Width = 109
Height = 21
TabOrder = 1
Text = 'Edit1'
end
object ClientSocket1: TClientSocket
Active = False
ClientType = ctNonBlocking
Port = 80
OnConnect = ClientSocket1Connect
OnDisconnect = ClientSocket1Disconnect
OnRead = ClientSocket1Read
OnWrite = ClientSocket1Write
OnError = ClientSocket1Error
Left = 164
Top = 196
end
object ServerSocket1: TServerSocket
Active = False
Port = 80
ServerType = stNonBlocking
OnListen = ServerSocket1Listen
OnClientConnect = ServerSocket1ClientConnect
OnClientDisconnect = ServerSocket1ClientDisconnect
OnClientRead = ServerSocket1ClientRead
OnClientError = ServerSocket1ClientError
Left = 196
Top = 196
end
object Timer1: TTimer
Interval = 200
OnTimer = Timer1Timer
Left = 228
Top = 196
end
object Timer2: TTimer
OnTimer = Timer2Timer
Left = 260
Top = 196
end
object PopupMenu1: TPopupMenu
Left = 292
Top = 196
object N11: TMenuItem
Caption = '开启服务'
OnClick = N11Click
end
object N21: TMenuItem
Caption = '停止服务'
OnClick = N21Click
end
object N01: TMenuItem
Caption = '退出'
OnClick = N01Click
end
end
end
 
看来若是真的想用好delphi自带的socket控件的化,好像需要好好看看它的源码和机理了.
呵呵.
ics偶先看看,有问题再提出看看.
 
晕倒,一直没有人.
那么那位能给偶一个完善的ics的wsocket和wserversocket的通讯程序,包括
错误处理以及数据发送接收等等.
要求功能完善.ok的话偶给200分.
 
Hi,Jinchi兄弟你好!很高兴看到你开这个造福天下的帖子:)
1。关于 ICS 的服务器,我的经验主要是它的性能一般,看过它的代码,几乎没提供大连接量的优化技术
不过呢, 跟它的客户端一样ICS 的 Server 测试充分,没什么 bug,在一般的APP里你可以放心的使用它。
另外这里有几页Delphi各种控件做的 WebServer 性能评测图:
http://www.dxsock.com/benchmarks/
供你参考,需要注意的是这个评测并不客观
2。关于服务器设计,有兴趣还可以参考我回的这贴
http://www.delphibbs.com/delphibbs/dispq.asp?lid=1268895

p.s.刚看你邮箱地址,好象是浙大校友哦:)
 
又一个浙大的,太牛了。
轻松虎大哥(谢谢你发给我的控件,太感谢了),
张无忌大虾,各位高手,
没有时间看看我的初级问题么??
比如说用这个服务器做代理访问新浪的时候就会出错,
但是有的网站(163)就没事儿,为什么会有这种事情?
这个程序有什么先天性的缺陷?
 
近期要搞socket编程。很感兴趣。听听课。 我用过indy8 和ics一点。 感觉indy的速度比较慢。呵呵
我用indy的http和udp。 ics的比较灵活。就是help太少,要走很多弯路。
 
其实每种socket控件都有它的优点,不能说indy或ics的是绝对好用。ics的我没用过,不知道,
但好多人都说它好,就算是吧。
我用得较多的是indy和delphi自带的tcpsocket控件,觉得各有所长。所在在工作中,对其中的
优缺点进行对比后,再决定用谁的,从不会说只认d自带的或indy的。
是不是大家在d7中发现没有了原来在d6中的serversocket和ClientSocket控件了而就找出
它们的缺点来安慰自己。其它在D7中还是保留了这两个控件的,只是不知道为什么borland在
安装D7时没把它装上面板上去。
大家可以这样把它请回来
打开菜单中的component->install packages,点击add,选择delphi7/bin目录下的dclsockets.bpl
文件就可以了。
 
to: 轻松虎,呵呵.偶是浙大的,你已经毕业叻么?88delphi板上没有见到过好像,呵呵
模delphi时间不长,多多指教的说,呵呵

发现用SOCKET配合着链表(自己写或者用TLIST做),做网络通讯的协议非常方便,呵呵.
tlist做链表更是方便的不得了.偶的项目原来不稳定,只好用它重新改写叻协议,效果
不错的说,呵呵.
另外,偶再贴一下<delphi深度历险>中的xstring公用单元吧,这个在自己做网络协议的
时候打包,解包必备,嘿嘿.偶不知没有它的化协议不知定义成什么样子叻.
unit xStrings;

interface

uses SysUtils, Classes, Windows, FileCtrl, Dialogs;

const
DEFAULT_DELIMITERS = [' ', #9, #10, #13];

function GetToken(const S: string; index: Integer; bTrail: Boolean = False; Delimiters: TSysCharSet = DEFAULT_DELIMITERS): string;
function CountWords(S: string; Delimiters: TSysCharSet = DEFAULT_DELIMITERS): Integer;
//上面这两个配合着用的最多.
function BracketString(const S: string): string;

procedure TruncateCRLF(var S: string);
function IsContainingCRLF(const S: string): Boolean;

function ReplaceString(var S: string; const Token, NewToken: string; bCaseSensitive: Boolean): Boolean;
procedure Simple_ReplaceString(var S: string; const Substr: string; index, Count: Integer);

function UnquoteString(const S: string): string;
function FirstToken(var S: string; const Delimiter: string; Remove: Boolean): string;
function AddTimeStamp(const S: string): string;

function PartialIndexOf(SL: TStrings; S: string; StartIndex: Integer; bForward: Boolean): Integer;
function CompositeStrings(SL: TStrings; const Delimiter: string): string;

function SafeLoadStrings(SL: TStrings; const Filename: string): Boolean;
procedure SafeSaveStrings(SL: TStrings; const Filename: string);

procedure RemoveDuplicates(SL: TStrings);
function ParseRPLNo(var Msg: string): Integer;

function RPos(const C: Char; const S: string): Integer;
function AnsiIPos(const Substr, S: string): Integer;
function MatchString(S, SubS: string; Options: TFindOptions): Integer;

implementation

function GetToken(const S: string; index: Integer; bTrail: Boolean = False; Delimiters: TSysCharSet = DEFAULT_DELIMITERS): string;
var
I, W, head, tail: Integer;
bInWord : Boolean;
begin
I := 1;
W := 0;
bInWord := False;
head := 1;
tail := Length(S);
while (I <= Length(S)) and (W <= index) do
begin
if S in Delimiters then
begin
if (W = index) and bInWord then tail := I - 1;
bInWord := False;
end else
begin
if not bInWord then
begin
bInWord := True;
Inc(W);
if W = index then head := I;
end;
end;

Inc(I);
end;

if bTrail then tail := Length(S);
if W >= index then Result := Copy(S, head, tail - head + 1)
else Result := '';
end;

function CountWords(S: string; Delimiters: TSysCharSet = DEFAULT_DELIMITERS): Integer;
var
bInWord: Boolean;
I : Integer;
begin
Result := 0;
I := 1;
bInWord := False;
while I <= Length(S) do
begin
if S in Delimiters then bInWord := False
else
begin
if not bInWord then
begin
bInWord := True;
Inc(Result);
end;
end;

Inc(I);
end;
end;

function IsContainingCRLF(const S: string): Boolean;
var
len: Integer;
begin
len := Length(S);
Result := (len >= 2) and (S[len - 1] = #13) and (S[len] = #10);
end;

procedure TruncateCRLF(var S: string);
var
I: Integer;
begin
I := 1;
while I <= Length(S) do
if (S = #13) or (S = #10) then Delete(S, I, 1)
else Inc(I);
end;

function ReplaceString(var S: string; const Token, NewToken: string; bCaseSensitive: Boolean): Boolean;
var
I : Integer;
sFirstPart: string;
begin
if bCaseSensitive then
I := AnsiPos(Token, S)
else
I := AnsiPos(AnsiUpperCase(Token), AnsiUpperCase(S));

if I <> 0 then
begin
sFirstPart := Copy(S, 1, I - 1) + NewToken; // 磷?礚絘患癹
S := Copy(S, I + Length(Token), Maxint);
end;

Result := I <> 0;
if Result then
begin
ReplaceString(S, Token, NewToken, bCaseSensitive);
S := sFirstPart + S;
end;
end;

procedure Simple_ReplaceString(var S: string; const Substr: string; index, Count: Integer);
begin
S := Format('%s%s%s',[Copy(S, 1, index - 1), Substr, Copy(S, index + Count, Maxint)]);
end;

function BracketString(const S: string): string;
begin
Result := S;
if (Result = '') or (Result[1] <> '[') then Result := '[' + Result;
if Result[Length(Result)] <> ']' then Result := Result + ']';
end;

function UnquoteString(const S: string): string;
begin
if S = '' then Exit;

Result := S;

if Result[1] = '"' then Delete(Result, 1, 1);
if Result = '' then Exit;

if Result[Length(Result)] = '"' then Delete(Result, Length(Result), 1);
end;

function FirstToken(var S: string; const Delimiter: string; Remove: Boolean): string;
var
I: Integer;
begin
I := Pos(Delimiter, S);
if I <> 0 then
begin
Result := Copy(S, 1, I - 1);
if Remove then S := Trim(Copy(S, I + 1, Maxint));
end else
begin
Result := S;
if Remove then S := '';
end;
end;

function CompositeStrings(SL: TStrings; const Delimiter: string): string;
var
I: Integer;
begin
Result := '';

with SL do
begin
for I := 0 to Count - 2 do
Result := Result + Strings + Delimiter;
if Count > 0 then
Result := Result + Strings[Count - 1];
end;
end;

function AddTimeStamp(const S: string): string;
begin
if S = '' then
Result := DateTimeToStr(Now)
else if S[Length(S)] = #10 then
Result := Copy(S, 1, Length(S) - 2) + ' at ' + DateTimeToStr(Now) + #13#10
else
Result := S + ' at ' + DateTimeToStr(Now);
end;

function SafeLoadStrings(SL: TStrings; const Filename: string): Boolean;
begin
Result := False;
repeat
try
if not FileExists(Filename) then Exit;
SL.LoadFromFile(Filename);
Result := True;
Break;
except
Sleep(500);
end;
until False;
end;

procedure SafeSaveStrings(SL: TStrings; const Filename: string);
begin
ForceDirectories(ExtractFilePath(Filename));
repeat
try
SL.SaveToFile(Filename);
Break;
except
Sleep(500);
end;
until False;
end;

function PartialIndexOf(SL: TStrings; S: string; StartIndex: Integer; bForward: Boolean): Integer;
begin
with SL do
begin
if bForward then
begin
for Result := StartIndex to Count - 1 do
if AnsiCompareText(S, Copy(Strings[Result], 1, Length(S))) = 0 then Exit;
end else
begin
for Result := StartIndex downto 0 do
if AnsiCompareText(S, Copy(Strings[Result], 1, Length(S))) = 0 then Exit;
end;
end;

Result := -1;
end;

// duplicated string must be adjacent ..
procedure RemoveDuplicates(SL: TStrings);
var
I: Integer;
begin
with SL do
begin
I := 1;
while I < Count do
if CompareText(Strings, Strings[I - 1]) = 0 then
Delete(I)
else
Inc(I);
end;
end;

function ParseRPLNo(var Msg: string): Integer;
var
S: string;
begin
S := GetToken(Msg, 1, False);
Result := StrToIntDef(S, 0);
Msg := GetToken(Msg, 2, True);
end;

function RPos(const C: Char; const S: string): Integer;
var
I: Integer;
begin
Result := 0;
I := Length(S);
repeat
if S = C then
begin
Result := I;
Exit;
end;
dec(I);
until I < 1;
end;

function AnsiIPos(const Substr, S: string): Integer;
begin
Result := AnsiPos(AnsiLowerCase(Substr), AnsiLowerCase(S));
end;

function MatchString(S, SubS: string; Options: TFindOptions): Integer;
const
Delimiters = [#0..#47, #58..#64, #123..#255];
var
EndI: Integer;
begin
if not (frMatchCase in Options) then
begin
S := AnsiUpperCase(S);
SubS := AnsiUpperCase(SubS);
end;

if frWholeWord in Options then
begin
Result := 1;
EndI := Length(SubS);
while EndI <= Length(S) do
begin
if ((Result = 1) or (S[Result - 1] in Delimiters)) and ((EndI = Length(S)) or (S[EndI + 1] in Delimiters)) and
(AnsiCompareStr(Copy(S, Result, Length(SubS)), SubS) = 0) then Break;
Inc(Result);
Inc(EndI);
end;
Result := EndI;
if Result > Length(S) then Result := 0;
end else Result := AnsiPos(SubS, S);
end;

end.


 
项目大体搞定,还算蛮稳定的,系统大体说来就是九个点通过9.2K的DDN线路实时采集数据,
通过socket汇总到一个点,然后这个点通过ddn专线远程传输到控制站.里面牵扯每个点的
单机数据库(采集点用的是bde+paradox),汇总点用的是sql2k,而控制站则是一个多层分布
式系统,控制站也采用socket通讯机理,因为dcom配置太麻烦了.
总的说来,delphi的tclientsocket和tserversocket还是蛮稳定的.但依然碰见两个问题,
一个就是那个tclientsocket资源释放的问题(98下有这个问题,而win2k则没有,具体可以
参考偶第一篇帖子理提到的解决方法,
而第二个问题是:偶汇总站采用tserversocket和检测点的tclientsocket实时通讯,采用
每个点对应一个tserversocket,没有庸tserversocket的多线程,怕出问题).而采用tclient
socket和远程控制站的tserversocket通讯,但当汇总点关闭,远程控制站的socket旧出现
非法socket错误,即使你处理了socket错误依然如此.其实理论上说来,汇总点到控制站
的socket通讯和汇总点到检测点的通讯方式,协议,处理完全一样.偶也不明白为什么.
后来旧修改成控制站庸tclientsocket而汇总点庸tserversocket,这样旧没有问题了.
偶也莫名其妙.
通过做项目,发现做一个系统跟学习一门编程语言真的大不一样.平时的编程语言的
学习不是最关键的,关键得是系统设计方法,以及对底层东西有透彻得理解;面向对象
设计方法尤其欠缺.而编程得系统设计得严禁更是缺少.这样旧很容易产生bug.
这里,再推荐一本书,刚买的,发现做过一个系统后,看书感觉更是不同了.书名是<
程序调试得思想与实践>,老外得,他本人是一个非常知名得调试员.颇为值得一看,呵呵.
 
clientsocket和serversocket组件在局域网中非常的简单好用,具有微软软件的优良风格,哪像indy
等故弄玄虚,有着sun的丑陋特色。
另:俺也是浙大的,94年毕业,呵呵,老咯~~
 
简单有简单的好处,复杂有复杂的道理
 
大家是不是还可以探讨一下dxsock
http://www.dxsock.com
从他的介绍来说,好像太牛了
 
后退
顶部