在北京,一个标准的程序开发人员底薪为4000元
1、出色的英文能力(听说读写缺一不可,一样月薪加一千)
2、专注于某一领域而不是所有的都会,比如目前热门的是网络安全linux,长胜不衰的是永远的C系列。要经常提到基于核的开发,如具有,每一个核心技术加1000元
3、优秀的项目管理能力(包括开发管理,质量管理,熟悉CMM者加2000元)
4、精明的人际关系,如何优秀的与上下级沟通,擅长与领导沟通者加1000元
5、卓越的领导力,毕竟自己干的活少,下面的人干的多,要让手下心情愉快的为你买命,这是艺术,加1000元
结论:能被自己掌握的只有英文这一项,其他的不是天生的就是机遇好,所以大家一起学英文。
Eclipse官方网站 http://www.eclipse.org/
安装
Eclipse SDK包括了Eclipse开发环境,Java开发环境,Plug-in开发环境,所有源代码和文档。如果只是用Eclipse开发Java应用,而不是开发Eclipse插件或者研究Eclipse代码,那么下载一个Platform Runtime Binary再加上JDT Runtime Binary是最好的选择。
热键
Template:Alt + /
修改处:
窗口->喜好设定->工作台->按键->编辑->内容辅助。
个人习惯:
Shift+SPACE(空白)。
简易说明:
编辑程序代码时,打sysout +Template激活键,就会自动出现:System.out.println(); 。设定Template的格式:窗口->喜好设定->Java->编辑器->模板。
程序代码自动排版:Ctrl+Shift+F
修改处:
窗口->喜好设定->工作台->按键->程序代码->格式。
个人习惯:Alt+Z。
自动排版设定:
窗口->喜好设定->Java->程序代码格式制作程序。样式页面->将插入tab(而非空格键)以内缩,该选项取消勾选,下面空格数目填4,这样在自动编排时会以空格4作缩排。
快速执行程序:Ctrl + F11
个人习惯:ALT+X
修改处:窗口->喜好设定->工作台->按键->执行->激活前一次的激活作业。
简易说明:
第一次执行时,它会询问您执行模式,设置好后,以后只要按这个热键,它就会快速执行。<ALT+Z(排版完)、ATL+X(执行)>..我觉得很顺手^___^
自动汇入所需要的类别:Ctrl+Shift+O
简易说明:
假设我们没有Import任何类别时,当我们在程序里打入:
BufferedReader buf =new BufferedReader(new InputStreamReader(System.in));
此时Eclipse会警示说没有汇入类别,这时我们只要按下Ctrl+Shift+O,它就会自动帮我们Import类别。
查看使用类别的原始码:Ctrl+鼠标左键点击
简易说明:可以看到您所使用类别的原始码。
将选取的文字批注起来:Ctrl+/
简易说明:Debug时很方便。
修改处:窗口->喜好设定->工作台->按键->程序代码->批注
视景切换:Ctrl+F8
个人习惯:Alt+S。
修改处:窗口->喜好设定->工作台->按键->窗口->下一个视景。
简易说明:可以方便我们快速切换编辑、除错等视景。
使用技巧
切换,英文、繁体、简体显示
1.首先要先安装完中文语言包。
2.在桌面的快捷方式后面加上参数即可,
英文-> -nl "zh_US"
繁体-> -nl "zh_TW"
简体-> -nl "zh_CN"。
(其它语系以此类推)
不完全汉化问题:
最好使用一个从未被启动过的Eclipse3.0.1,这样可以完全中文化。
如果在安装中文包时Eclipse已经被使用过,则可能会出现有部分内容不能被汉化,
比如“搜索”、“运行”等菜单。如果中文化的不完全,
把eclipse 目录下的configuration目录删除,重启即可。
摘要: 第一课 初识PASCAL语言 …………………… 1
第二课 赋值语句与简单的输出语句 …………………… 5
第三课 带格式的输出语句输入语句 …………………… 12
第四课 简单的分支结构程序设计  ...
阅读全文
Windows Sockets在头文件winsock.h中定义了所有的错误码,它们包括以“WSA”打头的Windows Sockets实现返回的错误码和Berkeley Sockets定义的错误码全集。定义Berkeley Sockets错误码是为了确保原有软件的可移植性。
A.1 Windows Sockets错误码列表
表A.1列出了WSAGetLastError()函数返回的可能错误码和它们的解释,它们可分为四个部分。
错误码的第一部分是用来解决在不同的C编译中对标准C错误码的不一致的定义。错误码的第二部分是标准Berkeley Sockets错误码的Windows Sockets版本。错误码的第三部分包括特定Windows Sockets扩充的错误码。错误码的第四部分由Windows Sockets的getXbyY()和WSAAsyncGetXByY()函数返回,相当于Berkeley软件中由变量h_errno返回的错误(事实上,Windows Sockets在头文件winsock.h中已将h_error定义成其值为WSAGetLastError()的一个宏),它们相当于由域名服务(Domain Name Service)返回的各种失败。如果Windows Sockets实现没有使用域名服务,它将使用最合适的代码。一般地,Windows Sockets应用程序应该将错误WSAHOST_NOT_FOUND和WSANO_DATA解释为指示关键字(名字,地址等)没有找着,而错误WSATRY_AGAIN和WSANO_RECOVERY是提醒名字服务自身是非操作的。
错误码由Windows Sockets 规范定义,在所有同一版本规范的Windows Sockets兼容实现中,它们是一致的。
表A.1 Windows Sockets错误码
Windows Sockets错误码 |
Berkeley 对应错误码 |
错误号 |
解 释 |
WSAEINTR |
EINTR |
10004 |
同标准C |
WSAEBADF |
EBADF |
10009 |
同标准C |
WSAEACCES |
EACCES |
10013 |
同标准C |
WSAEFAULT |
EFAULT |
10014 |
同标准C |
WSAEINVAL |
EINVAL |
10022 |
同标准C |
WSAEMFILE |
EMFILE |
10024 |
同标准C |
WSAEWOULDBLOCK |
EWOULDBLOCK |
10035 |
同BSD |
WSAEINPROGRESS |
EINPROGRESS |
10036 |
当一个阻塞函数正在进行时,调用任何Windows Sockets API函数均返回此错误 |
WSAEALREADY |
EALREADY |
10037 |
同BSD |
WSAENOTSOCK |
ENOTSOCK |
10038 |
同BSD |
WSAEDESTADDRREQ |
EDESTADDRREQ |
10039 |
同BSD |
WSAEMSGSIZE |
EMSGSIZE |
10040 |
同BSD |
WSAEPROTOTYPE |
EPROTOTYPE |
10041 |
同BSD |
WSAENOPROTOOPT |
ENOPROTOOPT |
10042 |
同BSD |
WSAEPROTONOSUPPORT |
EPROTONOSUPPORT |
10043 |
同BSD |
WSAESOCKTNOSUPPORT |
ESOCKTNOSUPPORT |
10044 |
同BSD |
WSAEOPNOTSUPP |
EOPNOTSUPP |
10045 |
同BSD |
WSAEPFNOSUPPORT |
EPFNOSUPPORT |
10046 |
同BSD |
WSAEAFNOSUPPORT |
EAFNOSUPPORT |
10047 |
同BSD |
WSAEADDRINUSE |
EADDRINUSE |
10048 |
同BSD |
WSAEADDRNOTAVAIL |
EADDRNOTAVAIL |
10049 |
同BSD |
WSAENETDOWN |
ENETDOWN |
10050 |
同BSD。任何时候只要Windows Sockets实现检测到网络子系统失败,它就报告此错误。 |
WSAENETUNREACH |
ENETUNREACH |
10051 |
同BSD |
WSAENETRESET |
ENETRESET |
10052 |
同BSD |
WSAECONNABORTED |
ECONNABORTED |
10053 |
同BSD |
WSAECONNRESET |
ECONNRESET |
10054 |
同BSD |
WSAENOBUFS |
ENOBUFS |
10055 |
同BSD |
WSAEISCONN |
EISCONN |
10056 |
同BSD |
WSAENOTCONN |
ENOTCONN |
10057 |
同BSD |
WSAESHUTDOWN |
ESHUTDOWN |
10058 |
同BSD |
WSAETOOMANYREFS |
ETOOMANYREFS |
10059 |
同BSD |
WSAETIMEDOUT |
ETIMEDOUT |
10060 |
同BSD |
WSAECONNREFUSED |
ECONNREFUSED |
10061 |
同BSD |
WSAELOOP |
ELOOP |
10062 |
同BSD |
WSAENAMETOOLONG |
ENAMETOOLONG |
10063 |
同BSD |
WSAEHOSTDOWN |
EHOSTDOWN |
10064 |
同BSD |
WSAEHOSTUNREACH |
EHOSTUNREACH |
10065 |
同BSD |
WSASYSNOTREADY |
|
10091 |
由WSAStartup() 返回,指示网络子系统无法使用。 |
WSAVERNOTSUPPORTED |
|
10092 |
由WSAStartup() 返回,指示Windows Sockets DLL 不能支持此应用程序。 |
WSANOTINITIALISED |
|
10093 |
由除WSAStartup()之外的其它函数返回,指示 尚没有一次成功的WSAStartup() 调用执行过。 |
WSAEDISCON |
|
10101 |
由WSARecv()和WSARecvFrom()返回,指示远程方已经初始化了一个“雅致”的shutdown序列。 |
WSAHOST_NOT_FOUND |
HOST_NOT_FOUND |
11001 |
同BSD |
WSATRY_AGAIN |
TRY_AGAIN |
11002 |
同BSD |
WSANO_RECOVERY |
NO_RECOVERY |
11003 |
同BSD |
WSANO_DATA |
NO_DATA |
11004 |
同BSD |
A.2 Windows Sockets错误码扩展描述
下面给出WSAGetLastError()函数返回的可能错误码按字母顺序排列的列表,同时给出简要的扩展描述。
WSAEACCES (10013) Permission denied.
试图使用被禁止的访问权限去访问套接字。例如,在没有使用函数setsockopt()的SO_BROADCAST命令设置广播权限的套接字上使用函数sendto()给一个广播地址发送数据。
WSAEADDRINUSE (10048) Address already in use.
正常情况下每一个套接字地址(协议/IP地址/端口号)只允许使用一次。当应用程序试图使用bind()函数将一个被已存在的或没有完全关闭的或正在关闭的套接字使用了的IP地址/端口号绑扎到一个新套接字上时,该错误发生。对于服务器应用程序来说,如果需要使用bind()函数将多个套接字绑扎到同一个端口上,可以考虑使用setsockopt()函数的SO_REUSEADDR命令。客户应用程序一般不必使用bind()函数——connect()函数总是自动选择没有使用的端口号。当bind()函数操作的是通配地址(包括ADDR_ANY)时,错误WSAEADDRINUSE可能延迟到一个明确的地址被提交时才发生。这可能在后续的函数如connect()、listen()、WSAConnect()或WSAJoinLeaf()调用时发生。
WSAEADDRNOTAVAIL (10049) Cannot assign requested address.
被请求的地址在它的环境中是不合法的。通常地在bind()函数试图将一个本地机器不合法的地址绑扎到套接字时产生。它也可能在connect()、sendto()、WSAConnect()、WSAJoinLeaf()或WSASendTo()函数调用时因远程机器的远程地址或端口号非法(如0地址或0端口号)而产生。
WSAEAFNOSUPPORT (10047) Address family not supported by protocol family.
使用的地址与被请求的协议不兼容。所有的套接字在创建时都与一个地址族(如IP协议对应的AF_INET)和一个通用的协议类型(如SOCK_STREAM)联系起来。如果在socket()调用中明确地要求一个不正确的协议,或在调用sendto()等函数时使用了对套接字来说是错误的地址族的地址,该错误返回。
WSAEALREADY (10037) Operation already in progress.
当在非阻塞套接字上已经有一个操作正在进行时,又有一个操作试图在其上执行则产生此错误。如:在一个正在进行连接的非阻塞套接字上第二次调用connect()函数;或取消一个已经被取消或已完成的异步请求(WSAAsyncGetXbyY())。
WSAECONNABORTED (10053) Software caused connection abort.
一个已建立的连接被你的主机上的软件终止,可能是因为一次数据传输超时或是协议错误。
WSAECONNREFUSED (10061) Connection refused.
因为目标主机主动拒绝,连接不能建立。这通常是因为试图连接到一个远程主机上不活动的服务,如没有服务器应用程序处于执行状态。
WSAECONNRESET (10054) Connection reset by peer.
存在的连接被远程主机强制关闭。通常原因为:远程主机上对等方应用程序突然停止运行,或远程主机重新启动,或远程主机在远程方套接字上使用了“强制”关闭(参见setsockopt(SO_LINGER))。另外,在一个或多个操作正在进行时,如果连接因“keep-alive”活动检测到一个失败而中断,也可能导致此错误。此时,正在进行的操作以错误码WSAENETRESET失败返回,后续操作将失败返回错误码WSAECONNRESET。
WSAEDESTADDRREQ (10039) Destination address required.
在套接字上一个操作所必须的地址被遗漏。例如,如果sendto()函数被调用且远程地址为ADDR_ANY时,此错误被返回。
WSAEFAULT (10014) Bad address.
系统检测到调用试图使用的一个指针参数指向的是一个非法指针地址。如果应用程序传递一个非法的指针值,或缓冲区长度太小,此错误发生。例如,参数为结构sockaddr,但参数的长度小于sizeof(struct sockaddr)。
WSAEHOSTDOWN (10064) Host is down.
套接字操作因为目的主机关闭而失败返回。套接字操作遇到不活动主机。本地主机上的网络活动没有初始化。这些条件由错误码WSAETIMEDOUT指示似乎更合适。
WSAEHOSTUNREACH (10065) No route to host.
试图和一个不可达主机进行套接字操作。参见WSAENETUNREACH。
WSAEINPROGRESS (10036) Operation now in progress.
一个阻塞操作正在执行。Windows Sockets只允许一个任务(或线程)在同一时间可以有一个未完成的阻塞操作,如果此时调用了任何函数(不管此函数是否引用了该套接字或任何其它套接字),此函数将以错误码WSAEINPROGRESS返回。
WSAEINTR (10004) Interrupted function call.
阻塞操作被函数WSACancelBlockingCall ()调用所中断。
WSAEINVAL (10022) Invalid argument.
提供了非法参数(例如,在使用setsockopt()函数时指定了非法的level)。在一些实例中,它也可能与套接字的当前状态相关,例如,在套接字没有使用listen()使其处于监听时调用accept()函数。
WSAEISCONN (10056) Socket is already connected.
连接请求发生在已经连接的套接字上。一些实现对于在已连接SOCK_DGRAM套接字上使用sendto()函数的情况也返回此错误(对于SOCK_STREAM套接字,sendto()函数的to参数被忽略),尽管其它一些实现将此操作视为合法事件。
WSAEMFILE (10024) Too many open files.
打开了太多的套接字。不管是对整个系统还是每一进程或线程,Windows Sockets实现都可能有一个最大可用的套接字句柄数。
WSAEMSGSIZE (10040) Message too long.
在数据报套接字上发送的一个消息大于内部消息缓冲区或一些其它网络限制,或者是用来接受数据报的缓冲区小于数据报本身。
WSAENETDOWN (10050) Network is down.
套接字操作遇到一个不活动的网络。此错误可能指示网络系统(例如WinSock DLL运行的协议栈)、网络接口或本地网络本身发生了一个严重的失败。
WSAENETRESET (10052) Network dropped connection on reset.
在操作正在进行时连接因“keep-alive”检测到失败而中断。也可能由setsockopt()函数返回,如果试图使用它在一个已经失败的连接上设置SO_KEEPALIVE。
WSAENETUNREACH (10051) Network is unreachable.
试图和一个无法到达的网络进行套接字操作。它常常意味着本地软件不知道到达远程主机的路由。
WSAENOBUFS (10055) No buffer space available.
由于系统缺乏足够的缓冲区空间,或因为队列已满,在套接字上的操作无法执行。
WSAENOPROTOOPT (10042) Bad protocol option.
在getsockopt()或setsockopt()调用中,指定了一个未知的、非法的或不支持的选项或层(level)。
WSAENOTCONN (10057) Socket is not connected.
因为套接字没有连接,发送或接收数据的请求不被允许,或者是使用sendto()函数在数据报套接字上发送时没有提供地址。任何其它类型的操作也可以返回此错误,例如,使用setsockopt()函数在一个已重置的连接上设置SO_KEEPALIVE。
WSAENOTSOCK (10038) Socket operation on non-socket.
操作试图不是在套接字上进行。它可能是套接字句柄参数没有引用到一个合法套接字,或者是调用select()函数时,一个fd_set中的成员不合法。
WSAEOPNOTSUPP (10045) Operation not supported.
对于引用的对象的类型来说,试图进行的操作不支持。通常它发生在套接字不支持此操作的套接字描述符上,例如,试图在数据报套接字上接收连接。
WSAEPFNOSUPPORT (10046) Protocol family not supported.
协议簇没有在系统中配置或没有支持它的实现存在。它与WSAEAFNOSUPPORT有些微的不同,但在绝大多数情况下是可互换的,返回这两个错误的所有Windows Sockets函数的说明见WSAEAFNOSUPPORT的描述。
WSAEPROCLIM (10067) Too many processes.
Windows Sockets实现可能限制同时使用它的应用程序的数量,如果达到此限制,WSAStartup()函数可能因此错误失败。
WSAEPROTONOSUPPORT (10043) Protocol not supported.
请求的协议没有在系统中配置或没有支持它的实现存在。例如,socket()调用请求一个SOCK_DGRAM套接字,但指定的是流协议。
WSAEPROTOTYPE (10041) Protocol wrong type for socket.
在socket()函数调用中指定的协议不支持请求的套接字类型的语义。例如,ARPA Internet UDP协议不能和SOCK_STREAM套接字类型一同指定。
WSAESHUTDOWN (10058) Cannot send after socket shutdown.
因为套接字在相应方向上已经被先前的shutdown()调用关闭,因此该方向上的发送或接收请求不被允许。通过调用shutdown()函数来请求对套接字的部分关闭,它发送一个信号来停止发送或接收或双向操作。
WSAESOCKTNOSUPPORT (10044) Socket type not supported.
不支持在此地址族中指定的套接字类型。例如,socket()调用中选择了可选的套接字类型SOCK_RAW,但是实现却根本不支持SOCK_RAW类型的套接字。
WSAETIMEDOUT (10060) Connection timed out.
连接请求因被连接方在一个时间周期内不能正确响应而失败,或已经建立的连接因被连接的主机不能响应而失败。
WSATYPE_NOT_FOUND (10109) Class type not found
指定的类没有找到。
WSAEWOULDBLOCK (10035) Resource temporarily unavailable.
此错误由在非阻塞套接字上不能立即完成的操作返回,例如,当套接字上没有排队数据可读时调用了recv()函数。此错误不是严重错误,相应操作应该稍后重试。对于在非阻塞SOCK_STREAM套接字上调用connect()函数来说,报告WSAEWOULDBLOCK是正常的,因为建立一个连接必须花费一些时间。
WSAHOST_NOT_FOUND (11001) Host not found.
主机未知。此名字不是一个正式主机名,也不是一个别名,它不能在查询的数据库中找到。此错误也可能在协议和服务查询中返回,它意味着指定的名字不能在相关数据库中找到。
WSA_INVALID_HANDLE (OS dependent) Specified event object handle is invalid.
应用程序试图使用一个事件对象,但指定的句柄非法。
WSA_INVALID_PARAMETER (OS dependent) One or more parameters are invalid.
应用程序使用了一个直接映射到Win32函数的WinSock函数,而Win32函数指示一个或多个参数有问题。
WSAINVALIDPROCTABLE (OS dependent) Invalid procedure table from service provider.
服务提供者返回了一个假的WS2_32.DLL程序(procedure)表。这通常是由一个或多个函数指针为空引起。
WSAINVALIDPROVIDER (OS dependent) Invalid service provider version number.
服务提供者返回一个不同于2.2的版本号。
WSA_IO_INCOMPLETE (OS dependent) Overlapped I/O event object not in signaled state.
应用程序试图检测一个没有完成的重叠操作的状态。应用程序使用函数WSAGetOverlappedResult()(参数fWait设置为false)以轮询模式检测一个重叠操作是否完成时将得到此错误码,除非该操作已经完成。
WSA_IO_PENDING (OS dependent) Overlapped operations will complete later.
应用程序已经初始化了一个不能立即完成的重叠操作。当稍后此操作完成时将有完成指示。
WSA_NOT_ENOUGH_MEMORY (OS dependent) Insufficient memory available.
应用程序使用了一个直接映射到Win32函数的WinSock函数,而Win32函数指示缺乏必要的内存资源。
WSANOTINITIALISED (10093) Successful WSAStartup() not yet performed.
应用程序没有调用WSAStartup()函数,或函数WSAStartup()调用失败了。应用程序可能访问了不属于当前活动任务的套接字(例如试图在任务间共享套接字),或调用了过多的WSACleanup()函数。
WSANO_DATA (11004) Valid name, no data record of requested type.
请求的名字合法并且在数据库中找到了,但它没有正确的关联数据用于解析。此错误的通常例子是主机名到地址(使用gethostbyname()或WSAAsyncGetHostByName()函数)的DNS转换请求,返回了MX(Mail eXchanger)记录但是没有A(Address)记录,它指示主机本身是存在的,但是不能直接到达。
WSANO_RECOVERY (11003) This is a non-recoverable error.
此错误码指示在数据库查找时发生了某种不可恢复错误。它可能是因为数据库文件(如BSD兼容的HOSTS、SERVICES或PROTOCOLS文件)找不到,或DNS请求应服务器有严重错误而返回。
WSAPROVIDERFAILEDINIT (OS dependent) Unable to initialize a service provider.
服务提供者的DLL不能加载(LoadLibrary()失败)或提供者的WSPStartup/NSPStartup函数失败。
WSASYSCALLFAILURE (OS dependent) System call failure..
当一个不应该失败的系统调用失败时返回。例如,如果WaitForMultipleObjects()调用失败,或注册的API不能够利用协议/名字空间目录。
WSASYSNOTREADY (10091) Network subsystem is unavailable.
此错误由WSAStartup()函数返回,它表示此时Windows Sockets实现因底层用来提供网络服务的系统不可用。用户应该检查:
是否有合适的Windows Sockets DLL文件在当前路径中。
是否同时使用了多个WinSock实现。如果有多于一个的WINSOCK DLL在系统中,必须确保搜索路径中第一个WINSOCK DLL文件是当前加载的网络子系统所需要的。
查看WinSock实现的文档以确保所有必须的部件都正确地安装并配置好了。
WSATRY_AGAIN (11002) Non-authoritative host not found.
此错误通常是在主机名解析时的临时错误,它意味着本地服务器没有从授权服务器接收到一个响应。稍后的重试可能会获得成功。
WSAVERNOTSUPPORTED (10092) WINSOCK.DLL version out of range.
当前的WinSock实现不支持应用程序指定的Windows Sockets规范版本。检查是否有旧的Windows Sockets DLL文件正在被访问。
WSAEDISCON (10101) Graceful shutdown in progress.
由WSARecv()和WSARecvFrom()函数返回,指示远程方已经初始化了一个“雅致”的关闭序列。
WSA_OPERATION_ABORTED (OS dependent) Overlapped operation aborted.
因为套接字的关闭,一个重叠操作被取消,或是执行了WSAIoctl()函数的SIO_FLUSH命令。
本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教。
一:select模型
二:WSAAsyncSelect模型
三:WSAEventSelect模型
四:Overlapped I/O 事件通知模型
五:Overlapped I/O 完成例程模型
六:IOCP模型
老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。
这和Socket模型非常类似。下面我就以老陈接收信件为例讲解Socket I/O模型~~~
一:select模型
老陈非常想看到女儿的信。以至于他每隔10分钟就下楼检查信箱,看是否有女儿的信~~~~~
在这种情况下,“下楼检查信箱”然后回到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作。
select模型和老陈的这种情况非常相似:周而复始地去检查......如果有数据......接收/发送.......
使用线程来select应该是通用的做法:
procedure TListenThread.Execute;
var
addr : TSockAddrIn;
fd_read : TFDSet;
timeout : TTimeVal;
ASock,
MainSock : TSocket;
len, i : Integer;
begin
MainSock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
addr.sin_family := AF_INET;
addr.sin_port := htons(5678);
addr.sin_addr.S_addr := htonl(INADDR_ANY);
bind( MainSock, @addr, sizeof(addr) );
listen( MainSock, 5 );
while (not Terminated) do
begin
FD_ZERO( fd_read );
FD_SET( MainSock, fd_read );
timeout.tv_sec := 0;
timeout.tv_usec := 500;
if select( 0, @fd_read, nil, nil, @timeout ) > 0 then //至少有1个等待Accept的connection
begin
if FD_ISSET( MainSock, fd_read ) then
begin
for i:=0 to fd_read.fd_count-1 do //注意,fd_count <= 64,也就是说select只能同时管理最多64个连接
begin
len := sizeof(addr);
ASock := accept( MainSock, addr, len );
if ASock <> INVALID_SOCKET then
....//为ASock创建一个新的线程,在新的线程中再不停地select
end;
end;
end;
end; //while (not self.Terminated)
shutdown( MainSock, SD_BOTH );
closesocket( MainSock );
end;
二:WSAAsyncSelect模型
后来,老陈使用了微软公司的新式信箱。这种信箱非常先进,一旦信箱里有新的信件,盖茨就会给老陈打电话:喂,大爷,你有新的信件了!从此,老陈再也不必频繁上下楼检查信箱了,牙也不疼了,你瞅准了,蓝天......不是,微软~~~~~~~~
微软提供的WSAAsyncSelect模型就是这个意思。
WSAAsyncSelect模型是Windows下最简单易用的一种Socket I/O模型。使用这种模型时,Windows会把网络事件以消息的形势通知应用程序。
首先定义一个消息标示常量:
const WM_SOCKET = WM_USER + 55;
再在主Form的private域添加一个处理此消息的函数声明:
private
procedure WMSocket(var Msg: TMessage); message WM_SOCKET;
然后就可以使用WSAAsyncSelect了:
var
addr : TSockAddr;
sock : TSocket;
sock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
addr.sin_family := AF_INET;
addr.sin_port := htons(5678);
addr.sin_addr.S_addr := htonl(INADDR_ANY);
bind( m_sock, @addr, sizeof(SOCKADDR) );
WSAAsyncSelect( m_sock, Handle, WM_SOCKET, FD_ACCEPT or FD_CLOSE );
listen( m_sock, 5 );
....
应用程序可以对收到WM_SOCKET消息进行分析,判断是哪一个socket产生了网络事件以及事件类型:
procedure TfmMain.WMSocket(var Msg: TMessage);
var
sock : TSocket;
addr : TSockAddrIn;
addrlen : Integer;
buf : Array [0..4095] of Char;
begin
//Msg的WParam是产生了网络事件的socket句柄,LParam则包含了事件类型
case WSAGetSelectEvent( Msg.LParam ) of
FD_ACCEPT :
begin
addrlen := sizeof(addr);
sock := accept( Msg.WParam, addr, addrlen );
if sock <> INVALID_SOCKET then
WSAAsyncSelect( sock, Handle, WM_SOCKET, FD_READ or FD_WRITE or FD_CLOSE );
end;
FD_CLOSE : closesocket( Msg.WParam );
FD_READ : recv( Msg.WParam, buf[0], 4096, 0 );
FD_WRITE : ;
end;
end;
三:WSAEventSelect模型
后来,微软的信箱非常畅销,购买微软信箱的人以百万计数......以至于盖茨每天24小时给客户打电话,累得腰酸背痛,喝蚁力神都不好使~~~~~~
微软改进了他们的信箱:在客户的家中添加一个附加装置,这个装置会监视客户的信箱,每当新的信件来临,此装置会发出“新信件到达”声,提醒老陈去收信。盖茨终于可以睡觉了。
同样要使用线程:
procedure TListenThread.Execute;
var
hEvent : WSAEvent;
ret : Integer;
ne : TWSANetworkEvents;
sock : TSocket;
adr : TSockAddrIn;
sMsg : String;
Index,
EventTotal : DWORD;
EventArray : Array [0..WSA_MAXIMUM_WAIT_EVENTS-1] of WSAEVENT;
begin
...socket...bind...
hEvent := WSACreateEvent();
WSAEventSelect( ListenSock, hEvent, FD_ACCEPT or FD_CLOSE );
...listen...
while ( not Terminated ) do
begin
Index := WSAWaitForMultipleEvents( EventTotal, @EventArray[0], FALSE, WSA_INFINITE, FALSE );
FillChar( ne, sizeof(ne), 0 );
WSAEnumNetworkEvents( SockArray[Index-WSA_WAIT_EVENT_0], EventArray[Index-WSA_WAIT_EVENT_0], @ne );
if ( ne.lNetworkEvents and FD_ACCEPT ) > 0 then
begin
if ne.iErrorCode[FD_ACCEPT_BIT] <> 0 then
continue;
ret := sizeof(adr);
sock := accept( SockArray[Index-WSA_WAIT_EVENT_0], adr, ret );
if EventTotal > WSA_MAXIMUM_WAIT_EVENTS-1 then//这里WSA_MAXIMUM_WAIT_EVENTS同样是64
begin
closesocket( sock );
continue;
end;
hEvent := WSACreateEvent();
WSAEventSelect( sock, hEvent, FD_READ or FD_WRITE or FD_CLOSE );
SockArray[EventTotal] := sock;
EventArray[EventTotal] := hEvent;
Inc( EventTotal );
end;
if ( ne.lNetworkEvents and FD_READ ) > 0 then
begin
if ne.iErrorCode[FD_READ_BIT] <> 0 then
continue;
FillChar( RecvBuf[0], PACK_SIZE_RECEIVE, 0 );
ret := recv( SockArray[Index-WSA_WAIT_EVENT_0], RecvBuf[0], PACK_SIZE_RECEIVE, 0 );
......
end;
end;
end;
四:Overlapped I/O 事件通知模型
后来,微软通过调查发现,老陈不喜欢上下楼收发信件,因为上下楼其实很浪费时间。于是微软再次改进他们的信箱。新式的信箱采用了更为先进的技术,只要用户告诉微软自己的家在几楼几号,新式信箱会把信件直接传送到用户的家中,然后告诉用户,你的信件已经放到你的家中了!老陈很高兴,因为他不必再亲自收发信件了!
Overlapped I/O 事件通知模型和WSAEventSelect模型在实现上非常相似,主要区别在“Overlapped”,Overlapped模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。这些提交的请求完成后,应用程序会收到通知。什么意思呢?就是说,如果你想从socket上接收数据,只需要告诉系统,由系统为你接收数据,而你需要做的只是为系统提供一个缓冲区~~~~~
Listen线程和WSAEventSelect模型一模一样,Recv/Send线程则完全不同:
procedure TOverlapThread.Execute;
var
dwTemp : DWORD;
ret : Integer;
Index : DWORD;
begin
......
while ( not Terminated ) do
begin
Index := WSAWaitForMultipleEvents( FLinks.Count, @FLinks.Events[0], FALSE, RECV_TIME_OUT, FALSE );
Dec( Index, WSA_WAIT_EVENT_0 );
if Index > WSA_MAXIMUM_WAIT_EVENTS-1 then //超时或者其他错误
continue;
WSAResetEvent( FLinks.Events[Index] );
WSAGetOverlappedResult( FLinks.Sockets[Index], FLinks.pOverlaps[Index], @dwTemp, FALSE, FLinks.pdwFlags[Index]^ );
if dwTemp = 0 then //连接已经关闭
begin
......
continue;
end else
begin
fmMain.ListBox1.Items.Add( FLinks.pBufs[Index]^.buf );
end;
//初始化缓冲区
FLinks.pdwFlags[Index]^ := 0;
FillChar( FLinks.pOverlaps[Index]^, sizeof(WSAOVERLAPPED), 0 );
FLinks.pOverlaps[Index]^.hEvent := FLinks.Events[Index];
FillChar( FLinks.pBufs[Index]^.buf^, BUFFER_SIZE, 0 );
//递一个接收数据请求
WSARecv( FLinks.Sockets[Index], FLinks.pBufs[Index], 1, FLinks.pdwRecvd[Index]^, FLinks.pdwFlags[Index]^, FLinks.pOverlaps[Index], nil );
end;
end;
五:Overlapped I/O 完成例程模型
老陈接收到新的信件后,一般的程序是:打开信封----掏出信纸----阅读信件----回复信件......为了进一步减轻用户负担,微软又开发了一种新的技术:用户只要告诉微软对信件的操作步骤,微软信箱将按照这些步骤去处理信件,不再需要用户亲自拆信/阅读/回复了!老陈终于过上了小资生活!
Overlapped I/O 完成例程要求用户提供一个回调函数,发生新的网络事件的时候系统将执行这个函数:
procedure WorkerRoutine( const dwError, cbTransferred : DWORD; const
lpOverlapped : LPWSAOVERLAPPED; const dwFlags : DWORD ); stdcall;
然后告诉系统用WorkerRoutine函数处理接收到的数据:
WSARecv( m_socket, @FBuf, 1, dwTemp, dwFlag, @m_overlap, WorkerRoutine );
然后......没有什么然后了,系统什么都给你做了!微软真实体贴!
while ( not Terminated ) do//这就是一个Recv/Send线程要做的事情......什么都不用做啊!!!
begin
if SleepEx( RECV_TIME_OUT, True ) = WAIT_IO_COMPLETION then //
begin
;
end else
begin
continue;
end;
end;
六:IOCP模型
微软信箱似乎很完美,老陈也很满意。但是在一些大公司情况却完全不同!这些大公司有数以万计的信箱,每秒钟都有数以百计的信件需要处理,以至于微软信箱经常因超负荷运转而崩溃!需要重新启动!微软不得不使出杀手锏......
微软给每个大公司派了一名名叫“Completion Port”的超级机器人,让这个机器人去处理那些信件!
“Windows NT小组注意到这些应用程序的性能没有预料的那么高。特别的,处理很多同时的客户请求意味着很多线程并发地运行在系统中。因为所有这些线程都是可运行的[没有被挂起和等待发生什么事],Microsoft意识到NT内核花费了太多的时间来转换运行线程的上下文[Context],线程就没有得到很多CPU时间来做它们的工作。大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比起创建进程开销要小,但也远不是没有开销的。我们不妨设想一下:如果事先开好N个线程,让它们在那hold[堵塞],然后可以将所有用户的请求都投递到一个消息队列中去。然后那N个线程逐一从消息队列中去取出消息并加以处理。就可以避免针对每一个用户请求都开线程。不仅减少了线程的资源,也提高了线程的利用率。理论上很不错,你想我等泛泛之辈都能想出来的问题,Microsoft又怎会没有考虑到呢?”-----摘自nonocast的《理解I/O Completion Port》
先看一下IOCP模型的实现:
//创建一个完成端口
FCompletPort := CreateIoCompletionPort( INVALID_HANDLE_VALUE, 0,0,0 );
//接受远程连接,并把这个连接的socket句柄绑定到刚才创建的IOCP上
AConnect := accept( FListenSock, addr, len);
CreateIoCompletionPort( AConnect, FCompletPort, nil, 0 );
//创建CPU数*2 + 2个线程
for i:=1 to si.dwNumberOfProcessors*2+2 do
begin
AThread := TRecvSendThread.Create( false );
AThread.CompletPort := FCompletPort;//告诉这个线程,你要去这个IOCP去访问数据
end;
OK,就这么简单,我们要做的就是建立一个IOCP,把远程连接的socket句柄绑定到刚才创建的IOCP上,最后创建n个线程,并告诉这n个线程到这个IOCP上去访问数据就可以了。
再看一下TRecvSendThread线程都干些什么:
procedure TRecvSendThread.Execute;
var
......
begin
while (not self.Terminated) do
begin
//查询IOCP状态(数据读写操作是否完成)
GetQueuedCompletionStatus( CompletPort, BytesTransd, CompletKey, POVERLAPPED(pPerIoDat), TIME_OUT );
if BytesTransd <> 0 then
....;//数据读写操作完成
//再投递一个读数据请求
WSARecv( CompletKey, @(pPerIoDat^.BufData), 1, BytesRecv, Flags, @(pPerIoDat^.Overlap), nil );
end;
end;
读写线程只是简单地检查IOCP是否完成了我们投递的读写操作,如果完成了则再投递一个新的读写请求。
应该注意到,我们创建的所有TRecvSendThread都在访问同一个IOCP(因为我们只创建了一个IOCP),并且我们没有使用临界区!难道不会产生冲突吗?不用考虑同步问题吗?
呵呵,这正是IOCP的奥妙所在。IOCP不是一个普通的对象,不需要考虑线程安全问题。它会自动调配访问它的线程:如果某个socket上有一个线程A正在访问,那么线程B的访问请求会被分配到另外一个socket。这一切都是由系统自动调配的,我们无需过问。
呵呵,终于写完了,好累......以上所有的源代码可以从这里看到:
http://community.csdn.net/Expert/topic/3844/3844679.xml?temp=5.836123E-02
不过代码写的很简陋,请多包涵!
1.首先你需要知道什么是"Socket Modes",什么是"Socket I/O Models"?
Socket Modes : Determines how winsock functions behave when called with a socket.
Socket I/O Models : Describes how an application manages and processes I/O on a socket.
2.那么Winsock提供了哪几种"Socket Modes"给程序员用呢?
a.Blocking Mode(阻塞式的)
b.Noblocking Mode(非阻塞式的)
3.详细解释一下阻塞和非阻塞.
我们那最常用的send和recv两个函数来说吧...
比如你调用send函数发送一定的Byte,在系统内部send做的工作其实只是把数据传输(Copy)到TCP/IP协议栈的输出缓冲区,它执行成功并不代表数据已经成功的发送出去了,如果TCP/IP协议栈没有足够的可用缓冲区来保存你Copy过来的数据的话...这时候就体现出阻塞和非阻塞的不同之处了:对于阻塞模式的socket send函数将不返回直到系统缓冲区有足够的空间把你要发送的数据Copy过去以后才返回,而对于阻塞的socket来说send会立即返回WSAEWOULDDBLOCK告诉调用者说:"发送操作被阻塞了!!!你想办法处理吧..."
对于recv函数,同样道理,该函数的内部工作机制其实是在等待TCP/IP协议栈的接收缓冲区通知它说:嗨,你的数据来了.对于阻塞模式的socket来说如果TCP/IP协议栈的接收缓冲区没有通知一个结果给它它就一直不返回:耗费着系统资源....对于阻塞模式的socket该函数会马上返回,然后告诉你:WSAEWOULDDBLOCK.
3.对于阻塞模式(Blocking Mode)我采取开线程的方式不就搞定了吗,而且还不用我去判断基本的收/发什么时候完成,多省心?
没错,这位大虾说的开线程的方式具体的做法如下:我们将应用划分成一个读线程(Reading Thread)一个工作线程(Working Thread),两个线程靠同步对象(Synchronization Object,可以是Event,Mutex...)来共享一个数据缓冲区,读线程(Reading Thread)负责从网
络读取数据并将其放到共享缓冲区中,在适当的时机(比如说读线程将工作线程需要的数据接收下来之后)触发一个事件,通知/唤醒工作线程(Working Thread),下面是上述文字的简单代码实现:
CRITICAL_SECTION g_data;
HANDLE g_hEvent;
TCHAR g_szBuffer[MAX_BUFFER_SIZE];
int g_nBytes;
void ReadThread(void)
{
int nTotle = nRead = nLeft = nBytes = 0;
while(!done)
{
nTotal = 0;
nLeft = NUM_BYTES_REQUIRED; //要读多少的Bytes
while( nTotal != NUM_BYTE_REQUIRED)
{
// 因为涉及线程和全局变量所以说需要用到关键代码段(Critical Section)
EnterCriticalSection(&g_data);
nRead = recv(sock,&(g_szBuffer[MAX_BUFFER_SIZE] - nBytes),nLeft);
if( -1 == nread )
{
printf("socket %d error.\n",WSAGetLasError());
ExitThread();
}
nTotal += nRead;
nLeft -= nRead;
nBytes += nRead;
LeaveCriticalSelection(&g_data);
}
// 从网络接收完指定的(NUM_BYTES_REQUIRED)Bytes之后通知WorkThread启动
SetEvent(g_hEvent);
}
}
void WorkThread(void)
{
WaitForSingleObject(g_hEvent);
EnterCriticalSection(&g_data);
// 主要的处理工作...
DoSomeWorkOnData(g_szBuffer);
g_nBytes -= NUM_BYTE_REQUIRED;
LeaveCriticalSelection(&g_data);
}
这种方式的一个缺点是:应用程序很难同时通过多个建立好连接的socket通信.当然话说回来我们可以为每一个连接好的socket都开一个Reading Thread and WorkThread,但是呢这会带来很大的开销,而且扩展性很差,试想:如果用这样的模型建立一个服务器端来一个连接开至少两个线程...天..要是业务负担很大/连接的socket很多的话怎么办??所以说别多想了...
这样的方式是很不适合做服务器端的.(注意:我说的服务器端是指C/S中的S端逻辑).
从编写代码的角度解读一下IO中最复杂的IOCP模型
下面的一段代码是Microsoft Win32 SDK里面的例子,这是一个Echo服务器程序:
//
// 这就不说了,为了使用Winsock2必须包含的头文件...
//
#include <winsock2.h>
#include <windows.h>
#include <stdio.h>
#define PORT 5150 // 这个服务器选择5150端口
#define DATA_BUFSIZE 8192 // 把Buffer定义为8K是一个比较被Microsoft推荐的大小
//
// 这个结构体很重要,请一定看清楚,到后面用到的时候别糊涂了
// 特别注意:把Overlapped放最开头是有原因的,你应该明确的是
// 如果有:
// PER_IO_OPERATION_DATA PerIoData;
// &PerIoData->Overlapped 和 &PerIoData是一样的/等价的/
// 指向同一块内存区域的.
//
typedef struct
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
CHAR Buffer[DATA_BUFSIZE];
DWORD BytesSEND; // 下面两个参数是用来记录每个connected socket上面的收发的Bytes的
DWORD BytesRECV;
} PER_IO_OPERATION_DATA, * LPPER_IO_OPERATION_DATA;
// 别把这个结构体和上面的结构体混淆了,名字很像的...其实它在这个程序中就相当于是那个和Client端连接的socket
typedef struct
{
SOCKET Socket;
} PER_HANDLE_DATA, * LPPER_HANDLE_DATA;
DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID);
void main(void)
{
SOCKADDR_IN InternetAddr;
//
// 一个listen和一个Accept socket是服务器程序的老套路了...
//
SOCKET Listen;
SOCKET Accept;
//
// IO完成端口映射到程序就是一个HANDLE
//
HANDLE CompletionPort;
SYSTEM_INFO SystemInfo;
//
// 看清楚,这是指针...不怕你笑话我们处还真有分不清和指针相关的一切概念的人,比如下面的代码:
// 我写了一个DLL,里面有一个这样的函数:
// BOOL GetPlayingPosition(DWORD * dwPos);
// 结果大虾调用的时候是这样的....
// DWORD * p;
// GetPlayingPostion(p);
// 它是调用爽了...我呢...:-(
// 当然了,也不好定义说它就是分不清指针,也许人家就不会写程序呢..对吧..
//
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
int i;
DWORD RecvBytes;
DWORD Flags;
DWORD ThreadID;
WSADATA wsaData;
DWORD Ret;
// 就像CoInitialize()一样的把Winsock 2.2服务启动起来...
if ((Ret = WSAStartup(0x0202, &wsaData)) != 0)
{
printf("WSAStartup failed with error %d\n", Ret);
return;
}
// CreateIoCompletionPort有两个不同的用途:1.创建完成端口;2.把已经创建好的完成端口和一个Socket关联/绑定起来.
// 下面这句话是它的第一个用途:创建完成端口
if ((CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)) == NULL)
{
printf( "CreateIoCompletionPort failed with error: %d\n", GetLastError());
return;
}
//
// 下面这一些代码说得是:根据该服务器跑的机器上有几个CPU建立2*CPU数量的Thread.
// 这涉及到完成端口工作线程应该创建多少个的问题,在早期(MSDN 2000)Microsoft说
// 是WorkThreadNums = 2 * CPU + 2,现在的MSDN 2003说是WorkThreadNums = 2 * CPU;
//
GetSystemInfo(&SystemInfo);
for(i = 0; i < SystemInfo.dwNumberOfProcessors * 2; i++)
{
HANDLE ThreadHandle;
// 创建一个线程并把上面创建的完成端口传给线程
if ((ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,
0, &ThreadID)) == NULL)
{
printf("CreateThread() failed with error %d\n", GetLastError());
return;
}
// 这个风格我喜欢,创建完Thread就把人家的Handle给release,省得要是线程异常退出搞得资源泄漏之类的麻烦事情.
CloseHandle(ThreadHandle);
}
// 开始创建监听Socket...
if ((Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0,WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
printf("WSASocket() failed with error %d\n", WSAGetLastError());
return;
}
InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
InternetAddr.sin_port = htons(PORT);
// 把监听socket和绑定TCP/IP协议栈做一个绑定...
if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr)) == SOCKET_ERROR)
{
printf("bind() failed with error %d\n", WSAGetLastError());
return;
}
// 设置一次能接受5个连接
if (listen(Listen, 5) == SOCKET_ERROR)
{
printf("listen() failed with error %d\n", WSAGetLastError());
return;
}
//
// 下面是服务器程序标准的循环了...
//
while(TRUE)
{
if ((Accept = WSAAccept(Listen, NULL, NULL, NULL, 0)) == SOCKET_ERROR)
{
printf("WSAAccept() failed with error %d\n", WSAGetLastError());
return;
}
//
// 接收到一个连接就new一个新的socket并承揽下连接进来的socket的接下来的接待/处理工作
// 为什么不用new呢?我想是因为用Windows提供的内存管理函数,这样的话在业务量大的情况下
// 出现内存匮乏的情况下不至于直接就异常了,怎么着也就是该次的内存分配失败...
//
if ((PerHandleData = (LPPER_HANDLE_DATA) GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
return;
}
// Associate the accepted socket with the original completion port.
printf("Socket number %d connected\n", Accept);
PerHandleData->Socket = Accept;
//
// 把accept socket和IO完成端口关联/绑定起来,并把accept socket传给IOCP机制这样的话
// 当调用GetQueuedCompletionStatus的时候就可以得到该值.
//
if (CreateIoCompletionPort((HANDLE) Accept, CompletionPort, (DWORD) PerHandleData,
0) == NULL)
{
printf("CreateIoCompletionPort failed with error %d\n", GetLastError());
return;
}
// 下面这个结构体也是会被传到工作线程中去的,怎么传呢?注意看下面红色部分
if ((PerIoData = (LPPER_IO_OPERATION_DATA) GlobalAlloc(GPTR,sizeof(PER_IO_OPERATION_DATA))) == NULL)
{
printf("GlobalAlloc() failed with error %d\n", GetLastError());
return;
}
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->BytesSEND = 0;
PerIoData->BytesRECV = 0;
PerIoData->DataBuf.len = DATA_BUFSIZE;
PerIoData->DataBuf.buf = PerIoData->Buffer;
Flags = 0;
//
// 在accept socket上面发起IO请求:WSARecv()以此激发IOCP工作机制..
// 注意WSARecv的第五个参数,也就是说我这时候告诉WSARecv的不仅仅是
// PerIoData->Overlapped的地址同时也是PerIoData的地址...
//
if (WSARecv(Accept, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return;
}
}
}
}
//
// 工作线程
//
DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
{
HANDLE CompletionPort = (HANDLE) CompletionPortID;
DWORD BytesTransferred;
LPOVERLAPPED Overlapped;
LPPER_HANDLE_DATA PerHandleData;
LPPER_IO_OPERATION_DATA PerIoData;
DWORD SendBytes, RecvBytes;
DWORD Flags;
while(TRUE)
{
//
// 这个函数解释清楚了整个机制也就明白了...
// 首先我们看CompletionPort应该没有问题,是从创建线程的主线程传过来的.
// BytesTransferred指明了这次IO传输了多少Bytes...
// PerHandleData是CreateIoCompletionPort的时候穿过来的,MSDN里面叫CompletionKey.
// 请注意PerIoData这个参数,本来按照MSDN的解释如下:
// "Pointer to a variable that receives the address of the OVERLAPPED structure that was
// specified when the completed I/O operation was started."
// 其实在这这个lpOverlapped传过来的就是上面WSARecv传入的overlapped的地址...
// 这下明白了吧:主线程借着overlapped的名声传了一个结构体指针过来..:-)
//
if (GetQueuedCompletionStatus(CompletionPort, &BytesTransferred,
(LPDWORD)&PerHandleData, (LPOVERLAPPED *) &PerIoData, INFINITE) == 0)
{
printf("GetQueuedCompletionStatus failed with error %d\n", GetLastError());
return 0;
}
//
// First check to see if an error has occured on the socket and if so
// then close the socket and cleanup the SOCKET_INFORMATION structure
// associated with the socket.
//
if (BytesTransferred == 0)
{
printf("Closing socket %d\n", PerHandleData->Socket);
if (closesocket(PerHandleData->Socket) == SOCKET_ERROR)
{
printf("closesocket() failed with error %d\n", WSAGetLastError());
return 0;
}
GlobalFree(PerHandleData);
GlobalFree(PerIoData);
continue;
}
//
// Check to see if the BytesRECV field equals zero. If this is so, then
// this means a WSARecv call just completed so update the BytesRECV field
// with the BytesTransferred value from the completed WSARecv() call.
//
if (PerIoData->BytesRECV == 0)
{
PerIoData->BytesRECV = BytesTransferred;
PerIoData->BytesSEND = 0;
}
else
{
PerIoData->BytesSEND += BytesTransferred;
}
if (PerIoData->BytesRECV > PerIoData->BytesSEND)
{
//
// Post another WSASend() request.
// Since WSASend() is not gauranteed to send all of the bytes requested,
// continue posting WSASend() calls until all received bytes are sent.
//
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->DataBuf.buf = PerIoData->Buffer + PerIoData->BytesSEND;
PerIoData->DataBuf.len = PerIoData->BytesRECV - PerIoData->BytesSEND;
if (WSASend(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &SendBytes, 0,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSASend() failed with error %d\n", WSAGetLastError());
return 0;
}
}
}
else
{
PerIoData->BytesRECV = 0;
// Now that there are no more bytes to send post another WSARecv() request.
Flags = 0;
ZeroMemory(&(PerIoData->Overlapped), sizeof(OVERLAPPED));
PerIoData->DataBuf.len = DATA_BUFSIZE;
PerIoData->DataBuf.buf = PerIoData->Buffer;
if (WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf), 1, &RecvBytes, &Flags,
&(PerIoData->Overlapped), NULL) == SOCKET_ERROR)
{
if (WSAGetLastError() != ERROR_IO_PENDING)
{
printf("WSARecv() failed with error %d\n", WSAGetLastError());
return 0;
}
}
}
}
}
很多时候远程系统在执行并发任务的时候,会把它接收到数据的长度以数字的形式发送出去。但用socket发送和接收数字型数据的时候,要考虑到一个问题:要根据网络另一端机器的类型转换数据。尤其需要知道怎样把要发送的数据格式(网络格式)从本地机器的格式(主机格式)转换成为行业标准格式。
使用IPAddress.NetworkToHostOrder可以把数据从网络规则转换为主机格式,下面的ReceiveHeader函数说明了它的用法,ReceiveHeader函数实现过程如下:
1 用Socket.Receive从远程机器接收数据。
2 验证接收到的字节数是4。
3 Socket.Receive返回一个字节型数组,BitConvert.ToInt32把它转换成数字型数值。
4 最后,IPAddress.NetworkToHostOrder把长数值转换为主机格式。
public int ReceiveHeader(Socket socket)
{
int dataSize = -1; // error
byte [] buffer = new byte[4];
int bytesRead = socket.Receive(buffer, 4,
System.Net.Sockets.SocketFlags.None);
if (4 == bytesRead)
{
dataSize = BitConverter.ToInt32(buffer, 0);
dataSize = IPAddress.NetworkToHostOrder(dataSize);
}
else // error condition
return dataSize;
}
下面再来看一下怎样用多线程读取的方法为每个字符串都建立连接,从远程机器接收字符串型数据。在这种情况下,要把字节型数据转换成String型对象。你可以根据需要用ASCIIEncoding或UnicodeEncoding类进行转换。ReceiveDetail函数按以下步骤实现(此函数必须在ReceiveHeader后调用,因为datasize的值是从ReceiveHeader中得到的。)
1 在while循环中调用Socket.Receive,直到无返回值为止。数据被读入一个字节型数组。
2 建立一个ASCIIEncoding对象。
3 调用ASCIIEncoding.GetString把字节型数组转换成String对象,然后把它和先前读入的数据连接。
public string ReceiveDetail(Socket socket, byte[] buffer,
int dataSize)
{
string response = "";
int bytesReceived = 0;
int totalBytesReceived = 0;
while (0 < (bytesReceived =
socket.Receive(buffer, (dataSize - totalBytesReceived),
SocketFlags.None)))
{
totalBytesReceived += bytesReceived;
ASCIIEncoding encoding = new ASCIIEncoding();
response += encoding.GetString(buffer, 0, bytesReceived);
}
return response;
}
一、Delphi与Socket
计算机网络是由一系列网络通信协议组成的,其中的核心协议是传输层的TCP/IP和UDP协议。TCP是面向连接的,通信双方保持一条通路,好比目前的电话线,使用telnet登陆BBS,用的就是TCP协议;UDP是无连接的,通信双方都不保持对方的状态,浏览器访问Internet时使用的HTTP协议就是基于UDP协议的。TCP和UDP协议都非常复杂,尤其是TCP协议,为了保证网络传输的正确性和有效性,必须进行一系列复杂的纠错和排序等处理。
Socket是建立在传输层协议(主要是TCP和UDP)上的一种套接字规范,最初是由美国加州Berkley大学提出,它定义两台计算机间进行通信的规范(也是一种编程规范),如果说两台计算机是利用一个“通道“进行通信,那么这个“通道“的两端就是两个套接字。套接字屏蔽了底层通信软件和具体操作系统的差异,使得任何两台安装了TCP协议软件和实现了套接字规范的计算机之间的通信成为可能。
微软的Windows Socket规范(简称winsock)对Berkley的套接字规范进行了扩展,利用标准的Socket的方法,可以同任何平台上的Socket进行通信;利用其扩展,可以更有效地实现在Windows平台上计算机间的通信。在Delphi中,其底层的Socket也应该是Windows的Socket。Socket减轻了编写计算机间通信软件的难度,但总的说来还是相当复杂的(这一点在后面具体会讲到);Inprise在Delphi中对Windows Socket进行了有效的封装,使得用户可以很方便地编写网络通信程序。下面我们实例解读在Delphi中如何利用Socket编写通信程序。
二、利用Delphi编写Socket通信程序。
下面是一个简单的Socket通信程序,其中客户机和服务机是同一个程序,当客户机(服务器)在一个memo1中输入一段文字然后敲入回车,该段文字就可以显示在服务器(客户机)的memo2中,反之亦成立。具体步骤如下:
1、新建一个form,任意命名,不妨设之为chatForm;放上一个MainMenu(在Standard栏中),建立ListenItem、ConnectItem、Disconnect和Exit菜单项;在从Internet栏中选择TServerSocket、TClientSocket添加到chatForm中,其中把TClientSocket的名字设为ClientSocket, port设为1025,默认的active为false;把TServerSocket的名字设为ServerSocket,port设为1025,默认的active为false,其他的不变;再放入两个memo,一个命名为memo1,另外一个命名为memo2,其中把memo2的color设置为灰色,因为主要用来显示对方的输入。下面我们一边编写代码一边解释原因。
2、双击ListemItem。写入如下代码:
procedure TChatForm.ListenItemClick(Sender: TObject);
begin
ListenItem.Checked := not ListenItem.Checked;
if ListenItem.Checked then
begin
ClientSocket.Active := False;
ServerSocket.Active := True;
end
else
begin
if ServerSocket.Active then
ServerSocket.Active := False;
end;
end;
该程序段的说明如下:当用户选择ListemItem时,该ListenItem取反,如果选中的话,说明处于Listen状态,读者要了解的是:listen是Socket作为Server时一个专有的方法,如果处于listen,则ServerSocket设置为活动状态;否则,取消listen,则关闭ServerSocket。实际上,只有用户一开始选择该菜单项,表明该程序用作Server。反之,如果用户选择ConnectItem,则必然作为Client使用。
3、双击ConnectItem,敲入以下代码。
procedure TChatForm.ConnectItemClick(Sender: TObject);
begin
if ClientSocket.Active then ClientSocket.Active := False;
if InputQuery('Computer to connect to', 'Address Name:', Server) then
if Length(Server) > 0 then
with ClientSocket do
begin
Host := Server;
Active := True;
ListenItem.Checked := False;
end;
end;
这段程序的主要功能就是当用户选择ConnectItem菜单项时,设置应用程序为客户机,弹出input框,让用户输入服务器的地址。这也就是我们不一开始固定ClientSocket的host的原因,这样用户可以动态地连接不同的服务器。读者需要了解的是主机地址只是Socket作为客户机时具有的一个属性,Socket作为服务器时“一般“不用地址,因为它同本机绑定。
4、在memo1的keydown方法中写入如下代码:
procedure TChatForm.Memo1KeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
begin
if Key = VK_Return then
if IsServer then
ServerSocket.Socket.Connections[0].SendText(Memo1.Lines[Memo1.Lines.Count - 1])
else
ClientSocket.Socket.SendText(Memo1.Lines[Memo1.Lines.Count - 1]);
end;
该段代码的作用很明显,就是开始发消息了。其中如果是Server的话,它只向第一个客户机发消息,由于一个服务器可以连接多个客户机,而同客户机的每一个连接都由一个Socket来维持,因此ServerSocket.Socket.Connnections数组中存储的就是同Client维持连接的Socket。在标准Socket中,服务器方的Socket通过accept()方法的返回值获取维持同客户机连接的Socket,而发送、接受消息的方法分别为send(sendto)和recv(recvfrom), Delphi对此进行了封装。
5、其余代码的简要介绍。
procedure TChatForm.ServerSocketAccept(Sender: TObject;
Socket: TCustomWinSocket);
begin
IsServer := True;
end;
ServerSocket的Accept方法,当客户机第一次连接时完成,通过其参数可以认为,它是在标准的accept方法后执行的,因为有TCustomWinSocket这个参数类型,它应该是标准Server方Socket的返回值。
procedure TChatForm.ClientSocketRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
Memo2.Lines.Add(Socket.ReceiveText);
end;
procedure TChatForm.ServerSocketClientRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
Memo2.Lines.Add(Socket.ReceiveText);
end;
这两段代码分别是服务器方和客户机方在收到对方的消息时,由Delphi触发的,作用是在memo2中显示收到的消息。其中,ClientSocketRead中的Socket实际上就是Socket本身,而在ServerSocketClientRead中的Socket实际上是ServerSocket.Socket.Connection[]中的某个Socket。不过在Delphi中,对服务器方的Socket进行了有效的封装。
procedure TChatForm.ServerSocketClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
Memo2.Lines.Clear;
end;
procedure TChatForm.ClientSocketDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
ListenItemClick(nil);
end;
这两段比较简单。其中ServerSocketClientConnect在ServerSocket收到一个新的连接时触发。而ClientSocketDisconnect在ClientSocket发出Disconncet时触发。
procedure TChatForm.Exit1Click(Sender: TObject);
begin
ServerSocket.Close;
ClientSocket.Close;
Close;
end;
procedure TChatForm.Disconnect1Click(Sender: TObject);
begin
ClientSocket.Active := False;
ServerSocket.Active := True;
end;
第一段为关闭应用程序。在标准Socket中,每个Socket在关闭时,必须调用closesocket()方法,否则系统不会释放资源。而在ServerSockt.Close和ClientSocket.Close中,系统内部肯定调用了closesocket()方法。
三、标准Socket与Delphi中的Socket。
标准的Socket的应用程序框架如下:
Server方: Socket()[ 新建一个Socket]--Bind()[ 同服务器地址邦定 ]--Listen() --Accept()--block wait--read()[接受消息,在windows平台中,方法为send(TCP),或者是sendto(UDP)]--处理服务请求--Write()[发送消息,在windows平台中,方法为send(TCP), 或者为sendto(UDP)。
Client方相对简单:Socket()--Connect()[通过一定的port连接特定的服务器,这是与服务器建立连接]--Write()--Read()。
Socket可以是基于TCP的,也可以是基于UDP,同时Socket甚至建立在其他的协议,比如IPX/SPX,DECNet等。在新建一个Socket时,可以指定新建何类Socket。Bind()用来同服务器的地址邦定,如果一个主机只有一个IP地址,实际上邦定的作用就相对多余了。Listen()开始监听网络,Accept()用于接受连接,其返回值是保持同客户机联系的Socket。
在Delphi中,对于Windows中的Socket进行了有效的封装。在Delphi中,按其继承关系,可以分层两类:
一、TComponent--TAbstractSocket--TCustomSocket--TCustomServerSocket--TServerSocket
TComponent--TAbstractSocket--TCustomSocket--TClientSocket
二、直接从TObject继承过来:
TObject--TCustomWinSocket--TServerWinSocket
TObject--TCustomWinSocket--TClientWinSocket
TObject--TCustomWinSocket--TServerClientWinSocket
可以看出第一类建立在TCustomSocket基础上,第二类建立在TCustomWinSocket的基础上。第一类建立在TComponet的基础上,第二类直接构建在TObject基础上。因此如果用户非常熟悉Socket并且想要编写控制台程序时,可以使用TCustomWinScoket类。
同uses中可以看出,它们都在ScktComp.pas中实现,而在schtComp.pas中,则包含了winsock.pas文件,如果继续深入winsock文件,在其中可以发现所有的Windows Socket的基本方法。
实际上,如果你了解了标准Socket的应用程序框架,对于使用Delphi编写Socket应用程序也就得心应手了;这不是说你必须了解复杂的Socket中的标准函数,也没有必要,因为Delphi已经为你做了很好的封装了,这也正是Delphi的强势所在,你只要了解那么一点点的基本框架。
笔者在工作中遇到对局域网中各工作站与服务器之间进行Socket通信的问题。现在将本人总结出来的TServerSocket和TClientSocket两个组件的基本用法写出来,希望与您分享。
ClientSocket组件为客户端组件。它是通信的请求方,也就是说,它是主动地与服务器端建立连接。
ServerSocket组件为服务器端组件。它是通信的响应方,也就是说,它的动作是监听以及被动接受客户端的连接请求,并对请求进行回复。
ServerSocket组件可以同时接受一个或多个ClientSocket组件的连接请求,并与每个ClientSocket组件建立单独的连接,进行单独的通信。因此,一个服务器端可以为多个客户端服务。
设计思路
本例包括一个服务器端程序和一个客户端程序。客户端程序可以放到多个计算机上运行,同时与服务器端进行连接通信。
本例的重点,一是演示客户端与服务器端如何通信;二是当有多个客户端同时连接到服务器端时,服务器端如何识别每个客户端,并对请求给出相应的回复。为了保证一个客户端断开连接时不影响其它客户端与服务器端的通信,同时保证服务器端能够正确回复客户端的请求,在本例中声明了一个记录类型:
type
client_record=record
CHandle: integer; //客户端套接字句柄
CSocket:TCustomWinSocket; //客户端套接字
CName:string; //客户端计算机名称
CAddress:string; //客户端计算机IP地址
CUsed: boolean; //客户端联机标志
end;
利用这个记录类型数据保存客户端的信息,同时保存当前客户端的连接状态。其中,CHandle保存客户端套接字句柄,以便准确定位每个与服务器端保持连接的客户端;Csocket保存客户端套接字,通过它可以对客户端进行回复。Cused记录当前客户端是否与服务器端保持连接。
下面对组件ServerSocket和ClientSocket的属性设置简单说明。
ServerSocket的属性:
· Port,是通信的端口,必须设置。在本例中设置为1025;
· ServerTypt,服务器端读写信息类型,设置为stNonBlocking表示异步读写信息,本例中采用这种方式。
· ThreadCacheSize,客户端的最大连接数,就是服务器端最多允许多少客户端同时连接。本例采用默认值10。
其它属性采用默认设置即可。
ClientSocket的属性:
· Port,是通信的端口,必须与服务器端的设置相同。在本例中设置为1025;
· ClientType,客户端读写信息类型,应该与服务器端的设置相同,为stNonBlocking表示异步读写信息。
· Host,客户端要连接的服务器的IP地址。必须设置,当然也可以在代码中动态设置。
其它属性采用默认设置即可。
程序源代码:
· 服务器端源码(uServerMain.pas):
unit uServerMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, ToolWin, ComCtrls, ExtCtrls, StdCtrls, Buttons;
const
CMax=10; //客户端最大连接数
type
client_record=record
CHandle: integer; //客户端套接字句柄
CSocket:TCustomWinSocket; //客户端套接字
CName:string; //客户端计算机名称
CAddress:string; //客户端计算机IP地址
CUsed: boolean; //客户端联机标志
end;
type
TfrmServerMain = class(TForm)
ServerSocket: TServerSocket;
ControlBar1: TControlBar;
ToolBar1: TToolBar;
tbConnect: TToolButton;
tbClose: TToolButton;
tbDisconnected: TToolButton;
Edit1: TEdit;
Memo1: TMemo;
StatusBar: TStatusBar;
procedure tbConnectClick(Sender: TObject);
procedure tbDisconnectedClick(Sender: TObject);
procedure ServerSocketClientRead(Sender: TObject;
Socket: TCustomWinSocket);
procedure ServerSocketListen(Sender: TObject;
Socket: TCustomWinSocket);
procedure ServerSocketClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure ServerSocketClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure tbCloseClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ServerSocketGetSocket(Sender: TObject; Socket: Integer;
var ClientSocket: TServerClientWinSocket);
procedure ServerSocketClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
private
{ Private declarations }
public
{ Public declarations }
session: array[0..CMax] of client_record; //客户端连接数组
Sessions: integer; //客户端连接数
end;
var
frmServerMain: TfrmServerMain;
implementation
{$R *.DFM}
//打开套接字连接,并使套接字进入监听状态
procedure TfrmServerMain.tbConnectClick(Sender: TObject);
begin
ServerSocket.Open ;
end;
//关闭套接字连接,不再监听客户端的请求
procedure TfrmServerMain.tbDisconnectedClick(Sender: TObject);
begin
ServerSocket.Close;
StatusBar.Panels[0].Text :='服务器套接字连接已经关闭,无法接受客户端的连接请求.';
end;
//从客户端读取信息
procedure TfrmServerMain.ServerSocketClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
i:integer;
begin
//将从客户端读取的信息添加到Memo1中
Memo1.Lines.Add(Socket.ReceiveText);
for i:=0 to sessions do
begin
//取得匹配的客户端
if session[i].CHandle = Socket.SocketHandle then
begin
session[i].CSocket.SendText('回复客户端'+session[i].CAddress+' ==> '+Edit1.Text);
end;
end;
end;
//服务器端套接字进入监听状态,以便监听客户端的连接
procedure TfrmServerMain.ServerSocketListen(Sender: TObject;
Socket: TCustomWinSocket);
begin
StatusBar.Panels[0].Text :='等待客户端连接...';
end;
//当客户端连接到服务器端以后
procedure TfrmServerMain.ServerSocketClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
var
i,j:integer;
begin
j:=-1;
for i:=0 to sessions do
begin
//在原有的客户端连接数组中有中断的客户端连接
if not session[i].CUsed then
begin
session[i].CHandle := Socket.SocketHandle ;//客户端套接字句柄
session[i].CSocket := Socket; //客户端套接字
session[i].CName := Socket.RemoteHost ; //客户端计算机名称
session[i].CAddress := Socket.RemoteAddress ;//客户端计算机IP
session[i].CUsed := True; //连接数组当前位置已经占用
Break;
end;
j:=i;
end;
if j=sessions then
begin
inc(sessions);
session[j].CHandle := Socket.SocketHandle ;
session[j].CSocket := Socket;
session[j].CName := Socket.RemoteHost ;
session[j].CAddress := Socket.RemoteAddress ;
session[j].CUsed := True;
end;
StatusBar.Panels[0].Text := '客户端 '+Socket.RemoteHost + ' 已经连接';
end;
//当客户端断开连接时
procedure TfrmServerMain.ServerSocketClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
var
i:integer;
begin
for i:=0 to sessions do
begin
if session[i].CHandle =Socket.SocketHandle then
begin
session[i].CHandle :=0;
session[i].CUsed := False;
Break;
end;
end;
StatusBar.Panels[0].Text :='客户端 '+Socket.RemoteHost + ' 已经断开';
end;
//关闭窗口
procedure TfrmServerMain.tbCloseClick(Sender: TObject);
begin
Close;
end;
procedure TfrmServerMain.FormCreate(Sender: TObject);
begin
sessions := 0;
end;
procedure TfrmServerMain.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
ServerSocket.Close ;
end;
//当客户端正在与服务器端连接时
procedure TfrmServerMain.ServerSocketGetSocket(Sender: TObject;
Socket: Integer; var ClientSocket: TServerClientWinSocket);
begin
StatusBar.Panels[0].Text :='客户端正在连接...';
end;
//客户端发生错误
procedure TfrmServerMain.ServerSocketClientError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
StatusBar.Panels[0].Text :='客户端'+Socket.RemoteHost +'发生错误!';
ErrorCode := 0;
end;
end.
· 客户端源码(uClientMain.pas):
unit uClientMain;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ScktComp, ComCtrls, ToolWin, ExtCtrls, StdCtrls, Buttons;
const
SocketHost = '172.16.1.6'; //服务器端地址
type
TfrmClientMain = class(TForm)
ControlBar1: TControlBar;
ToolBar1: TToolBar;
tbConnected: TToolButton;
tbSend: TToolButton;
tbClose: TToolButton;
tbDisconnected: TToolButton;
ClientSocket: TClientSocket;
Edit1: TEdit;
Memo1: TMemo;
StatusBar: TStatusBar;
btnSend: TBitBtn;
procedure tbConnectedClick(Sender: TObject);
procedure tbDisconnectedClick(Sender: TObject);
procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
procedure tbSendClick(Sender: TObject);
procedure tbCloseClick(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure ClientSocketConnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure ClientSocketConnecting(Sender: TObject;
Socket: TCustomWinSocket);
procedure ClientSocketDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure ClientSocketError(Sender: TObject; Socket: TCustomWinSocket;
ErrorEvent: TErrorEvent; var ErrorCode: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
var
frmClientMain: TfrmClientMain;
implementation
{$R *.DFM}
//打开套接字连接
procedure TfrmClientMain.tbConnectedClick(Sender: TObject);
begin
ClientSocket.Open ;
end;
//关闭套接字连接
procedure TfrmClientMain.tbDisconnectedClick(Sender: TObject);
begin
ClientSocket.Close;
end;
//接受服务器端的回复
procedure TfrmClientMain.ClientSocketRead(Sender: TObject;
Socket: TCustomWinSocket);
begin
Memo1.Lines.Add(Socket.ReceiveText);
end;
//发送信息到服务器端
procedure TfrmClientMain.tbSendClick(Sender: TObject);
begin
ClientSocket.Socket.SendText(Edit1.Text);
end;
procedure TfrmClientMain.tbCloseClick(Sender: TObject);
begin
Close;
end;
//设置要连接的服务器端地址
procedure TfrmClientMain.FormShow(Sender: TObject);
begin
ClientSocket.Host := SocketHost;
end;
//已经连接到服务器端
procedure TfrmClientMain.ClientSocketConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
tbSend.Enabled := True;
tbDisconnected.Enabled :=True;
btnSend.Enabled := True;
StatusBar.Panels[0].Text := '已经连接到 '+ Socket.RemoteHost ;
end;
//正在连接到服务器端
procedure TfrmClientMain.ClientSocketConnecting(Sender: TObject;
Socket: TCustomWinSocket);
begin
StatusBar.Panels[0].Text := '正在连接到服务器... ' ;
end;
//当断开与服务器端的连接时发生
procedure TfrmClientMain.ClientSocketDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
tbSend.Enabled := False;
btnSend.Enabled := False;
tbDisconnected.Enabled := False;
StatusBar.Panels[0].Text := '已经断开与 '+ Socket.RemoteHost +' 的连接';
end;
procedure TfrmClientMain.FormClose(Sender: TObject;
var Action: TCloseAction);
begin
ClientSocket.Close ;
end;
//当与服务器端的连接发生错误时
procedure TfrmClientMain.ClientSocketError(Sender: TObject;
Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;
var ErrorCode: Integer);
begin
StatusBar.Panels[0].Text := '与服务器端的连接发生错误';
ErrorCode := 0;
end;
end.
小结
上述方法是比较简单的实现方法,同时也是相对较容易理解的方法。通过这个方法,笔者成功实现了局域网内多个客户端与服务器端进行Socket通信的功能,同时可以保证一个客户端的连接、通信或是断开都不影响其它客户端的正常通信。
附录:
服务器端窗体和客户端窗体及组件的属性设置参加相应的DFM文件。
uServerMain.pas对应的DFM文件(uServerMain.dfm)
object frmServerMain: TfrmServerMain
Left = 297
Top = 258
BorderIcons = [biSystemMenu, biMinimize]
BorderStyle = bsSingle
Caption = 'ServerSocket'
ClientHeight = 279
ClientWidth = 476
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
OnClose = FormClose
OnCreate = FormCreate
PixelsPerInch = 96
TextHeight = 13
object ControlBar1: TControlBar
Left = 0
Top = 0
Width = 476
Height = 30
Align = alTop
AutoSize = True
TabOrder = 0
object ToolBar1: TToolBar
Left = 11
Top = 2
Width = 459
Height = 22
ButtonHeight = 21
ButtonWidth = 55
Caption = 'ToolBar1'
EdgeInner = esNone
EdgeOuter = esNone
Flat = True
ShowCaptions = True
TabOrder = 0
object tbConnect: TToolButton
Left = 0
Top = 0
Caption = ' 连接 '
ImageIndex = 0
OnClick = tbConnectClick
end
object tbDisconnected: TToolButton
Left = 55
Top = 0
Caption = '断开'
ImageIndex = 4
OnClick = tbDisconnectedClick
end
object tbClose: TToolButton
Left = 110
Top = 0
Caption = '关闭'
ImageIndex = 3
OnClick = tbCloseClick
end
end
end
object Edit1: TEdit
Left = 0
Top = 232
Width = 473
Height = 21
TabOrder = 1
Text = '你好!'
end
object Memo1: TMemo
Left = 0
Top = 30
Width = 476
Height = 195
Align = alTop
TabOrder = 2
end
object StatusBar: TStatusBar
Left = 0
Top = 257
Width = 476
Height = 22
Panels = <
item
Width = 50
end>
SimplePanel = False
end
object ServerSocket: TServerSocket
Active = False
Port = 1025
ServerType = stNonBlocking
OnListen = ServerSocketListen
OnGetSocket = ServerSocketGetSocket
OnClientConnect = ServerSocketClientConnect
OnClientDisconnect = ServerSocketClientDisconnect
OnClientRead = ServerSocketClientRead
OnClientError = ServerSocketClientError
Left = 368
end
end
uClientMain.pas对应的DFM文件(uClientMain.dfm)
object frmClientMain: TfrmClientMain
Left = 361
Top = 290
BorderIcons = [biSystemMenu, biMinimize]
BorderStyle = bsSingle
Caption = 'ClientSocket'
ClientHeight = 230
ClientWidth = 402
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
Position = poScreenCenter
OnClose = FormClose
OnShow = FormShow
PixelsPerInch = 96
TextHeight = 13
object ControlBar1: TControlBar
Left = 0
Top = 0
Width = 402
Height = 30
Align = alTop
AutoSize = True
TabOrder = 0
object ToolBar1: TToolBar
Left = 11
Top = 2
Width = 385
Height = 22
ButtonHeight = 21
ButtonWidth = 55
Caption = 'ToolBar1'
EdgeInner = esNone
EdgeOuter = esNone
Flat = True
ShowCaptions = True
TabOrder = 0
object tbConnected: TToolButton
Left = 0
Top = 0
Caption = ' 连接 '
ImageIndex = 0
OnClick = tbConnectedClick
end
object tbSend: TToolButton
Left = 55
Top = 0
Caption = '发送'
Enabled = False
ImageIndex = 1
OnClick = tbSendClick
end
object tbDisconnected: TToolButton
Left = 110
Top = 0
Caption = '断开'
Enabled = False
ImageIndex = 3
OnClick = tbDisconnectedClick
end
object tbClose: TToolButton
Left = 165
Top = 0
Caption = '退出'
ImageIndex = 2
OnClick = tbCloseClick
end
end
end
object Edit1: TEdit
Left = 0
Top = 184
Width = 321
Height = 21
TabOrder = 1
Text = '问候'
end
object Memo1: TMemo
Left = 0
Top = 30
Width = 402
Height = 147
Align = alTop
TabOrder = 2
end
object StatusBar: TStatusBar
Left = 0
Top = 208
Width = 402
Height = 22
Panels = <
item
Width = 50
end>
SimplePanel = False
end
object btnSend: TBitBtn
Left = 336
Top = 183
Width = 60
Height = 22
Caption = '发送'
Enabled = False
TabOrder = 4
OnClick = tbSendClick
end
object ClientSocket: TClientSocket
Active = False
ClientType = ctNonBlocking
Port = 1025
OnConnecting = ClientSocketConnecting
OnConnect = ClientSocketConnect
OnDisconnect = ClientSocketDisconnect
OnRead = ClientSocketRead
OnError = ClientSocketError
Left = 320
end
end
标题:<<Delphi6函数大全-SysUtils.pas>>
格式:
text大小:92671
制作:Zswang
日期:2002-01-25
//*)
首部 function Languages: TLanguages; $[SysUtils.pas
功能 返回系统语言对象
说明 通过此函数可以得到系统的语言环境
参考 type SysUtils.TLanguages
例子
///////Begin Languages
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
Memo1.Clear;
for I := 0 to Languages.Count - 1 do
Memo1.Lines.Add(Languages.Name[I]);
end;
///////End Languages
━━━━━━━━━━━━━━━━━━━━━
首部 function AllocMem(Size: Cardinal): Pointer; $[SysUtils.pas
功能 返回一个指定大小Size的内存块
说明 配合用FreeMem释放资源
参考 function System.GetMem
例子
///////Begin AllocMem
procedure TForm1.Button1Click(Sender: TObject);
var
I: PInteger;
begin
I := AllocMem(SizeOf(Integer));
I^ := 100;
Edit1.Text := IntToStr(I^);
FreeMem(I, SizeOf(Integer));
end;
///////End AllocMem
━━━━━━━━━━━━━━━━━━━━━
首部 procedure AddExitProc(Proc: TProcedure); $[SysUtils.pas
功能 添加一个退出处理的过程
说明 建议用finalization部分取代
参考 <NULL>
例子
////////Begin AddExitProc
uses
ShellApi;
procedure ExitProc;
begin
ShellExecute(0, 'Open', 'Calc.exe', nil, nil, SW_SHOW);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
AddExitProc(ExitProc);
end;
////////End AddExitProc
━━━━━━━━━━━━━━━━━━━━━
首部 function NewStr(const S: string): PString; deprecated; $[SysUtils.pas
功能 返回一个新的字符串指针地址
说明 字符串S为空时返回NullStr
参考 procedure System.New
例子
////////Begin NewStr,DisposeStr
procedure TForm1.Button1Click(Sender: TObject);
var
P: PString;
begin
P := NewStr(Edit1.Text);
Edit2.Text := P^;
DisposeStr(P);
end;
////////End NewStr,DisposeStr
━━━━━━━━━━━━━━━━━━━━━
首部 procedure DisposeStr(P: PString); deprecated; $[SysUtils.pas
功能 释放字符串指针P资源
说明 配合函数NewStr使用
参考 procedure System.Dispose
例子 <如上参见,如下参见>
━━━━━━━━━━━━━━━━━━━━━
首部 procedure AssignStr(var P: PString; const S: string); deprecated; $[SysUtils.pas
功能 将字符串S更新给字符串指针P
说明 更新值时会释放以前字符串指针的资源
参考 function SysUtils.NewStr;function SysUtils.DisposeStr
例子
////////Begin AssignStr
procedure TForm1.Button1Click(Sender: TObject);
var
P: PString;
begin
P := nil;
AssignStr(P, Edit1.Text);
Edit2.Text := P^;
DisposeStr(P);
end;
////////End AssignStr
━━━━━━━━━━━━━━━━━━━━━
首部 procedure AppendStr(var Dest: string; const S: string); deprecated; $[SysUtils.pas
功能 在字符串Dest后追加字符串S
说明 相当于Dest := Dest + S;Delphi6已经不建议使用
参考 <NULL>
例子
////////Begin AppendStr
procedure TForm1.Button1Click(Sender: TObject);
var
S: string;
begin
S := Edit2.Text;
AppendStr(S, Edit1.Text);
Edit2.Text := S;
end;
////////End AppendStr
━━━━━━━━━━━━━━━━━━━━━
首部 function UpperCase(const S: string): string; $[SysUtils.pas
功能 返回字符串S的大写形式
说明 非小写字符不处理
参考 procedure System.SetLength
例子 Edit2.Text := UpperCase(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function LowerCase(const S: string): string; $[SysUtils.pas
功能 返回字符串S的小写形式
说明 非大写字符不处理
参考 procedure System.SetLength
例子 Edit2.Text := LowerCase(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function CompareStr(const S1, S2: string): Integer; $[SysUtils.pas
功能 返回比较两个字符
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;区分大小写
参考 <NULL>
例子 SpinEdit1.Value := CompareStr(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function CompareMem(P1, P2: Pointer; Length: Integer): Boolean; assembler; $[SysUtils.pas
功能 返回比较两个内存指针
说明 CompareMem(PChar('12a'), PChar('12c'), 2)=True;CompareMem(PChar('12a'), PChar('12c'), 3)=False
参考 <NULL>
例子 CheckBox1.Checked := CompareMem(Self, Form1, 8);
━━━━━━━━━━━━━━━━━━━━━
首部 function CompareText(const S1, S2: string): Integer; $[SysUtils.pas
功能 返回比较两个字符串
说明 不区分大小写
参考 <NULL>
例子 SpinEdit1.Value := CompareText(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function SameText(const S1, S2: string): Boolean; $[SysUtils.pas
功能 返回两个字符串是否相等
说明 不区分大小写
参考 <NULL>
例子 CheckBox1.Checked := SameText(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiUpperCase(const S: string): string; $[SysUtils.pas
功能 返回字符串S的大写形式
说明 ANSI(American National Standards Institute)美国国家标准协会;非小写的字符不变
参考 function Windows.CharUpperBuff
例子 Edit2.Text := AnsiUpperCase(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiLowerCase(const S: string): string; $[SysUtils.pas
功能 返回字符串S的小写形式
说明 非大写字符不处理
参考 function Windows.CharLowerBuff
例子 Edit2.Text := AnsiLowerCase(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiCompareStr(const S1, S2: string): Integer; $[SysUtils.pas
功能 反回比较两个字符串
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;区分大小写
参考 function Windows.CompareString
例子 SpinEdit1.Value := AnsiCompareStr(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiSameStr(const S1, S2: string): Boolean; $[SysUtils.pas
功能 返回两个字符串是否相等
说明 区分大小写
参考 function SysUtils.AnsiCompareStr
例子 CheckBox1.Checked := AnsiSameStr(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiCompareText(const S1, S2: string): Integer; $[SysUtils.pas
功能 反回比较两个字符串
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;不区分大小写
参考 function Windows.CompareString
例子 SpinEdit1.Value := AnsiCompareText(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiSameText(const S1, S2: string): Boolean; $[SysUtils.pas
功能 返回两个字符串是否相等
说明 不区分大小写
参考 function SysUtils.AnsiCompareText
例子 CheckBox1.Checked := AnsiSameText(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiStrComp(S1, S2: PChar): Integer; $[SysUtils.pas
功能 返回比较两个指针字符串
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;区分大小写
参考 function System.CompareString
例子 SpinEdit1.Value := AnsiStrComp(PChar(Edit1.Text), PChar(Edit2.Text))
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiStrIComp(S1, S2: PChar): Integer; $[SysUtils.pas
功能 返回比较两个指针字符串
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;不区分大小写;Ignore(忽略)
参考 function Windows.CompareString
例子 SpinEdit1.Value := AnsiStrIComp(PChar(Edit1.Text), PChar(Edit2.Text))
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiStrLComp(S1, S2: PChar; MaxLen: Cardinal): Integer; $[SysUtils.pas
功能 返回比较两个指针字符串指定长度
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;区分大小写;Length(长度)
参考 function Windows.CompareString
例子 SpinEdit1.Value := AnsiStrLComp(PChar(Edit1.Text), PChar(Edit2.Text), SpinEdit2.Value)
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiStrLIComp(S1, S2: PChar; MaxLen: Cardinal): Integer; $[SysUtils.pas
功能 返回比较两个指针字符串指定长度
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;不区分大小写
参考 function Windows.CompareString
例子 SpinEdit1.Value := AnsiStrLIComp(PChar(Edit1.Text), PChar(Edit2.Text), SpinEdit2.Value)
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiStrLower(Str: PChar): PChar; $[SysUtils.pas
功能 返回指针字符串小写形式
说明 非大写字符不处理
参考 function Windows.CharLower
例子 Edit2.Text := AnsiStrLower(PChar(Edit1.Text));
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiStrUpper(Str: PChar): PChar; $[SysUtils.pas
功能 返回指针字符串大写形式
说明 非小写字符不处理
参考 function Windows.CharUpper
例子 Edit2.Text := AnsiStrUpper(PChar(Edit1.Text));
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiLastChar(const S: string): PChar; $[SysUtils.pas
功能 返回字符串S的最后一个指针字符
说明 当字符串S为空串则返回空指针
参考 function SysUtils.ByteType
例子 Edit2.Text := AnsiLastChar(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiStrLastChar(P: PChar): PChar; $[SysUtils.pas
功能 返回指针字符串P的最后一个指针字符
说明 当字符串P为空空指针则返回空指针
参考 function SysUtils.ByteType
例子 Edit2.Text := AnsiLastChar(PChar(Edit1.Text));
━━━━━━━━━━━━━━━━━━━━━
首部 function WideUpperCase(const S: WideString): WideString; $[SysUtils.pas
功能 返回双字节字符串的大写形式
说明 WideChar双字节字符
参考 function Windows.CharUpperBuffW
例子 Edit2.Text := WideUpperCase(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function WideLowerCase(const S: WideString): WideString; $[SysUtils.pas
功能 返回双字节字符串的小写形式
说明 我怎么就测试不出来呢
参考 function Windows.CharLowerBuffW
例子 Edit2.Text := WideLowerCase(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function WideCompareStr(const S1, S2: WideString): Integer; $[SysUtils.pas
功能 返回比较两个双字节字符串
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;区分大小写
参考 function Windows.CompareStringW
例子 SpinEdit1.Value := WideCompareStr(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function WideSameStr(const S1, S2: WideString): Boolean; $[SysUtils.pas
功能 返回两个双字节字符串是否相同
说明 区分大小写
参考 function SysUtils.WideCompareStr
例子 CheckBox1.Checked := WideSameStr(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function WideCompareText(const S1, S2: WideString): Integer; $[SysUtils.pas
功能 返回比较两个双字节字符串
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;不区分大小写
参考 function Windows.CompareStringW
例子 SpinEdit1.Value := WideCompareText(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function WideSameText(const S1, S2: WideString): Boolean; $[SysUtils.pas
功能 返回两个双字节字符串是否相同
说明 不区分大小写
参考 function SysUtils.WideCompareText
例子 CheckBox1.Checked := WideSameText(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function Trim(const S: string): string; overload; $[SysUtils.pas
首部 function Trim(const S: WideString): WideString; overload; $[SysUtils.pas
功能 返回除去字符串S左右不可见字符
说明 小于#32的字符看作不可见字符
参考 function System.Copy
例子 Edit2.Text := Trim(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function TrimLeft(const S: string): string; overload; $[SysUtils.pas
首部 function TrimLeft(const S: WideString): WideString; overload; $[SysUtils.pas
功能 返回除去字符串S左边不可见字符
说明 小于#32的字符看作不可见字符
参考 function System.Copy
例子 Edit2.Text := TrimLeft(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function TrimRight(const S: string): string; overload; $[SysUtils.pas
首部 function TrimRight(const S: WideString): WideString; overload; $[SysUtils.pas
功能 返回除去字符串S右边不可见字符
说明 小于#32的字符看作不可见字符
参考 function System.Copy
例子 Edit2.Text := TrimRight(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function QuotedStr(const S: string): string; $[SysUtils.pas
功能 返回字符串S在pascal中的表现形式
说明 单引号中的一个单引号将转成两个
参考 procedure System.Insert
例子 Edit2.Text := QuotedStr(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiQuotedStr(const S: string; Quote: Char): string; $[SysUtils.pas
功能 返回字符串S以字符Quote为引号的表现形式
说明 AnsiQuotedStr('hello"world', '@')='@hello"world@';AnsiQuotedStr('hello"world', '"')='"hello""world"'
参考 function SysUtils.AnsiStrScan
例子 Edit2.Text := AnsiQuotedStr(Edit1.Text, '"');
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiExtractQuotedStr(var Src: PChar; Quote: Char): string; $[SysUtils.pas
功能 返回以字符Quote为引号的表现形式原形
说明 表现形式非法时Src不变否则为空
参考 function SysUtils.AnsiStrScan
例子
///////Begin AnsiExtractQuotedStr
procedure TForm1.Button1Click(Sender: TObject);
var
P: PChar;
begin
P := PChar(Edit1.Text);
Edit2.Text := AnsiExtractQuotedStr(P, '"');
Edit3.Text := P;
end;
///////End AnsiExtractQuotedStr
━━━━━━━━━━━━━━━━━━━━━
首部 function AnsiDequotedStr(const S: string; AQuote: Char): string; $[SysUtils.pas
功能 返回以字符AQuote为引号的表现形式原形
说明 表现形式非法时则返回S
参考 function SysUtils.AnsiExtractQuotedStr
例子 Edit2.Text := AnsiDequotedStr(Edit1.Text, '"');
━━━━━━━━━━━━━━━━━━━━━
首部 function AdjustLineBreaks(const S: string; Style: TTextLineBreakStyle = {$IFDEF LINUX} tlbsLF {$ENDIF} {$IFDEF MSWINDOWS} tlbsCRLF {$ENDIF}): string; $[SysUtils.pas
功能 返回将给定字符串的行分隔符调整为CR/LF序列
说明 AdjustLineBreaks('1'#13'2'#13)='1'#13#10'2'#13#10;AdjustLineBreaks('1'#10'2'#10)='1'#13#10'2'#13#10
参考 function SysUtils.StrNextChar
例子 <NULL>
━━━━━━━━━━━━━━━━━━━━━
首部 function IsValidIdent(const Ident: string): Boolean; $[SysUtils.pas
功能 返回字符串Ident是否是正确的标识符
说明 标识符::字母|下划线[字母|下划线|数字]...
参考 <NULL>
例子 CheckBox1.Checked := IsValidIdent(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function IntToStr(Value: Integer): string; overload; $[SysUtils.pas
首部 function IntToStr(Value: Int64): string; overload; $[SysUtils.pas
功能 返回整数Value转换成字符串
说明 Format('%d', [Value])
参考 function SysUtils.FmtStr
例子 Edit2.Text := IntToStr(SpinEdit1.Value);
━━━━━━━━━━━━━━━━━━━━━
首部 function IntToHex(Value: Integer; Digits: Integer): string; overload; $[SysUtils.pas
首部 function IntToHex(Value: Int64; Digits: Integer): string; overload; $[SysUtils.pas
功能 返回整数Value转换成十六进制表现结果;Format('%.*x', [Digits, Value])
说明 参数Digits指定字符最小宽度;最小宽度不足时将用0填充
参考 function SysUtils.FmtStr
例子 Edit2.Text := IntToHex(SpinEdit1.Value, SpinEdit2.Value);
━━━━━━━━━━━━━━━━━━━━━
首部 function StrToInt(const S: string): Integer; $[SysUtils.pas
功能 返回字符串S转换成整数
说明 字符串非整数表达时将引起异常
参考 procedure System.Val
例子 SpinEdit1.Value := StrToInt(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function StrToIntDef(const S: string; Default: Integer): Integer; $[SysUtils.pas
功能 返回字符串S转换成整数
说明 字符串非整数表达时则返回默认值Default
参考 procedure System.Val
例子 SpinEdit1.Value := StrToIntDef(Edit1.Text, 0);
━━━━━━━━━━━━━━━━━━━━━
首部 function TryStrToInt(const S: string; out Value: Integer): Boolean; $[SysUtils.pas
功能 返回字符串S转换成整数Value是否成功
说明 字符串非整数表达时返回False并且Value将输出为0
参考 procedure System.Val
例子
///////Begin TryStrToInt
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
CheckBox1.Checked := TryStrToInt(Edit1.Text, I);
SpinEdit1.Value := I;
end;
///////End TryStrToInt
━━━━━━━━━━━━━━━━━━━━━
首部 function StrToInt64(const S: string): Int64; $[SysUtils.pas
功能 返回字符串S转换成六十四位整数
说明 字符串非六十四位整数表达时将引起异常
参考 procedure System.Val
例子 SpinEdit1.Value := StrToInt64(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function StrToInt64Def(const S: string; const Default: Int64): Int64; $[SysUtils.pas
功能 返回字符串S转换成六十四位整数
说明 字符串非六十四位整数表达时则返回默认值Default
参考 procedure System.Val
例子 SpinEdit1.Value := StrToInt64Def(Edit1.Text, 0);
━━━━━━━━━━━━━━━━━━━━━
首部 function TryStrToInt64(const S: string; out Value: Int64): Boolean; $[SysUtils.pas
功能 返回字符串S转换成六十四位整数Value是否成功
说明 字符串非六十四位整数表达时返回False并且Value将输出为0
参考 procedure System.Val
例子
///////Begin TryStrToInt64
procedure TForm1.Button1Click(Sender: TObject);
var
I: Int64;
begin
CheckBox1.Checked := TryStrToInt64(Edit1.Text, I);
SpinEdit1.Value := I;
end;
///////End TryStrToInt64
━━━━━━━━━━━━━━━━━━━━━
首部 function StrToBool(const S: string): Boolean; $[SysUtils.pas
功能 返回字符串S转换成逻辑值
说明 字符非逻辑表达时将引起异常
参考 function SysUtils.TryStrToBool
例子 CheckBox1.Checked := StrToBool(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function StrToBoolDef(const S: string; const Default: Boolean): Boolean; $[SysUtils.pas
功能 返回字符串S转换成逻辑值
说明 字符非逻辑表达时则返回默认值Default
参考 function SysUtils.TryStrToBool
例子 CheckBox1.Checked := StrToBoolDef(Edit1.Text, False);
━━━━━━━━━━━━━━━━━━━━━
首部 function TryStrToBool(const S: string; out Value: Boolean): Boolean; $[SysUtils.pas
功能 返回字符串S转换成逻辑值Value是否成功
说明 [注意]0为假非0为真;不是'True'和'False';Delphi6 Bug 如下修正
参考 function SysUtils.AnsiSameText;var SysUtils.TrueBoolStrs;var SysUtils.FalseBoolStrs
例子
///////Begin TryStrToBool
procedure TForm1.Button1Click(Sender: TObject);
var
B: Boolean;
begin
SetLength(TrueBoolStrs, 2);
SetLength(FalseBoolStrs, 2);
TrueBoolStrs[0] := 'True';
FalseBoolStrs[0] := 'False';
TrueBoolStrs[1] := 'Yes';
FalseBoolStrs[1] := 'No';
CheckBox1.Checked := TryStrToBool(Edit1.Text, B);
CheckBox2.Checked := B;
end;
///////End TryStrToBool
附加
///////Begin TryStrToBool
function TryStrToBool(const S: string; out Value: Boolean): Boolean;
function CompareWith(const aArray: array of string): Boolean;
var
I: Integer;
begin
Result := False;
for I := Low(aArray) to High(aArray) do
if AnsiSameText(S, aArray[I]) then
begin
Result := True;
Break;
end;
end;
var
LResult: Extended;
begin
Result := TryStrToFloat(S, LResult);
if Result then
Value := LResult <> 0
else
begin
Result := True; //修正处
VerifyBoolStrArray;
if CompareWith(TrueBoolStrs) then
Value := True
else if CompareWith(FalseBoolStrs) then
Value := False
else
Result := False;
end;
end;
///////End TryStrToBool
━━━━━━━━━━━━━━━━━━━━━
首部 function BoolToStr(B: Boolean; UseBoolStrs: Boolean = False): string; $[SysUtils.pas
功能 返回逻辑值B转换成字符串
说明 BoolToStr(False, False)='0';BoolToStr(False, True)='-1'
参考 var SysUtils.TrueBoolStrs;var SysUtils.FalseBoolStrs
例子 Edit1.Text := BoolToStr(CheckBox1.Checked, CheckBox2.Checked);
━━━━━━━━━━━━━━━━━━━━━
首部 function LoadStr(Ident: Integer): string; $[SysUtils.pas
功能 返回根据标识Ident的字符串资源
说明 字符串资源是指程序的内部资源
参考 function SysUtils.FindStringResource
例子 Edit2.Text := LoadStr(StrToIntDef(Edit1.Text, 0));
━━━━━━━━━━━━━━━━━━━━━
首部 function FmtLoadStr(Ident: Integer; const Args: array of const): string; $[SysUtils.pas
功能 返回格式化的字符串资源
说明 字符串资源是指程序的内部资源
参考 function SysUtils.FmtStr;function SysUtils.FindStringResource
例子 <NULL>;
━━━━━━━━━━━━━━━━━━━━━
首部 function FileOpen(const FileName: string; Mode: LongWord): Integer; $[SysUtils.pas
功能 返回打开文件果
说明 Mode指定打开文件的模式(fmOpenRead,fmOpenWrite,fmOpenReadWrite....);打开失败则返回负数
参考 function Windows.CreateFile
例子
///////Begin FileOpen,FileClose
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
I := FileOpen(Edit1.Text, fmOpenRead);
CheckBox1.Checked := I > 0;
FileClose(I);
end;
///////Begin FileOpen,FileClose
━━━━━━━━━━━━━━━━━━━━━
首部 function FileCreate(const FileName: string): Integer; overload; $[SysUtils.pas
首部 function FileCreate(const FileName: string; Rights: Integer): Integer; overload; $[SysUtils.pas
功能 返回创建文件
说明 创建失败则返回负数
参考 function Windows.CreateFile
例子
///////Begin FileCreate
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
I := FileCreate(Edit1.Text);
CheckBox1.Checked := I > 0;
FileClose(I);
end;
///////End FileCreate
━━━━━━━━━━━━━━━━━━━━━
首部 function FileRead(Handle: Integer; var Buffer; Count: LongWord): Integer; $[SysUtils.pas
功能 返回读取文件缓冲区的大小
说明 读取失败则返回负数
参考 function Windows.ReadFile
例子 <参见FileOpen>
━━━━━━━━━━━━━━━━━━━━━
首部 function FileWrite(Handle: Integer; const Buffer; Count: LongWord): Integer; $[SysUtils.pas
功能 返回写入文件缓冲区的大小
说明 写入失败则返回负数
参考 function Windows.WriteFile
例子 <参见FileCreate>
━━━━━━━━━━━━━━━━━━━━━
首部 function FileSeek(Handle, Offset, Origin: Integer): Integer; overload; $[SysUtils.pas
首部 function FileSeek(Handle: Integer; const Offset: Int64; Origin: Integer): Int64; overload; $[SysUtils.pas
功能 返回指定文件偏移量
说明 Offset指定偏移量;Origin指定原点(Origin为0时指文件首;为1时指当前位置;为2时指文件尾)
参考 function Windows.SetFilePointer
例子 <参见FileOpen>
━━━━━━━━━━━━━━━━━━━━━
首部 procedure FileClose(Handle: Integer); $[SysUtils.pas
功能 返回关闭文件
说明 不关闭打开的文件会占用系统资源
参考 function Windows.CloseHandle
例子 <参见FileOpen>
━━━━━━━━━━━━━━━━━━━━━
首部 function FileAge(const FileName: string): Integer; $[SysUtils.pas
功能 返回文件创建的时间
说明 文件不存在则返回-1
参考 function Windows.FindFirstFile
例子
///////Begin FileAge,DateTimeToStr,FileDateToDateTime
procedure TForm1.Button1Click(Sender: TObject);
begin
SpinEdit1.Value := FileAge(Edit1.Text);
if SpinEdit1.Value > 0 then
Edit2.Text := DateTimeToStr(FileDateToDateTime(SpinEdit1.Value));
end;
///////End FileAge,DateTimeToStr,FileDateToDateTime
━━━━━━━━━━━━━━━━━━━━━
首部 function FileExists(const FileName: string): Boolean; $[SysUtils.pas
功能 返回文件名FileName是否有实体存在
说明 包括隐藏文件
参考 function SysUtils.FileAge
例子 CheckBox1.Checked := FileExists(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function DirectoryExists(const Directory: string): Boolean; $[SysUtils.pas
功能 返回目录名FileName是否有实体存在
说明 包括隐藏目录
参考 function Windows.GetFileAttributes
例子 CheckBox1.Checked := DirectoryExists(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ForceDirectories(Dir: string): Boolean; $[SysUtils.pas
功能 返回创建子目录是否成功
说明 直接创建多级目录
参考 function SysUtils.CreateDir
例子 CheckBox1.Checked := ForceDirectories(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function FindFirst(const Path: string; Attr: Integer; var F: TSearchRec): Integer; $[SysUtils.pas
功能 返回设置文件搜索
说明 搜索成功则返回0
参考 function Windows.FindFirstFile
例子
///////Begin FindFirst,FindNext,FindClose
procedure TForm1.Button1Click(Sender: TObject);
var
vSearchRec: TSearchRec;
I: Integer;
begin
Memo1.Clear;
I := FindFirst(Edit1.Text, faAnyFile, vSearchRec);
while I = 0 do begin
Memo1.Lines.Add(vSearchRec.Name);
I := FindNext(vSearchRec);
end;
FindClose(vSearchRec);
end;
///////End FindFirst,FindNext,FindClose
━━━━━━━━━━━━━━━━━━━━━
首部 function FindNext(var F: TSearchRec): Integer; $[SysUtils.pas
功能 返回继续文件搜索
说明 搜索成功则返回0
参考 function Windows.FindNextFile
例子 <参见FindFirst>
━━━━━━━━━━━━━━━━━━━━━
首部 procedure FindClose(var F: TSearchRec); $[SysUtils.pas
功能 结束当前文件搜索
说明 不关闭查询会占用系统资源
参考 function Windows.FindClose
例子 <参见FindFirst>
━━━━━━━━━━━━━━━━━━━━━
首部 function FileGetDate(Handle: Integer): Integer; $[SysUtils.pas
功能 返回文件的修改时间
说明 读取失败则返回-1
参考 function Windows.GetFileTime
例子
///////Begin FileGetDate
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
begin
I := FileOpen(Edit1.Text, fmOpenRead);
if I < 0 then Exit;
SpinEdit1.Value := FileGetDate(I);
Edit2.Text := DateTimeToStr(FileDateToDateTime(SpinEdit1.Value));
FileClose(I);
end;
///////End FileGetDate
━━━━━━━━━━━━━━━━━━━━━
首部 function FileSetDate(const FileName: string; Age: Integer): Integer; overload; $[SysUtils.pas
首部 function FileSetDate(Handle: Integer; Age: Integer): Integer; overload; platform; $[SysUtils.pas
功能 返回设置文件的修改时间
说明 修改成功则返回0
参考 function Windows.SetFileTime
例子 SpinEdit1.Value := FileSetDate(Edit1.Text, DateTimeToFileDate(StrToDateTime(Edit2.Text)));
━━━━━━━━━━━━━━━━━━━━━
首部 function FileGetAttr(const FileName: string): Integer; platform; $[SysUtils.pas
功能 返回文件的属性
说明 读取失败则返回$FFFFFFFF
参考 function Windows.GetFileAttributes
例子 SpinEdit1.Value := FileGetAttr(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function FileSetAttr(const FileName: string; Attr: Integer): Integer; platform; $[SysUtils.pas
功能 返回设置文件的属性
说明 设置成功则返回0
参考 function Windows.SetFileAttributes
例子 SpinEdit1.Value := FileSetAttr(Edit1.Text, SpinEdit2.Value);
━━━━━━━━━━━━━━━━━━━━━
首部 function FileIsReadOnly(const FileName: string): Boolean; $[SysUtils.pas
功能 返回文件是否只读
说明 文件不存在看作只读
参考 function Windows.GetFileAttributes
例子 CheckBox1.Checked := FileIsReadOnly(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function FileSetReadOnly(const FileName: string; ReadOnly: Boolean): Boolean; $[SysUtils.pas
功能 返回设置文件是否只读是否成功
说明 文件不存在则返回False
参考 function Windows.GetFileAttributes;function Windows.SetFileAttributes
例子 CheckBox1.Checked := FileSetReadOnly(Edit1.Text, CheckBox2.Checked);
━━━━━━━━━━━━━━━━━━━━━
首部 function DeleteFile(const FileName: string): Boolean; $[SysUtils.pas
功能 返回删除文件是否成功
说明 文件不存在则返回False
参考 function Windows.DeleteFile
例子 CheckBox1.Checked := DeleteFile(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function RenameFile(const OldName, NewName: string): Boolean; $[SysUtils.pas
功能 返回重命名文件是否成功
说明 文件不存在则返回False
参考 function Windows.MoveFile
例子 CheckBox1.Checked := RenameFile(Edit1.Text, Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ChangeFileExt(const FileName, Extension: string): string; $[SysUtils.pas
功能 返回改变扩展名后的文件名
说明 [注意]扩展名Extension前要加点;ChangeFileExt('a.jpg', 'bmp')='abmp'
参考 function SysUtils.LastDelimiter;function System.Copy
例子 Edit1.Text := ChangeFileExt(Edit2.Text, Edit3.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ExtractFilePath(const FileName: string): string; $[SysUtils.pas
功能 返回文件名所在的路径
说明 ExtractFilePath('C:\')='C:\';ExtractFilePath('\\Server\Tool\Calc.exe')='\\Server\Tool\'
参考 function SysUtils.LastDelimiter;function System.Copy
例子 Edit1.Text := ExtractFilePath(Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ExtractFileDir(const FileName: string): string; $[SysUtils.pas
功能 返回文件名所在的目录
说明 ExtractFileDir('C:\')='C:\';ExtractFileDir('\\Server\Tool\Calc.exe')='\\Server\Tool'
参考 function SysUtils.LastDelimiter;function System.Copy
例子 Edit1.Text := ExtractFileDir(Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ExtractFileDrive(const FileName: string): string; $[SysUtils.pas
功能 返回文件名所在驱动器
说明 ExtractFileDrive('C:\')='C:';ExtractFileDrive('\\Server\Tool\Calc.exe')='\\Server\Tool'
参考 function System.Copy
例子 Edit1.Text := ExtractFileDrive(Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ExtractFileName(const FileName: string): string; $[SysUtils.pas
功能 返回绝对文件名
说明 ExtractFileName('C:\')='';ExtractFileName('\\Server\Tool\Calc.exe')='Calc.exe'
参考 function SysUtils.LastDelimiter;function System.Copy
例子 Edit1.Text := ExtractFileName(Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ExtractFileExt(const FileName: string): string; $[SysUtils.pas
功能 返回文件名的扩展名
说明 ExtractFileExt('C:\')='';ExtractFileExt('\\Server\Tool\Calc.exe')='.exe'
参考 function SysUtils.LastDelimiter;function System.Copy
例子 Edit1.Text := ExtractFileExt(Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ExpandFileName(const FileName: string): string; $[SysUtils.pas
功能 返回文件名的完整表示
说明 ExpandFileName('hello.pas')='C:\Program Files\Borland\Delphi6\Projects\hello.pas'
参考 function Windows.GetFullPathName
例子 Edit1.Text := ExpandFileName(Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ExpandFileNameCase(const FileName: string; out MatchFound: TFilenameCaseMatch): string; $[SysUtils.pas
功能 分情况返回文件名的完整表示
说明 type TFilenameCaseMatch = (mkNone, mkExactMatch, mkSingleMatch, mkAmbiguous);
参考 function Windows.GetFullPathName;function SysUtils.SameFileName;function SysUtils.FindFirst
例子
///////Begin ExpandFileNameCase
procedure TForm1.Button1Click(Sender: TObject);
var
vFilenameCaseMatch: TFilenameCaseMatch;
begin
Edit1.Text := ExpandFileNameCase(Edit2.Text, vFilenameCaseMatch);
SpinEdit1.Value := Ord(vFilenameCaseMatch);
end;
///////End ExpandFileNameCase
━━━━━━━━━━━━━━━━━━━━━
首部 function ExpandUNCFileName(const FileName: string): string; $[SysUtils.pas
功能 返回LINUX文件名的完整表示
说明 ExpandUNCFileName('C:/')='C:\'
参考 function SysUtils.ExpandFileName
例子 Edit1.Text := ExpandUNCFileName(Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ExtractRelativePath(const BaseName, DestName: string): string; $[SysUtils.pas
功能 返回参数的相对路径
说明 ExtractRelativePath('C:\Windows\', 'C:\Windows\System')='System'
参考 function SysUtils.SameFilename;function SysUtils.ExtractFileDrive
例子 Edit1.Text := ExtractRelativePath(Edit2.Text, Edit3.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function ExtractShortPathName(const FileName: string): string; $[SysUtils.pas
功能 返回参数的DOS路径
说明 ExtractShortPathName('C:\Program Files\Borland')='C:\PROGRA~1\BORLAND'
参考 function Windows.GetShortPathName
例子 Edit1.Text := ExtractShortPathName(Edit2.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function FileSearch(const Name, DirList: string): string; $[SysUtils.pas
功能 返回目录列表中DirList搜索的第一个结果
说明 FileSearch('Calc.exe', 'd:\winxp\system32;c:\windows')='d:\winxp\system32\calc.exe'
参考 function SysUtils.FileExists;function SysUtils.AnsiLastChar
例子 Edit1.Text := FileSearch(Edit2.Text, Edit3.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function DiskFree(Drive: Byte): Int64; $[SysUtils.pas
功能 返回驱动器可用空间
说明 参数Drive为0表示当前路径,为1表示=A驱,为2表示=B驱...;获取失败则返回-1
参考 function Windows.GetDiskFreeSpaceExA
例子 SpinEdit1.Value := DiskFree(SpinEdit2.Value);
━━━━━━━━━━━━━━━━━━━━━
首部 function DiskSize(Drive: Byte): Int64; $[SysUtils.pas
功能 返回驱动器全部空间
说明 参数Drive为0表示当前路径,为1表示=A驱,为2表示=B驱...;获取失败则返回-1
参考 function Windows.GetDiskFreeSpaceExA
例子 SpinEdit1.Value := DiskSize(SpinEdit2.Value);
━━━━━━━━━━━━━━━━━━━━━
首部 function FileDateToDateTime(FileDate: Integer): TDateTime; $[SysUtils.pas
功能 返回将文件日期时间类型转换日期时间类型
说明 FileDate非法是将触发异常
参考 function SysUtils.EncodeDate;function SysUtils.EncodeTime
例子 <参见FileAge>
━━━━━━━━━━━━━━━━━━━━━
首部 function DateTimeToFileDate(DateTime: TDateTime): Integer; $[SysUtils.pas
功能 返回将日期时间类型转换文件日期时间类型
说明 年份在1980到2107之外则返回0
参考 function SysUtils.DecodeDate;function SysUtils.DecodeTime
例子 <参见FileSetDate>
━━━━━━━━━━━━━━━━━━━━━
首部 function GetCurrentDir: string; $[SysUtils.pas
功能 返回当前操作目录
说明 [注意]调用文件对话框会改变当前操作目录
参考 function System.GetDir
例子 Edit1.Text := GetCurrentDir;
━━━━━━━━━━━━━━━━━━━━━
首部 function SetCurrentDir(const Dir: string): Boolean; $[SysUtils.pas
功能 返回设置当前操作目录是否成功
说明 [注意]调用文件对话框会改变当前操作目录
参考 function Windows.SetCurrentDirectory
例子 CheckBox1.Checked := SetCurrentDir(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function CreateDir(const Dir: string): Boolean; $[SysUtils.pas
功能 返回创建目录是否成功
说明 不支持多级目录;已经存在则返回False
参考 function Windows.CreateDirectory
例子 CheckBox1.Checked := CreateDir(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function RemoveDir(const Dir: string): Boolean; $[SysUtils.pas
功能 返回删除目录是否成功
说明 必须是空目录
参考 function Windows.RemoveDirectory
例子 CheckBox1.Checked := RemoveDir(Edit1.Text);
━━━━━━━━━━━━━━━━━━━━━
首部 function StrLen(const Str: PChar): Cardinal; $[SysUtils.pas
功能 返回指针字符串的长度
说明 当指针字符串Str为nil时将触发异常
参考 <NULL>
例子 SpinEdit2.Value := StrLen(PChar(Edit1.Text));
━━━━━━━━━━━━━━━━━━━━━
首部 function StrEnd(const Str: PChar): PChar; $[SysUtils.pas
功能 返回指针字符串的结尾
说明 当指针字符串Str为nil时将触发异常
参考 <NULL>
例子 Edit2.Text := StrEnd(PChar(Edit1.Text)) - SpinEdit1.Value;
━━━━━━━━━━━━━━━━━━━━━
首部 function StrMove(Dest: PChar; const Source: PChar; Count: Cardinal): PChar; $[SysUtils.pas
功能 返回将指针字符串Source指定内存数量Count复制覆盖到指针字符串Dest中
说明 Dest没有分配资源将触发异常s
参考 function System.Move
例子
///////Begin StrMove
procedure TForm1.Button1Click(Sender: TObject);
var
vBuffer: PChar;
begin
vBuffer := '0123456789';
StrMove(vBuffer, PChar(Edit1.Text), SpinEdit1.Value);
Edit2.Text := vBuffer;
end;
///////End StrMove
━━━━━━━━━━━━━━━━━━━━━
首部 function StrCopy(Dest: PChar; const Source: PChar): PChar; $[SysUtils.pas
功能 返回将指针字符串Source复制到指针字符串Dest中
说明 Dest应已经分配足够的空间非则将触发异常
参考 <NULL>
例子
///////Begin StrCopy
procedure TForm1.Button1Click(Sender: TObject);
var
vBuffer: PChar;
begin
GetMem(vBuffer, Length(Edit1.Text) + 1);
StrCopy(vBuffer, PChar(Edit1.Text));
Edit2.Text := vBuffer;
FreeMem(vBuffer);
end;
///////End StrCopy
━━━━━━━━━━━━━━━━━━━━━
首部 function StrECopy(Dest:PChar; const Source: PChar): PChar; $[SysUtils.pas
功能 返回将指针字符串Source复制到指针字符串Dest中的结尾
说明 可以连接指针字符串
参考 <NULL>
例子
///////Begin StrECopy
procedure TForm1.Button1Click(Sender: TObject);
var
vBuffer: array[0..255] of Char;
begin
StrECopy(StrECopy(vBuffer, PChar(Edit1.Text)), PChar(Edit2.Text));
Edit3.Text := vBuffer;
end;
///////End StrECopy
━━━━━━━━━━━━━━━━━━━━━
首部 function StrLCopy(Dest: PChar; const Source: PChar; MaxLen: Cardinal): PChar; $[SysUtils.pas
功能 返回将指针字符串Source指定长度MaxLen复制到指针字符串Dest中
说明 Dest应已经分配足够的空间非则将触发异常
参考 <NULL>
例子
///////Begin StrLCopy
procedure TForm1.Button1Click(Sender: TObject);
var
vBuffer: array[0..255] of Char;
begin
StrLCopy(vBuffer, PChar(Edit1.Text), SpinEdit1.Value);
Edit2.Text := vBuffer;
end;
///////End StrLCopy
━━━━━━━━━━━━━━━━━━━━━
首部 function StrPCopy(Dest: PChar; const Source: string): PChar; $[SysUtils.pas
功能 返回将指针字符串Source复制到指针字符串Dest中
说明 StrLCopy(Dest, PChar(Source), Length(Source))
参考 function SysUtils.StrLCopy
例子
///////Begin StrPCopy
procedure TForm1.Button1Click(Sender: TObject);
var
vBuffer: array[0..255] of Char;
begin
StrPCopy(vBuffer, PChar(Edit1.Text));
Edit2.Text := vBuffer;
end;
///////End StrPCopy
━━━━━━━━━━━━━━━━━━━━━
首部 function StrPLCopy(Dest: PChar; const Source: string; MaxLen: Cardinal): PChar; $[SysUtils.pas
功能 返回将字符串Source指定长度MaxLen复制到指针字符串Dest中
说明 StrLCopy(Dest, PChar(Source), MaxLen)
参考 function SysUtils.StrLCopy
例子
///////Begin StrPLCopy
procedure TForm1.Button1Click(Sender: TObject);
var
vBuffer: array[0..255] of Char;
begin
StrPLCopy(vBuffer, Edit1.Text, SpinEdit1.Value);
Edit2.Text := vBuffer;
end;
///////End StrPLCopy
━━━━━━━━━━━━━━━━━━━━━
首部 function StrCat(Dest: PChar; const Source: PChar): PChar; $[SysUtils.pas
功能 返回连接指针字符串Dest和指针字符串Source
说明 StrCopy(StrEnd(Dest), Source)
参考 function SysUntils.StrCopy
例子
///////Begin StrCat
procedure TForm1.Button1Click(Sender: TObject);
var
vBuffer: array[0..255] of Char;
begin
StrPCopy(vBuffer, Edit1.Text);
StrCat(vBuffer, PChar(Edit2.Text));
Edit3.Text := vBuffer;
end;
///////End StrCat
━━━━━━━━━━━━━━━━━━━━━
首部 function StrLCat(Dest: PChar; const Source: PChar; MaxLen: Cardinal): PChar; $[SysUtils.pas
功能 返回连接指针字符串Dest和指针字符串Source
说明 [注意]MaxLen指定连接后的最大长度不是指针字符串Source的长度
参考 <NULL>
例子
///////Begin StrLCat
procedure TForm1.Button1Click(Sender: TObject);
var
vBuffer: array[0..255] of Char;
begin
StrPCopy(vBuffer, Edit1.Text);
StrLCat(vBuffer, PChar(Edit2.Text), SpinEdit1.Value);
Edit3.Text := vBuffer;
end;
///////End StrLCat
━━━━━━━━━━━━━━━━━━━━━
首部 function StrComp(const Str1, Str2: PChar): Integer; $[SysUtils.pas
功能 返回比较两个指针字符串
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;区分大小写;[注意]返回第一个出现不同字符的差异
参考 <NULL>
例子 SpinEdit1.Value := StrComp(PChar(Edit1.Text), PChar(Edit2.Text));
━━━━━━━━━━━━━━━━━━━━━
首部 function StrIComp(const Str1, Str2: PChar): Integer; $[SysUtils.pas
功能 返回比较两个指针字符串
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;不区分大小写;[注意]返回第一个出现不同字符的差异
参考 <NULL>
例子 SpinEdit1.Value := StrIComp(PChar(Edit1.Text), PChar(Edit2.Text));
━━━━━━━━━━━━━━━━━━━━━
首部 function StrLComp(const Str1, Str2: PChar; MaxLen: Cardinal): Integer; $[SysUtils.pas
功能 返回比较两个指针字符串指定长度
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;区分大小写;Length(长度);[注意]返回第一个出现不同字符的差异
参考 <NULL>
例子 SpinEdit1.Value := StrLComp(PChar(Edit1.Text), PChar(Edit2.Text), SpinEdit2.Value)
━━━━━━━━━━━━━━━━━━━━━
首部 function StrLIComp(const Str1, Str2: PChar; MaxLen: Cardinal): Integer; $[SysUtils.pas
功能 返回比较两个指针字符串指定长度
说明 当S1>S2返回值>0;当S1<S2返回值<0;当S1=S2返回值=0;不区分大小写;[注意]返回第一个出现不同字符的差异
参考 <NULL>
例子 SpinEdit1.Value := StrLIComp(PChar(Edit1.Text), PChar(Edit2.Text), SpinEdit2.Value)
━━━━━━━━━━━━━━━━━━━━━
首部 function StrScan(const Str: PChar; Chr: Char): PChar; $[SysUtils.pas
功能 返回在指针字符串Str搜索字符Chr第一个出现的地址
说明 没有找到则返回空指针
参考 <NULL>
例子 Edit2.Text := StrScan(PChar(Edit1.Text), '*');
━━━━━━━━━━━━━━━━━━━━━
首部 function StrRScan(const Str: PChar; Chr: Char): PChar; $[SysUtils.pas
功能 返回在指针字符串Str搜索字符Chr最后一个出现的地址
说明 没有找到则返回空指针
参考 <NULL>
例子 Edit2.Text := StrRScan(PChar(Edit1.Text), '*');
━━━━━━━━━━━━━━━━━━━━━
首部 function StrPos(const Str1, Str2: PChar): PChar; $[SysUtils.pas
功能 返回指针字符串Str2在Str1中第一个出现的地址
说明 没有找到则返回空指针;StrPos('12345', '3') = '345'
参考 <NULL>
例子 Edit3.Text := StrPos(PChar(Edit1.Text), PChar(Edit2.Text));
━━━━━━━━━━━━━━━━━━━━━
首部 function StrUpper(Str: PChar): PChar; $[SysUtils.pas
功能 返回指针字符串Str大写
说明 非小写字符不处理
参考 <NULL>
例子 Edit1.Text := StrUpper(PChar(Edit2.Text));
━━━━━━━━━━━━━━━━━━━━━
首部 function StrLower(Str: PChar): PChar; $[SysUtils.pas
功能 返回指针字符串Str小写
说明 非大写字符不处理
参考 <NULL>
例子 Edit1.Text := StrLower(PChar(Edit2.Text));
━━━━━━━━━━━━━━━━━━━━━
首部 function StrPas(const Str: PChar): string; $[SysUtils.pas
功能 返回指针字符串Str转换成字符串
说明 也可以直接赋值
参考 <NULL>
例子 Edit1.Text := StrPas(PChar(Edit2.Text));
━━━━━━━━━━━━━━━━━━━━━
首部 function StrAlloc(Size: Cardinal): PChar; $[SysUtils.pas
功能 返回分配指定空间的内存资源给指针字符串
说明 空间的大小也将保存;用StrDispose才能全部释放
参考 function System.GetMem
例子
///////Begin StrAlloc
procedure TForm1.Button1Click(Sender: TObject);
var
P: PChar;
begin
P := StrAlloc(SpinEdit1.Value);
ShowMessage(IntToStr(StrLen(P)));
Dec(P, SizeOf(Cardinal));
ShowMessage(IntToStr(Cardinal(Pointer(P)^)));
Inc(P, SizeOf(Cardinal));
StrDispose(P);
end;