2005年08月29日

2005年08月26日

1,关于Unix Socket

1.1 什么是Socket?
 Socket是网络文件描述符。在基于Socket的编程技术中,用户不直接访问发送和接收包的网络接口设备,而是建立一个中间文件描述符来处理编程接口到网络的操作。

1.2 Socket包括哪些内容?
 一个特殊的通信域,比如一个网络连接
 一个特殊的通信类型,比如流或者数据报
 一个特殊的协议,比如TCP或者UDP

1.3 Unix的Socket() C函数详细说明:
int socket(int domain, int type, int protocol)
domain的取值:
 PF_UNIX  Unix IPC通信
 PF_INET  IPV4通信
 PF_INET6 IPV6
 PF_IPX  Novell IPX
 PF_NETLINK Kernel用户接口驱动程序
 PF_X25  X.25
 PF_AX25
 PF_ATMPVC ATM PVC
 PF_APPLETALK AppleTalk协议
 PF_PACKET 低级包接口
type的取值:
 SOCK_STREAM 使用面向连接的通信包
 SOCK_DGRAM 使用无连接的通信包
 SOCK_SEQPACKET 使用有固定最大长度的面向连接的通信包
 SOCK_RAW 使用原IP包
 SOCK_RDM 使用不保证次序的可靠数据报
Protocol:
 一般使用与type对应的默认协议,用0表示。
例如: int newsocket = socket(PF_INET, SOCK_STREAM,  0 );//使用TCP

1.4 使用面向连接的套接字
IP领域只有两种类型: connection-oriented, connectionless
使用面向连接的套接字,服务器和客户端需要如下通信方式:

Server   Client

socket() socket()
bind()
listen()
accept() <—- connect()
recv()   <—- send()
send()   —-> recv()
close()  <—> close()

1.5 使用无连接的套接字
SOCK_DGRAM使用UDP协议。通信方式如下:

UDP Server UDP Client

socket() socket()
bind()
recvform() <— sendto()
sendto()   —> recvform()
close()  close()

1.6 使用无阻塞的I/O方法

什么是阻塞?
 比如使用recv(),如果函数接受不到数据,就会阻塞程序的继续执行。

如何防止阻塞?
 使用fcntl()函数,把套接字设置为无阻塞模式。

 int newsocket;
 newsocket = socket(PF_INET, SOCK_STREAM, 0 );
 fcntl( newsocket, F_SETEL, O_NONBLOCK );

 以后使用recv()就不会阻塞了。

 另一种方式是使用多路套接字select()

2 WinSock技术

总的说来,就是模仿Unix socket的实现。
2.1 WinSock下的函数和流程

Server  Client
WSAstartup() WSAStartup()
WSASocket() WASSocket()
bind()
listen()
WSAAccept() <– WSAConnect()
WSARecv()   <– WSASend()
WSASend()   –> WSARecv()
close()     <-> close()
WSACleanup() WSACleanup()

与Unix socket的最主要区别就是在最上面加了WSAStartup()函数,最后加了WSACleanup()函数。中间都是一样的。

int WSAStartup(WORD wversion, LPWSADATA lpWSAData)
第一个参数是版本,要求2.2 还是1.1.
函数成功以后,lpWSAData指向一个结构体,包括win sock的一些信息。

WSACleanup()函数用来释放winsocket库,这个函数之后,再调用任何socket函数,都会出错。

2.2 WinSock

2.2.1 采用类似Unix的方法
 使用ioctlsocket()把socket设置为无堵塞的。
 使用select()多路传输多个套接字。

2.2.2 使用WSAAsyncSelect()函数

WSAAsynSelect()扩展了Unix的select()函数,它允许Windows进行查询套接字的操作。创建的WSAAsynSelect()方法包括要监视的套接字和一个Windows消息值,当某个套接字出现的时候,该消息就会送到窗口。

int WSAAsynSelect(Socket s, HWND hwnd, unsigned int wMsg, long lEvent)

Event包括如下事件类型:
FD_ACCEPT  用套接字建立一个新的连接
FD_ADDRESS_LIST_CHANGE 为套接字的协议族而改变的本地地址列表
FD_CLOSE  一个现有的连接被关闭
FD_CONNECT  已经完成与远程主机连接的套接字
FD_GROUP_QOS  套接字组的QOS已经改变
FD_OOB   套接字受到带外的数据
FD_QOS   套接字的QOS已经改变
FD_READ   套接字有准备读的数据
FD_ROUTING_INTERFACE_CHANGE 套接字的路由接口已经改变到一个特殊的目的地
FD_WRITE  套接字准备写数据

Example:
 WSAAsynSelect( sock, hwnd, WM_SOCKET, FD_READ | FD_CLOSE );

2.2.3 使用WSAEventSelect()函数
为了适应一些应用程序例如精灵程序或者某些没有用户界面的服务程序(因此不使用窗口
句柄),Windows Socket
2提供了WSAEventSelect()函数和WSAEnumNetworkEvents()函数。WSAEventSelect()函数和
WSAAyncSelect()函数很类似,区别仅在于当一个FD_XXX网络事件发生时,WSAEventSelect()函数将导致一个应用程序指定
的事件对象被设置,而WSAAyncSelect()将导致一条Windows消息被发送(例如FD_READ,FD_WRITE等等)。

winexec(Pchar(‘StrCommand’),sw_Show);

其中"StrCommand"代表以下命令之一(使用Windows中的运行不要加引号):

"rundll32 shell32,Control_RunDLL" – 运行控制面板

"rundll32 shell32,OpenAs_RunDLL" – 打开"打开方式"窗口

"rundll32 shell32,ShellAboutA Info-Box" – 打开"关于"窗口

"rundll32 shell32,Control_RunDLL desk.cpl" – 打开"显示属性"窗口

"rundll32 user,cascadechildwindows" – 层叠全部窗口

"rundll32 user,tilechildwindows" – 最小化所有的子窗口

"rundll32 user,repaintscreen" – 刷新桌面

"rundll32 shell,shellexecute Explorer" – 重新运行Windows Explorer

"rundll32 keyboard,disable" – 锁写键盘

"rundll32 mouse,disable" – 让鼠标失效

"rundll32 user,swapmousebutton" – 交换鼠标按钮

"rundll32 user,setcursorpos" – 设置鼠标位置为(0,0)

"rundll32 user,wnetconnectdialog" – 打开"映射网络驱动器"窗口

"rundll32 user,wnetdisconnectdialog" – 打开"断开网络驱动器"窗口

"rundll32 user,disableoemlayer" – 显示BSOD窗口, (BSOD) = Blue Screen Of

Death, 即蓝屏

"rundll32 diskcopy,DiskCopyRunDll" – 打开磁盘复制窗口

"rundll32 rnaui.dll,RnaWizard" – 运行"Internet连接向导", 如果加上参数"/1"则为

silent模式

"rundll32 shell32,SHFormatDrive" – 打开"格式化磁盘(A)"窗口

"rundll32 shell32,SHExitWindowsEx -1" – 冷启动Windows Explorer

"rundll32 shell32,SHExitWindowsEx 1" – 关机

"rundll32 shell32,SHExitWindowsEx 0" – 退当前用户

"rundll32 shell32,SHExitWindowsEx 2" Windows9x 快速重启

"rundll32 krnl386.exe,exitkernel" – 强行退出Windows 9x(无确认)

"rundll rnaui.dll,RnaDial "MyConnect" – 运行"网络连接"对话框

"rundll32 msprint2.dll,RUNDLL_PrintTestPage" – 选择打印机和打印测试页

"rundll32 user,setcaretblinktime" – 设置光标闪烁速度

"rundll32 user, setdoubleclicktime" – 测试鼠标双击速度

"rundll32 sysdm.cpl,InstallDevice_Rundll" – 搜索非PnP设备

 控制面板中的各项功能

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL’, 9);  {辅助选项 属性-键盘}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL access.cpl, 1′, 9); {辅助选项 属性-声音}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL access.cpl, 2′, 9); {辅助选项 属性-显示}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL access.cpl, 3′, 9); {辅助选项 属性-鼠标}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL access.cpl, 4′, 9); {辅助选项 属性-常规}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL access.cpl, 5′, 9); {添加/删除程序 属性-安装/卸载}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Appwiz.cpl, 1′, 9); {添加/删除程序 属性-Windows安装程序}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Appwiz.cpl, 2′, 9); {添加/删除程序 属性-启动盘}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Appwiz.cpl, 3′, 9); {显示 属性-背景}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 0′, 9);  {显示 属性-屏幕保护程序}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 1′, 9); {显示 属性-外观}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 2′, 9); {显示 属性-设置}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL desk.cpl, 3′, 9);  {Internet 属性-常规}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 0′, 9); {Internet 属性-安全}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 1′, 9); {Internet 属性-内容}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 2′, 9); {Internet 属性-连接}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 3′, 9); {Internet 属性-程序}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 4′, 9); {Internet 属性-高级}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Inetcpl.cpl, 5′, 9); {区域设置 属性-区域设置}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 0′, 9); {区域设置 属性-数字}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 1′, 9); {区域设置 属性-货币}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 2′, 9); {区域设置 属性-时间}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 3′, 9);  {区域设置 属性-日期}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Intl.cpl, 4′, 9);

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Joy.cpl, 0′, 9);

 

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Joy.cpl, 1′, 9);  {鼠标 属性}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Main.cpl’, 9);  {多媒体 属性-音频}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 0′, 9); {多媒体 属性-视频}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 1′, 9); {多媒体 属性-MIDI}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 2′, 9); {多媒体 属性-CD音乐}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 3′, 9); {多媒体 属性-设备}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Mmsys.cpl, 4′, 9); {调制解调器 属性}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Modem.cpl’, 9);

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Netcpl.cpl’, 9);  {密码 属性}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Password.cpl’, 9); {扫描仪与数字相机 属性}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Sticpl.cpl’, 9);  {系统 属性-常规}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 0′, 9); {系统 属性-设备管理器}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 1′, 9); {系统 属性-硬件配置文件}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 2′, 9); {系统 属性-性能}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Sysdm.cpl, 3′, 9); {日期/时间 属性}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL timedate.cpl’, 9); {电源管理 属性}

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Powercfg.cpl’, 9);

 

 winexec(‘rundll32.exe shell32.dll, Control_RunDLL Telephon.cpl’, 9);

关于调用后的判断处理建议:

先声明一个cardinal类型的变量RtnCardinal获取返回值进行判断如:

RtnCardinal := winexec(‘rundll32.exe shell32.dll, Control_RunDLL

Telephon.cpl’, 9);

 返回值 可能原因

 0 程序超出内存 

ERROR_BAD_FORMAT 程序为一个非法的Win32.EXE程序

ERROR_FILE_NOT_FOUND 指定文件没找到 

ERROR_PATH_NOT_FOUND 指定路径没找到

使用方法:

点击“开始-程式-Ms-Dos方式”,进入Dos视窗,然後键入"rundll32.exe user.exe,restartwindows",再按下回车键,这时你将看到,机器被重启了!怎么样,是不是很有趣?

  当然,Rundll的功能绝不仅仅是重启你的机器。其实,Rundll者,顾名思义,执行Dll也,它的功能就是以命令列的方式呼叫Windows的
动态链结库,Rundll32.exe与Rundll.exe的区别就在於前者是呼叫32位的链结库,而後者是运用於16位的链结库,它们的命令格式是:


  RUNDLL.EXE ,,
 
 这里要注意三点:1.Dll档案名中不能含有空格,比如该档案位於c:\Program
Files\目录,你要把这个路径改成c:\Progra~1\;2.Dll档案名与Dll入口点间的逗号不能少,否则程式将出错并且不会给出任何资讯!
3.这是最重要的一点:Rundll不能用来呼叫含返回值参数的Dll,例如Win32API中的GetUserName(),GetTextFace
()等。在Visual Basic中,提供了一条执行外部程式的指令Shell,格式为:

  Shell “命令列”

  如果能配合Rundll32.exe用好Shell指令,会使您的VB程式拥有用其他方法难以甚至无法实现的效果:仍以重启为例,传统的方法需要你在VB工程中先建立一个模组,然後写入WinAPI的声明,最後才能在程式中呼叫。而现在只需一句:

  Shell “rundll32.exe user.exe,restartwindows”就搞定了!是不是方便多了?
实际上,Rundll32.exe在呼叫各种Windows控制面板和系统选项方面有著独特的优势。下面,我就将本人在因特网上收集的有关Rundll的
指令列举如下(很有用的,能省去你很多呼叫Windows API的时间!!),供大家在程式设计中引用:

  命令列: rundll32.exe shell32.dll,Control_RunDLL

  功能: 显示控制面板

  命令列: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,1

  功能: 显示“控制面板-辅助选项-键盘”选项视窗

  命令列: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,2

  功能: 显示“控制面板-辅助选项-声音”选项视窗

  命令列: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,3

  功能: 显示“控制面板-辅助选项-显示”选项视窗

  命令列: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,4

  功能: 显示“控制面板-辅助选项-滑鼠”选项视窗

  命令列: rundll32.exe shell32.dll,Control_RunDLL access.cpl,,5

  功能: 显示“控制面板-辅助选项-传统”选项视窗

  命令列: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl @1

  功能: 执行“控制面板-添加新硬体”向导。

  命令列: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL AddPrinter

  功能: 执行“控制面板-添加新印表机”向导。

  命令列: rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,1

  功能: 显示 “控制面板-添加/删除程式-安装/卸载” 面板。

  命令列: rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,2

  功能: 显示 “控制面板-添加/删除程式-安装Windows” 面板。

  命令列: rundll32.exe shell32.dll,Control_RunDLL appwiz.cpl,,3

  功能: 显示 “控制面板-添加/删除程式-启动盘” 面板。

  命令列: rundll32.exe syncui.dll,Briefcase_Create

  功能: 在桌面上建立一个新的“我的公文包”。

  命令列: rundll32.exe diskcopy.dll,DiskCopyRunDll

  功能: 显示复制软碟视窗

  命令列: rundll32.exe apwiz.cpl,NewLinkHere %1

  功能: 显示“建立快捷方式”的对话框,所建立的快捷方式的位置由%1参数决定。

  命令列: rundll32.exe shell32.dll,Control_RunDLL timedate.cpl,,0

  功能: 显示“日期与时间”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL timedate.cpl,,1

  功能: 显示“时区”选项视窗。

  命令列: rundll32.exe rnaui.dll,RnaDial [某个拨号连接的名称]

  功能: 显示某个拨号连接的拨号视窗。如果已经拨号连接,则显示目前的连接状态的视窗。

  命令列: rundll32.exe rnaui.dll,RnaWizard

  功能: 显示“新建拨号连接”向导的视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,0

  功能: 显示“显示属性-背景”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,1

  功能: 显示“显示属性-萤屏保护”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,2

  功能: 显示“显示属性-外观”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL desk.cpl,,3

  功能: 显示显示“显示属性-属性”选项视窗。

  命令列: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL FontsFolder

  功能: 显示Windows的“字体”档案夹。

  命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @3

  功能: 同样是显示Windows的“字体”档案夹。

  命令列: rundll32.exe shell32.dll,SHformatDrive

  功能: 显示格式化软碟对话框。

  命令列: rundll32.exe shell32.dll,Control_RunDLL joy.cpl,,0

  功能: 显示“控制面板-游戏控制器-一般”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL joy.cpl,,1

  功能: 显示“控制面板-游戏控制器-进阶”选项视窗。

  命令列: rundll32.exe mshtml.dll,PrintHTML (HTML文档)

  功能: 列印HTML文档。

  命令列: rundll32.exe shell32.dll,Control_RunDLL mlcfg32.cpl

  功能: 显示Microsoft Exchange一般选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @0

  功能: 显示“控制面板-滑鼠” 选项 。

  命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @1

  功能: 显示 “控制面板-键盘属性-速度”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @1,,1

  功能: 显示 “控制面板-键盘属性-语言”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @2

  功能: 显示Windows“印表机”档案夹。

  命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @3

  功能: 显示Windows“字体”档案夹。

  命令列: rundll32.exe shell32.dll,Control_RunDLL main.cpl @4

  功能: 显示“控制面板-输入法属性-输入法”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL modem.cpl,,add

  功能: 执行“添加新调制解调器”向导。

  命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,0

  功能: 显示“控制面板-多媒体属性-音频”属性页。

  命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,1

  功能: 显示“控制面板-多媒体属性-视频”属性页。

  命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,2

  功能: 显示“控制面板-多媒体属性-MIDI”属性页。

  命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,3

  功能: 显示“控制面板-多媒体属性-CD音乐”属性页。

  命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl,,4

  功能: 显示“控制面板-多媒体属性-设备”属性页。

  命令列: rundll32.exe shell32.dll,Control_RunDLL mmsys.cpl @1

  功能: 显示“控制面板-声音”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL netcpl.cpl

  功能: 显示“控制面板-网路”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL odbccp32.cpl

  功能: 显示ODBC32资料管理选项视窗。

  命令列: rundll32.exe shell32.dll,OpenAs_RunDLL

功能: 显示指定档案(drive:\path\filename)的“打开方式”对话框。

  命令列: rundll32.exe shell32.dll,Control_RunDLL password.cpl

  功能: 显示“控制面板-密码”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL powercfg.cpl

  功能: 显示“控制面板-电源管理属性”选项视窗。

  命令列: rundll32.exe shell32.dll,SHHelpShortcuts_RunDLL PrintersFolder

  功能: 显示Windows“印表机”档案夹。(同rundll32.exe

shell32.dll,Control_RunDLL main.cpl @2)

  命令列: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,0

  功能: 显示“控制面板-区域设置属性-区域设置”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,1

  功能: 显示“控制面板-区域设置属性-数字”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,2

  功能: 显示“控制面板-区域设置属性-货币”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,3

  功能: 显示“控制面板-区域设置属性-时间”选项视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL intl.cpl,,4

  功能: 显示“控制面板-区域设置属性-日期”选项视窗。

  命令列: rundll32.exe desk.cpl,InstallScreenSaver [萤屏保护档案名]

  功能: 将指定的萤屏保护档案设置为Windows的屏保,并显示萤屏保护属性视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,0

  功能: 显示“控制面板-系统属性-传统”属性视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,1

  功能: 显示“控制面板-系统属性-设备管理器”属性视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,2

  功能: 显示“控制面板-系统属性-硬体配置档案”属性视窗。

  命令列: rundll32.exe shell32.dll,Control_RunDLL sysdm.cpl,,3

  功能: 显示“控制面板-系统属性-性能”属性视窗。

  命令列: rundll32.exe user.exe,restartwindows

  功能: 强行关闭所有程式并重启机器。

  命令列: rundll32.exe user.exe,exitwindows

  功能: 强行关闭所有程式并关机。

  命令列: rundll32.exe shell32.dll,Control_RunDLL telephon.cpl

  功能: 显示“拨号属性”选项视窗

  命令列: rundll32.exe shell32.dll,Control_RunDLL themes.cpl

  功能: 显示“桌面主旨”选项面板

  当然,不止是VisualBasic,象Delphi.VisualC++等其他程式设计语言也可以通过呼叫外部命令的方法来使用Rundll的这些功能,具体方法这里就不再详细叙述了。灵活的使用Rundll,一定会使你的程式设计轻轻松松,达到事半功倍的效果!

本站原创:KKnD

Winsock提供了一个很有用的异步I/O模型,利用这个模型,应用程序可以在一个套接字上接

收以Windows消息为基础的网络事件通知。这个模型最开始出现在Winsock
1.1
版本中,是为

了帮助开发者面向一些早期的16位Windows平台而设计的。但是现在的应用程序仍然可以从

这种模型中得到好处,就连MFC中的CSocket类也采纳了这种模型。

由于该模型是基于Windows消息机制的,所以要想使用这种模型必须要Create一个窗口,这

个窗口将会被用来接收消息。接下来建立套接字,然后调用WSAAsyncSelect函数,打开窗口

消息通知,函数原型如下:

int
WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int uMsg, long
lEvent);

其中s就是我们想要的那个套接字;hWnd是接收消息通知那个窗口句柄;wMsg参数指定在

发生网络事件时要接受的消息,通常设成比WM_USER大的一个值,以避免消息冲突;


lEvent
指定了一个位掩码,对应一系列网络事件的组合,见下表:

Event
含义
FD_READ 程序想要接收有关是否可读的通知,以便读入数据
FD_WRITE 程序想要接收有关是否可写的通知,以便写入数据
FD_OOB 程序想要接收是否有OOB数据到达的通知
FD_ACCEPT 程序想要接收与进入连接有关的通知
FD_CONNECT 程序想要接收与一次连接或多点接入有关的通知
FD_CLOSE 程序想要接收与套接字关闭有关的通知
FD_QOS 程序想要接收套接字“服务质量(QoS)”发生变化的通知
FD_GROUP_QOS 暂时没用,属于保留事件
FD_ROUTING_INTERFACE_CHANGE 程序想要接收有关到指定地址的路由接口发生变化的通知
FD_ADDRESS_LIST_CHANGE 程序想要接收本地地址变化的通知

当程序在一个套接字上调用WSAAsyncSelect成功后,这个程序就会在与hWnd窗口句柄对

应的窗口例程中以Windows消息的形式接收网络事件通知。窗口例程通常定义成这个样子:

LRESULT
CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam,


         LPARAM
lParam)


其中wParam参数指定在其上面发生了一个网络事件的套接字,如果定义了多个套接字,这

个参数就显得很重要了。lParam参数则包含了两方面的重要信息,它的低位字指定了已经发

生的网络事件,而高位字包含了可能出现的错误代码。

简单的来说,这个模型的具体使用流程就是:

当网络消息抵达一个窗口例程后,程序要先检测lParam的高位字节,从而判断是否在套接字

上面发生了网络错误。现成的宏已经有在这里了 –> WSAGETSELECTERROR,可以用它返

回高字节包含的错误信息,如果没有发现任何的错误,接下来就是确定究竟是什么类型的网

络事件触发了这条Windows消息,这个操作也有现成的宏
–> WSAGETSELECTEVENT

下面就是源代码,其中部分很基本的代码我就省略掉了,编译平台为

Win2000
Server with SP2 + VC6.0 with SP5

#include
<windows.h>

#include <winsock2.h>


#define PORT 5150

#define DATA_BUFSIZE 8192

typedef
struct _SOCKET_INFORMATION {

 BOOL RecvPosted;

 CHAR Buffer[DATA_BUFSIZE];

 WSABUF DataBuf;

 SOCKET Socket;

 DWORD BytesSEND;

 DWORD BytesRECV;

 _SOCKET_INFORMATION *Next;

} SOCKET_INFORMATION, * LPSOCKET_INFORMATION;

#define
WM_SOCKET (WM_USER + 1)

void
CreateSocketInformation(SOCKET s, HWND);

LPSOCKET_INFORMATION GetSocketInformation(SOCKET s);

void FreeSocketInformation(SOCKET s);

LPSOCKET_INFORMATION
SocketInfoList;

LRESULT
CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

int
APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,

    
LPSTR
lpCmdLine, int nCmdShow)

{

 DWORD Ret;

 SOCKET Listen;

 SOCKADDR_IN InternetAddr;

 WSADATA wsaData;

 static TCHAR szAppName[] = TEXT ("HelloWin") ;

 HWND hwnd ;

 MSG msg ;

 WNDCLASS wndclass ;


 // Prepare echo server

 wndclass.style
= CS_HREDRAW | CS_VREDRAW ;

 …

 …

 RegisterClass
(&wndclass);

 hwnd = CreateWindow (…) ; // creation parameters

 ShowWindow (hwnd, nCmdShow) ;

 UpdateWindow (hwnd) ;



 if ((Ret = WSAStartup(0×0202, &wsaData)) != 0)

 {

  MessageBox(hwnd, TEXT("Start socket failed"),
TEXT("error"), MB_OK);

  ExitProcess(1);


 }

 if
((Listen = socket (PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)


 {

  MessageBox(hwnd, TEXT("socket() failed"), TEXT("error"),
MB_OK);

  ExitProcess(1);


 
}

 WSAAsyncSelect(Listen,
hwnd, WM_SOCKET, FD_ACCEPT|FD_CLOSE);

 InternetAddr.sin_family
= AF_INET;


 InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);

 InternetAddr.sin_port = htons(PORT);


 if (bind(Listen, (PSOCKADDR) &InternetAddr, sizeof(InternetAddr))

    == SOCKET_ERROR)

 {

  MessageBox(hwnd, TEXT("bind() failed"), TEXT("error"),
MB_OK);

  ExitProcess(1);

 }

 if
(listen(Listen, 5))

 {

  MessageBox(hwnd, TEXT("listen() failed"), TEXT("error"),
MB_OK);

  ExitProcess(1);


 
}

 // Translate and dispatch window messages for the application
thread

 while (GetMessage (&msg, NULL, 0, 0))

 {

  TranslateMessage (&msg) ;

  DispatchMessage (&msg) ;

 }

 return msg.wParam ;

}

LRESULT
CALLBACK WndProc (HWND hwnd, UINT message,

        
WPARAM
wParam, LPARAM lParam)

{

 HDC hdc ;

 PAINTSTRUCT ps ;

 RECT rect ;

 SOCKET Accept;

 LPSOCKET_INFORMATION SocketInfo;

 DWORD RecvBytes, SendBytes;

 DWORD Flags;



 switch (message)

 {

  case WM_CREATE:

   return 0 ;



  case WM_PAINT:

   hdc = BeginPaint (hwnd, &ps) ;

   GetClientRect (hwnd, &rect) ;

   DrawText (hdc, TEXT ("Server Started!"), -1,
&rect,

   DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;

   EndPaint (hwnd, &ps) ;

   return 0 ;



  case WM_DESTROY:

   PostQuitMessage (0) ;

   return 0 ;

  case
WM_SOCKET:


   if (WSAGETSELECTERROR(lParam))

   {

    MessageBox();

    FreeSocketInformation(wParam);

   }

   else

   {

    switch(WSAGETSELECTEVENT(lParam))

    {

     case FD_ACCEPT:

      if ((Accept = accept(wParam, NULL, NULL)) == INVALID_SOCKET)

      {

       MessageBox();

       break;

      }

      //
Create a socket information structure to associate with the


      // socket for processing I/O.

      CreateSocketInformation(Accept,
hwnd);


      WSAAsyncSelect(Accept,
hwnd, WM_SOCKET,

         FD_READ|FD_WRITE|FD_CLOSE);

      break;

     case
FD_READ:

      SocketInfo
= GetSocketInformation(wParam);

      //
Read data only if the receive buffer is empty.

      if
(SocketInfo->BytesRECV != 0)

      {

       SocketInfo->RecvPosted
= TRUE;

       return
0;

      }

      else

      {

       SocketInfo->DataBuf.buf
= SocketInfo->Buffer;

       SocketInfo->DataBuf.len
= DATA_BUFSIZE;


       Flags
= 0;

       if
(WSARecv(SocketInfo->Socket, &(SocketInfo->DataBuf),


           1,
&RecvBytes, &Flags, NULL, NULL) == SOCKET_ERROR)

       {

        if
(WSAGetLastError() != WSAEWOULDBLOCK)

        {

         MessageBox(…);

         FreeSocketInformation(wParam);

         return
0;

        }

       }


       else
// No error so update the byte count

        SocketInfo->BytesRECV
= RecvBytes;

      }

      //
DO NOT BREAK HERE SINCE WE GOT A SUCCESSFUL RECV.

      //
Go ahead and begin writing data to the client.

      case
FD_WRITE:

       SocketInfo
= GetSocketInformation(wParam);

       if
(SocketInfo->BytesRECV > SocketInfo->BytesSEND)

       {

        SocketInfo->DataBuf.buf
=

         SocketInfo->Buffer
+ SocketInfo->BytesSEND;

        SocketInfo->DataBuf.len
=

         SocketInfo->BytesRECV
– SocketInfo->BytesSEND;

        if
(WSASend(SocketInfo->Socket, &(SocketInfo->DataBuf),


            1,
&SendBytes, 0,NULL, NULL) == SOCKET_ERROR)

        {

         if
(WSAGetLastError() != WSAEWOULDBLOCK)

         {

          MessageBox(…);

          FreeSocketInformation(wParam);

          return
0;

         }

        }


        else
// No error so update the byte count

         SocketInfo->BytesSEND
+= SendBytes;

       }

       if
(SocketInfo->BytesSEND == SocketInfo->BytesRECV)

       {

        SocketInfo->BytesSEND
= 0;

        SocketInfo->BytesRECV
= 0;

        //
If a RECV occurred during our SENDs then we need to post

        //
an FD_READ notification on the socket.

        if
(SocketInfo->RecvPosted == TRUE)

        {

         SocketInfo->RecvPosted
= FALSE;

         PostMessage(hwnd,
WM_SOCKET, wParam, FD_READ);

        }

       }

       if(SocketInfo->DataBuf.buf
!= NULL)

        MessageBox(hwnd,
SocketInfo->DataBuf.buf,

          
TEXT("Received"), MB_OK);

       break;

      case
FD_CLOSE:

       FreeSocketInformation(wParam);

       break;

    }

   }

   return
0;

 }

 return
DefWindowProc(hwnd, message, wParam, lParam);

}



void CreateSocketInformation(SOCKET s, HWND hwnd)

{

 LPSOCKET_INFORMATION
SI;


 
if
((SI = (LPSOCKET_INFORMATION) GlobalAlloc(GPTR,

 sizeof(SOCKET_INFORMATION)))
== NULL)

 {

  MessageBox(…);

  return;

 }

 //
Prepare SocketInfo structure for use.

 SI->Socket
= s;

 SI->RecvPosted
= FALSE;

 SI->BytesSEND
= 0;

 SI->BytesRECV
= 0;

 SI->Next
= SocketInfoList;

 SocketInfoList
= SI;

}

LPSOCKET_INFORMATION
GetSocketInformation(SOCKET s)

{

 SOCKET_INFORMATION
*SI = SocketInfoList;

 
while(SI)

 {

  if (SI->Socket == s)

  return SI;

  SI
= SI->Next;

 }

 return
NULL;

}

void
FreeSocketInformation(SOCKET s)

{

 SOCKET_INFORMATION *SI = SocketInfoList;

 SOCKET_INFORMATION *PrevSI = NULL;

 while(SI)

 {

  if (SI->Socket == s)

  {

   if (PrevSI)

    PrevSI->Next = SI->Next;

   else

    SocketInfoList = SI->Next;

   closesocket(SI->Socket);

   GlobalFree(SI);

   return;

  }

  PrevSI
= SI;

  SI = SI->Next;

 }

}

服务器就这样建好了,只需要一个客户机就可以通信了,具体的代码我就不再贴了,各位可

以自己设计客户机,或者去下载区下载源程序。最后向大家推荐《Windows网络编程技术》,

真的不错。

本文中部分内容翻译自MSDN,客户机程序使用的是《Windows网络编程技术》中的例子

      WSAEventSelect模型有点类似WSAAsyncSelect模型,不同的是他不是用消息映射的方式来响应网络事件,而是用等待多重事件的方式来响应网络事件。下面是用WSAEventSelect模型和多线程机制做的一个简单的服务器程序的.cpp和.h文件,应用程序基于MFC的标准对话框。实现接受多个客户端的连接请求,并记录下所有客户端的相关信息,显示在列表框中。

// serverDlg.cpp : implementation file
//

#include "stdafx.h"
#include "server.h"
#include "serverDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

SOCKET Accept; file://用于新的一个连接通信的套接字
WSAEVENT NewEvent; file://对应于新的套接字的新事件
SOCKET Socket[WSA_MAXIMUM_WAIT_EVENTS];  file://存放所有生成的套接字
WSAEVENT Event[WSA_MAXIMUM_WAIT_EVENTS]; file://存放所有生成的事件对象
int EventTotal; file://创建的事件总数
int Index;      file://等待多重事件函数的返回值
WSANETWORKEVENTS NetworkEvents; file://用于接收套接字上发生的网络事件类型以及可能出现的错
误代码

/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
 CAboutDlg();

// Dialog Data
 file://{{AFX_DATA(CAboutDlg)
 enum { IDD = IDD_ABOUTBOX };
 file://}}AFX_DATA

 // ClassWizard generated virtual function overrides
 file://{{AFX_VIRTUAL(CAboutDlg)
 protected:
 virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
 file://}}AFX_VIRTUAL

// Implementation
protected:
 file://{{AFX_MSG(CAboutDlg)
 file://}}AFX_MSG
 DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
 file://{{AFX_DATA_INIT(CAboutDlg)
 file://}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 file://{{AFX_DATA_MAP(CAboutDlg)
 file://}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
 file://{{AFX_MSG_MAP(CAboutDlg)
  // No message handlers
 file://}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CServerDlg dialog

CServerDlg::CServerDlg(CWnd* pParent /*=NULL*/)
 : CDialog(CServerDlg::IDD, pParent)
{
 file://{{AFX_DATA_INIT(CServerDlg)
  // NOTE: the ClassWizard will add member initialization here
 file://}}AFX_DATA_INIT
 // Note that LoadIcon does not require a subsequent DestroyIcon in Win32
 m_Connectnum = 0;
 m_NetworkID = 0;
 EventTotal = 0;
    for(int i = 0; i < MAX_CLIENT_NUM; i++)
 {
  ZeroMemory(&m_ClientInfo[i], sizeof(client_info));
 }

 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CServerDlg::DoDataExchange(CDataExchange* pDX)
{
 CDialog::DoDataExchange(pDX);
 file://{{AFX_DATA_MAP(CServerDlg)
  // NOTE: the ClassWizard will add DDX and DDV calls here
 file://}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CServerDlg, CDialog)
 file://{{AFX_MSG_MAP(CServerDlg)
 ON_WM_SYSCOMMAND()
 ON_WM_PAINT()
 ON_WM_QUERYDRAGICON()
 ON_WM_TIMER()
 file://}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CServerDlg message handlers

BOOL CServerDlg::OnInitDialog()
{
 CDialog::OnInitDialog();

 // Add "About…" menu item to system menu.

 // IDM_ABOUTBOX must be in the system command range.
 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
 ASSERT(IDM_ABOUTBOX < 0xF000);

 CMenu* pSysMenu = GetSystemMenu(FALSE);
 if (pSysMenu != NULL)
 {
  CString strAboutMenu;
  strAboutMenu.LoadString(IDS_ABOUTBOX);
  if (!strAboutMenu.IsEmpty())
  {
   pSysMenu->AppendMenu(MF_SEPARATOR);
   pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
  }
 }

 // Set the icon for this dialog.  The framework does this automatically
 //  when the application’s main window is not a dialog
 SetIcon(m_hIcon, TRUE);   // Set big icon
 SetIcon(m_hIcon, FALSE);  // Set small icon
 
 // TODO: Add extra initialization here

 WSADATA wsaData;
 int ret;

 ret = WSAStartup(MAKEWORD(2,2), &wsaData);
 if(ret != 0)
 {
  MessageBox("初始化套接字失败!");
  return FALSE;
 }

 file://创建一个套接字
 m_ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if(m_ListenSocket == INVALID_SOCKET)
 {
  MessageBox("创建套接字失败!");
  closesocket(m_ListenSocket);
  WSACleanup();
  return FALSE;
 }

 file://绑定到指定的端口上
 sockaddr_in localaddr;
 localaddr.sin_family = AF_INET;
 localaddr.sin_port = htons(1688);
 localaddr.sin_addr.s_addr = 0;

 if(bind(m_ListenSocket, (const struct sockaddr*)&localaddr, sizeof(sockaddr))
                                                                 == SOCKET_ERROR)
 {
  MessageBox("绑定地址失败!");
  closesocket(m_ListenSocket);
  WSACleanup();
  return FALSE;
 }
 
 NewEvent = WSACreateEvent(); file://创建一个新的事件对象

 file://将创建的事件对象与前面创建的套接字关联在一起,并注册网络事件类型
        if(WSAEventSelect(m_ListenSocket, NewEvent, FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR)
 {
  MessageBox("注册网络事件失败!");
  closesocket(m_ListenSocket);
  WSACleanup();
  return FALSE;
 }

 file://让创建的套接字处于监听状态
 listen(m_ListenSocket, 5);

 Event[EventTotal] = NewEvent;
 Socket[EventTotal] = m_ListenSocket;
 EventTotal++;

        file://设置List控件的图象列表
 HICON hIcon;

 m_imagelist.Create(16, 16, 0, 4, 4); // 32, 32 for large icons
 hIcon = AfxGetApp()->LoadIcon(IDI_CLIENT_INFO);
 
 m_imagelist.SetBkColor (RGB(248,232,224));
 m_imagelist.Add(hIcon);

 pList = (CListCtrl*)GetDlgItem(IDC_CLIENT_INFO);
 pList->SetImageList(&m_imagelist, LVSIL_SMALL);
 pList->SetBkColor(RGB(248,232,224));
 pList->SetTextBkColor(RGB(248,232,224));

 pList->InsertColumn(0,"   客户名",LVCFMT_CENTER,90, 0);
 pList->InsertColumn(1,"网络ID",LVCFMT_CENTER,50,1);
 pList->InsertColumn(2,"IP地址",LVCFMT_CENTER,100,2);
 pList->InsertColumn (3,"登录时间",LVCFMT_CENTER,120,3);
 pList->InsertColumn (4,"在线时间",LVCFMT_CENTER,100,4);

 SetTimer(1, 1000, NULL);

 file://启动核心处理线程
 AfxBeginThread(KernelWorkThread,this,THREAD_PRIORITY_NORMAL);

 return TRUE;  // return TRUE  unless you set the focus to a control
}

void CServerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
 if ((nID & 0xFFF0) == IDM_ABOUTBOX)
 {
  CAboutDlg dlgAbout;
  dlgAbout.DoModal();
 }
 else
 {
  CDialog::OnSysCommand(nID, lParam);
 }
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CServerDlg::OnPaint()
{
 if (IsIconic())
 {
  CPaintDC dc(this); // device context for painting

  SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

  // Center icon in client rectangle
  int cxIcon = GetSystemMetrics(SM_CXICON);
  int cyIcon = GetSystemMetrics(SM_CYICON);
  CRect rect;
  GetClientRect(&rect);
  int x = (rect.Width() – cxIcon + 1) / 2;
  int y = (rect.Height() – cyIcon + 1) / 2;

  // Draw the icon
  dc.DrawIcon(x, y, m_hIcon);
 }
 else
 {
  CDialog::OnPaint();
 }
}

// The system calls this to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CServerDlg::OnQueryDragIcon()
{
 return (HCURSOR) m_hIcon;
}

file://核心处理线程, 响应并处理各种网络事件
UINT KernelWorkThread(LPVOID pParam)
{
 int len = sizeof(sockaddr);

 CServerDlg* dlg;
 dlg = (CServerDlg*)pParam;

 while(1)
 {
         Index = WSAWaitForMultipleEvents(EventTotal, Event, FALSE, WSA_INFINITE, FALSE);

  WSAEnumNetworkEvents(Socket[Index - WSA_WAIT_EVENT_0],
                     Event[Index - WSA_WAIT_EVENT_0],
                                     &NetworkEvents);
 
  if(NetworkEvents.lNetworkEvents & FD_ACCEPT)
  file://连接事件
  {
   if(NetworkEvents.iErrorCode[FD_ACCEPT_BIT] != 0)
   {
    dlg->MessageBox("接受连接事件失败!");
    break;
   }

   Accept = accept(Socket[Index - WSA_WAIT_EVENT_0],
                                 (struct sockaddr*)&(dlg->clientaddr), &len);
   if(Accept == INVALID_SOCKET)
   {
    dlg->MessageBox("接受连接失败!");
    break;
   }
  
   if(EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
   {
    dlg->MessageBox("连接个数溢出,拒绝接受!");
    break;
   }

   NewEvent = WSACreateEvent();

   if(WSAEventSelect(Accept, NewEvent, FD_READ | FD_WRITE | FD_CLOSE)
                                                                   == SOCKET_ERROR)
   {
    dlg->MessageBox("注册网络事件失败!");
    closesocket(Accept);
    break;
   }

   Event[EventTotal] = NewEvent;
   Socket[EventTotal] = Accept;
   EventTotal ++;
  }

  if(NetworkEvents.lNetworkEvents & FD_READ)
  file://读取数据事件
  {
   if(NetworkEvents.iErrorCode[FD_READ_BIT] != 0)
   {
    dlg->MessageBox("读事件失败!");
    break;
   }

   if(dlg->OnReceive(Socket[Index - WSA_WAIT_EVENT_0]) == FALSE)
   {
    dlg->MessageBox("读取数据失败!");
    break;
   }
  }

  if(NetworkEvents.lNetworkEvents & FD_CLOSE)
  file://关闭套接字事件
  {
   if(NetworkEvents.iErrorCode[FD_CLOSE_BIT] != 0)
   {
    dlg->MessageBox("关闭事件失败!");
    break;
   }

   if(dlg->OnClose(Socket[Index - WSA_WAIT_EVENT_0]) == FALSE)
   {
    dlg->MessageBox("关闭套接字失败!");
    break;
   }
  }
 }

 return 0;
}

BOOL CServerDlg::OnClose(SOCKET pSocket)
{
 int i, exitnum;
 
 for(i = 0; i < m_Connectnum; i++)
 {
  if(m_ClientInfo[i].Client_Socket == pSocket)
  {
   exitnum = i;
  }
 }
 for(i = exitnum; i < m_Connectnum; i++)
 {
  memcpy(&m_ClientInfo[i], &m_ClientInfo[i+1], sizeof(client_info));
 }

 m_Connectnum –;

 file://向所有客户端发送在线客户信息的报文
 cmd_client_info ClientInfo;
 ClientInfo.cmd_type = CMD_CLIENT_INFO;
 ClientInfo.client_num = m_Connectnum;
 
 for(i=0; i<=m_Connectnum; i++)
 {
  ClientInfo.Networks_ID[i] = m_ClientInfo[i].Network_ID;
  strcpy(ClientInfo.users_name[i], m_ClientInfo[i].User_Name);
  strcpy(ClientInfo.clients_ipaddr[i], inet_ntoa(m_ClientInfo[i].Client_Addr.sin_addr));
 }
 for(i=0; i<=m_Connectnum; i++)
 {
  send(m_ClientInfo[i].Client_Socket, (char*)&ClientInfo, sizeof(cmd_client_info), NULL);
 }
 closesocket(pSocket);

        pList->DeleteItem(exitnum);
 
 return TRUE;
}

BOOL CServerDlg::OnReceive(SOCKET pSocket)
{
    static char rcvbuf[65535];   file://接收缓冲区
    int ret;
 int offset=0;
 find_type* pFindType;
 int i = 0;
 CTime m_current_time=CTime::GetCurrentTime ();
 CString strTime = m_current_time.Format("%c");
 CString networkid; file://列表框的网络ID项

    ret = recv(pSocket, rcvbuf, 65535, 0);
 if(ret == OPERATION_ERROR)
  return FALSE;

 while(offset < ret)
 {
  pFindType = (find_type*)(rcvbuf+offset);
  switch(pFindType->cmd_type)
  {
  case CMD_HELLO:
   cmd_hello Hello;
   memcpy(&Hello, rcvbuf+offset, sizeof(cmd_hello));
   offset+=sizeof(cmd_hello);

   cmd_hello_resp HelloResp;
   m_NetworkID ++;
   HelloResp.cmd_type = CMD_HELLO_RESP;
   HelloResp.Network_ID = m_NetworkID;
   strcpy(HelloResp.user_name, Hello.user_name);

   memcpy((struct sockaddr*)&(m_ClientInfo[m_Connectnum].Client_Addr),
    (const struct sockaddr*)&clientaddr, sizeof(sockaddr));
   m_ClientInfo[m_Connectnum].Client_Socket = Accept;
   strcpy(m_ClientInfo[m_Connectnum].User_Name, HelloResp.user_name);
   m_ClientInfo[m_Connectnum].Network_ID = m_NetworkID;
   m_ClientInfo[m_Connectnum].Login_Time = m_current_time;
   send(pSocket, (char*)&HelloResp, sizeof(cmd_hello_resp), NULL);

   file://向登录的客户端发送回应报文
   Sleep(200);

   cmd_client_info ClientInfo;
   ClientInfo.cmd_type = CMD_CLIENT_INFO;
   ClientInfo.client_num = m_Connectnum +1;

   for(i=0; i<=m_Connectnum; i++)
   {
    ClientInfo.Networks_ID[i] = m_ClientInfo[i].Network_ID;
    strcpy(ClientInfo.users_name[i], m_ClientInfo[i].User_Name);
                                strcpy(ClientInfo.clients_ipaddr[i],
                                       inet_ntoa(m_ClientInfo[i].Client_Addr.sin_addr));
   }

   file://向所有在线客户端发送在线客户信息报文
   for(i=0; i<=m_Connectnum; i++)
   {
    send(m_ClientInfo[i].Client_Socket, (char*)&ClientInfo,
                                     sizeof(cmd_client_info), NULL);
   }

   file://刷新客户端信息列表
   networkid.Format("%d", m_NetworkID);

   LVITEM lvinsert;
   lvinsert.mask=LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM;
   lvinsert.iItem=m_Connectnum;
   lvinsert.iSubItem=0;
   lvinsert.cchTextMax=20;
   lvinsert.pszText=HelloResp.user_name;
   lvinsert.iImage = 0;
   pList->InsertItem (&lvinsert);
   pList->SetItemText (m_Connectnum,1,networkid);
   pList->SetItemText(m_Connectnum,2,
                               inet_ntoa(m_ClientInfo[m_Connectnum].Client_Addr.sin_addr));
   pList->SetItemText (m_Connectnum,3,strTime);

   m_Connectnum ++;

   break;
  case CMD_ASK:
   cmd_ask Ask;
   cmd_ask_resp AskResp;
   memcpy(&Ask,rcvbuf+offset,sizeof(cmd_ask));
   offset+=sizeof(cmd_ask);
   AskResp.cmd_type = CMD_ASK_RESP;
   AskResp.Network_ID = Ask.Network_ID;
   for(i=0; i<m_Connectnum; i++)
   {
    if(m_ClientInfo[i].Network_ID == Ask.Network_ID)
    {
     strcpy(AskResp.pData1,m_ClientInfo[i].User_Name);
     strcat(AskResp.pData1, ":");
    }
   }
   strcpy(AskResp.pData2, Ask.pData);
   for(i=0; i<m_Connectnum; i++)
   {
    send(m_ClientInfo[i].Client_Socket, (char*)&AskResp, sizeof(AskResp), 0);
   }

   break;
  case CMD_GOODBYE:
   closesocket(pSocket);
   break;
  default:
   break;
  }
 }

 return TRUE;
}
BOOL CServerDlg::OnSend(SOCKET pSocket)
{
 return TRUE;
}

void CServerDlg::OnOK()
{
 closesocket(m_ListenSocket);
 WSACleanup();
 CDialog::OnOK();
}

void CServerDlg::OnTimer(UINT nIDEvent)
{
 CTime m_current_time = CTime::GetCurrentTime();
 CTimeSpan logintimes;
 CString login_times;
 CString networkid; file://列表框的网络ID项
 
 for(int i=0; i<m_Connectnum; i++)
 {
  logintimes = m_current_time – m_ClientInfo[i].Login_Time;
  login_times.Format("%d小时%d分%d秒", logintimes.GetHours(),
                                    logintimes.GetMinutes(),
            logintimes.GetSeconds());
 
  pList->SetItemText (i,4,login_times);
 }

 CDialog::OnTimer(nIDEvent);
}


// serverDlg.h : header file
//

#if !defined(AFX_SERVERDLG_H__B0AA0367_C1F4_11D4_AB1C_0080C8D6FEA5__INCLUDED_)
#define AFX_SERVERDLG_H__B0AA0367_C1F4_11D4_AB1C_0080C8D6FEA5__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "global.h"

/////////////////////////////////////////////////////////////////////////////
// CServerDlg dialog

class CServerDlg : public CDialog
{
file://全局函数
    friend UINT KernelWorkThread(LPVOID pParam);
// Construction
public:
 CListCtrl* pList; file://客户端在线信息列表框对象
 CImageList m_imagelist;

 SOCKET m_ListenSocket; file://用于监听端口的套接字
 client_info m_ClientInfo[MAX_CLIENT_NUM]; file://保存在线客户端信息的结构体数组
 sockaddr_in clientaddr; file://保存发起连接的客户端地址
 int m_Connectnum; file://在线客户端个数
 int m_NetworkID;  file://返回给客户端的网络ID号

 BOOL OnSend(SOCKET pSocket);    file://发送数据网络事件的响应函数
 BOOL OnReceive(SOCKET pSocket); file://接收数据网络事件的响应函数
 BOOL OnClose(SOCKET pSocket);   file://关闭套接字网络事件的响应函数

 CServerDlg(CWnd* pParent = NULL); // standard constructor

// Dialog Data
 file://{{AFX_DATA(CServerDlg)
 enum { IDD = IDD_SERVER_DIALOG };
  // NOTE: the ClassWizard will add data members here
 file://}}AFX_DATA

 // ClassWizard generated virtual function overrides
 file://{{AFX_VIRTUAL(CServerDlg)
 protected:
 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
 file://}}AFX_VIRTUAL

// Implementation
protected:
 HICON m_hIcon;

 // Generated message map functions
 file://{{AFX_MSG(CServerDlg)
 virtual BOOL OnInitDialog();
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
 afx_msg void OnPaint();
 afx_msg HCURSOR OnQueryDragIcon();
 virtual void OnOK();
 afx_msg void OnTimer(UINT nIDEvent);
 file://}}AFX_MSG
 DECLARE_MESSAGE_MAP()
};

file://{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_SERVERDLG_H__B0AA0367_C1F4_11D4_AB1C_0080C8D6FEA5__INCLUDED_)

2005年08月24日


点这里观看短片>>>

短片里面的台词内容如下:

It is a best time,it is a worst time.

In the year 2014 people have access to a breadth and depth of information unimaginable in an earlier age.


Everyone contributes in some way.

Everyone
participates to create a living, breathing mediascape. However, the
Press, as you know it, has ceased to exist. The Fourth Estate’s
fortunes have waned. 20th Century news organizations are an
after-thought, a lonely remnant of a not too distant past.

The road to 2014 began in the mid-20th Century.

In
1989, Tim Berners-Lee, a computer scientist at the CERN particle
physics laboratory in Switzerland, invents the World Wide Web.

1994
sees the founding of Amazon.com. Its young creator dreams of a store
that sells everything. Amazon’s model, which would come to set the
standard for Internet sales, is built on automated personalized
recommendations – a store that can make suggestions.

In 1998,
two Stanford programmers create Google. Their algorithm echoes the
language of Amazon, it treats links as recommendations, and from that
foundation powers the world’s most effective search engine.

In
1999, TiVo transforms television by unshackling it from the constraints
of time – and commercials. Almost no one who tries it ever goes back.

That year, a dot-com start-up named Pyra Labs unveils Blogger, a personal publishing tool.

Friendster
launches in 2002 and hundreds of thousands of young people rush to
populate it with an incredibly detailed map of their lives, their
interests and their social networks. Also in 2002, Google launches
GoogleNews, a news portal. News organizations cry foul. GoogleNews is
edited entirely by computers.

In 2003, Google buys Blogger. Google’s plans are a mystery, but their interest in Blogger is not unreasonable.

2003 is the Year of the Blog.

2004 would be remembered as the year that everything began.

Reason
Magazine sends subscribers an issue with a satellite photo of their
houses on the cover and information custom-tailored to each subscriber
inside.

Sony and Philips unveil the world’s first mass-produced electronic paper.

Google unveils GMail, with a gigabyte of free space for every user.

Microsoft unveils Newsbot, a social news filter.

Amazon unveils A9, a search engine built on Google’s technology that also incorporates Amazon’s trademark recommendations.

And then, Google goes public.

Awash in new capital, the company makes a major acquisition. Google buys TiVo.

2005 – In response to Google’s recent moves, Microsoft buys Friendster.

2006
– Google combines all of its services – TiVo, Blogger, GMail,
GoogleNews and all of its searches into the Google Grid, a universal
platform that provides a functionally limitless amount of storage space
and bandwidth to store and share media of all kinds. Always online,
accessible from anywhere. Each user selects her own level of privacy.
She can store her content securely on the Google Grid, or publish it
for all to see. It has never been easier for anyone, everyone to create
as well as consume media.

2007 – Microsoft responds to Google’s
mounting challenge with Newsbotster, a social news network and
participatory journalism platform. Newsbotster ranks and sorts news,
based on what each user’s friends and colleagues are reading and
viewing and it allows everyone to comment on what they see.

Sony’s ePaper is cheaper than real paper this year. It’s the medium of choice for Newsbotster.

2008
sees the alliance that will challenge Microsoft’s ambitions. Google and
Amazon join forces to form Googlezon. Google supplies the Google Grid
and unparalled search technology. Amazon supplies the social
recommendation engine and its huge commercial infrastructure. Together,
they use their detailed knowledge of every user’s social network,
demographics, consumption habits and interests to provide total
customization of content – and advertising.

The News Wars of 2010 are notable for the fact that no actual news organizations take part.

Googlezon
finally checkmates Microsoft with features the software giant cannot
match. Using a new algorithm, Googlezon’s computers construct news
stories dynamically, stripping sentences and facts from all content
sources and recombining them. The computer writes a news story for
every user.

In 2011, the slumbering Fourth Estate awakes to
make its first and final stand. The New York Times Company sues
Googlezon, claiming that the company’s fact-stripping robots are a
violation of copyright law. The case goes all the way to the Supreme
Court, which on August 4, 2011 decides in favour of Googlezon.

On Sunday, March 9 2014, Googlezon unleashes EPIC.

Welcome to our world.

The
‘Evolving Personalized Information Construct’ is the system by which
our sprawling, chaotic mediascape is filtered, ordered and delivered.
Everyone contributes now – from blog entries, to phone-cam images, to
video reports, to full investigations. Many people get paid too – a
tiny cut of Googlezon’s immense advertising revenue, proportional to
the popularity of their contributions.

EPIC produces a custom
contents package for each user, using his choices, his consumption
habits, his interests, his demographics, his social network – to shape
the product.

A new generation of freelance editors has sprung
up, people who sell their ability to connect, filter and prioritize the
contents of EPIC.

We all subscribe to many Editors; EPIC allows
us to mix and match their choices however we like. At its best, edited
for the savviest readers, EPIC is a summary of the world – deeper,
broader and more nuanced than anything ever available before.

2005年08月23日

本文简单介绍了当前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
不过代码写的很简陋,请多包涵!

2005年08月22日

作者:Hackfan

日期:2005.8.21凌晨

联系:QQ:106814 Email:hackfan@vip.sina.com



1、研究说明



  Tencent在tqq.tencent.com的8000有一个使用HTTP的QQ接口,通过这个接口,可以进行一些基本的操作,如:登
陆、登出、改变登陆状态(上线、忙碌、离线、隐身)、添加删除好友、查看好友信息、发送验证信息(接受被加为好友、申请加对方为好友、拒绝被加为好友)、
收发用户消息、系统信息。



  目前我研究的是1.1版本的HTTP QQ协议,研究是微程在的成果上进行的,不敢说有什么超越,只不过更为详细和准确。



2、接口说明:



  接口位置:tqq.tencent.com:8000

  通信协议:HTTP

  数据传输方法:POST

  HTTP请求格式:



POST HTTP/1.1

Host: tqq.tencent.com:8000

Content-Type: text/plain; charset=UTF-8

Content-length: 长度

Connection: close



数据



  其中长度为 数据 的长度,数据的格式:

  VER=1.1&CMD=命令&SEQ=标记&UIN=QQ号&….



  以上4个参数是每个请求都必有的。其中,VER表示协议的版本,目前为1.1,据说1.2已经出来了,这个乱写的话,服务器返回NULL;
CMD为操作的指令,有Login、List、Query_Stat、GetInfo、AddToList、Ack_AddToList、
DelFromList、Change_Stat、GetMsgEx、CLTMSG、Logout;SEQ为当前请求的标记,防止重复发送,可以用当前时
间,也可以用随机数;UIN是当前执行操作的QQ号。不过不同的CMD还需要不同的参数,下面我就公布我的研究成果。



3、研究方法:



  我对目前网上的资料不够满意,就自己写程序,发送多条相同CMD不同参数的请求,根据服务器的返回,来做判断。感兴趣的朋友可以参考一下,此处可以跳过。

  下面我公布我探测的代码(PHP):



[code:1:1bbf2dec18]

<?

$uin = "QQ号";

$pwd = md5("QQ密码");



//登陆测试

$poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M5=1&LC=9326B87B234E7235";

//注意:登陆测试不能同时进行,必须等到服务器认为QQ断开了,才能够测试,不然结果不可信

/*******

$poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M5=0&LC=9326B87B234E7235";

$poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M5=1&LC=9326B87B234E7235";

$poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M5=2&LC=9326B87B234E7235";

$poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M5=3&LC=9326B87B234E7235";

$poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M6=1&LC=9326B87B234E7235";

$poststring[] = "VER=1.1&CMD=Login&SEQ=".rand(1000,9000)."&UIN=".$uin."&PS=".$pwd."&M6=1&LC=1223423545756679";

*******/





//得到好友列表

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin;

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0";

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160";

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=0";

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=".rand(1,10);

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=0";

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160&UN=0";

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160&UN=0";

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);

$poststring[] = "VER=1.1&CMD=List&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=106814";



//得到在线列表

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin;

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0";

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160";

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=0";

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=".rand(1,10);

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=0";

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160&UN=0";

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=160&UN=0";

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=".rand(1,200)."&UN=0";

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=".rand(1,10);

$poststring[] = "VER=1.1&CMD=Query_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&TN=0&UN=106814";



//查看好友信息

$poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=0&UN=106814";

$poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=1&UN=106814";

$poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=2&UN=106814";

$poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=3&UN=106814";

$poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=4&UN=106814";

$poststring[] = "VER=1.1&CMD=GetInfo&SEQ=".rand(1000,9000)."&UIN=".$uin."&LV=5&UN=106814";



//增加好友

$poststring[] = "VER=1.1&CMD=AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814";



//发送验证

$poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=0&RS=TEST";

$poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=1&RS=TEST";

$poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=2&RS=TEST";

$poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=3&RS=TEST";

$poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=4&RS=TEST";

$poststring[] = "VER=1.1&CMD=Ack_AddToList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&CD=5&RS=TEST";



//删除好友

$poststring[] = "VER=1.1&CMD=DelFromList&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814";



//改变状态

for($i=0;$i<=60;$i=$i+5)

{

$poststring[] = "VER=1.1&CMD=Change_Stat&SEQ=".rand(1000,9000)."&UIN=".$uin."&ST=".$i;

}



//获得消息

$poststring[] = "VER=1.1&CMD=GetMsgEx&SEQ=".rand(1000,9000)."&UIN=".$uin."";



//发送消息

$poststring[] = "VER=1.1&CMD=CLTMSG&SEQ=".rand(1000,9000)."&UIN=".$uin."&UN=106814&MG=TEST";



//登出

$poststring[] = "VER=1.1&CMD=Logout&SEQ=".rand(1000,9000)."&UIN=".$uin."";



$file = fopen("p.txt","w");



foreach($poststring as $k=>$v)

{

ss_timing_start();

$fp = fsockopen('tqq.tencent.com', '8000', $errno, $errstr, $timeout = 10); 



if(!$fp){ 

 //error tell us 

 $content = $k.chr(13).chr(10)."ERROR:$errstr ($errno)"; 

   

}else{ 



  //send the server request 

  fputs($fp, "POST HTTP/1.1\r\n"); 

//  fputs($fp, "Host: $host\r\n"); 

//  fputs($fp, "Content-type: application/x-www-form-urlencoded\r\n"); 

  fputs($fp, "Content-length: ".strlen($v)."\r\n"); 

  fputs($fp, "Connection: close\r\n\r\n"); 

  fputs($fp, $v . "\r\n\r\n"); 



  //loop through the response from the server 

  $res = "";

  while(!feof($fp)) { 

   $res .= fgets($fp, 4096); 

  } 

  //close fp - we are done with it 

  fclose($fp); 



  $content = $v.chr(13).chr(10).$res;



ss_timing_stop(); 


$content .= chr(13).chr(10)."Time: ".ss_timing_current().chr(13).chr(10)."--------------------------------------".chr(13).chr(10);

fputs($file,$content);

}

fclose($file);

?>

<?

function ss_timing_start ($name = "default") { 

global $ss_timing_start_times; 

$ss_timing_start_times[$name] = explode(' ', microtime()); 



function ss_timing_stop ($name = "default") { 

global $ss_timing_stop_times; 

$ss_timing_stop_times[$name] = explode(' ', microtime()); 



function ss_timing_current ($name = "default") { 

global $ss_timing_start_times, $ss_timing_stop_times; 

if (!isset($ss_timing_start_times[$name])) { 

return 0; 



if (!isset($ss_timing_stop_times[$name])) { 

$stop_time = explode(' ', microtime()); 



else { 

$stop_time = $ss_timing_stop_times[$name]; 



$current  =  $stop_time[1]-$ss_timing_start_times[$name][1]; 

$current += $stop_time[0]-$ss_timing_start_times[$name][0]; 

return $current; 



?>

[/code:1:1bbf2dec18]



4、研究成果:



(1).登陆

  说明:在你做任何其他操作以前,你必须登陆。只有在登陆以后,你的其他指令才有可能被正确执行(返回RES=0),不然服务器会返回RES=
20,不过有个例外,就是logout。当你成功登陆以后,服务器就会根据你的IP*和参数中的UIN来验证身份。一台电脑可以同时登陆多个QQ,互不影
响,就是因为有参数UIN。

  *至于我能够确定服务器是通过IP来验证的,是因为服务器不可能通过我的请求获得其他信息了^_^



  提交数据:VER=1.1&CMD=Login&SEQ=标记&UIN=QQ号&PS=QQ密码&M5=1&LC=9326B87B234E7235

  说明:QQ密码是通过md5加密的字符串,在PHP中可以直接用md5()进行加密;

     M5这个参数的作用还不清楚,但最好为1。

     LC这个参数有点神秘,不能有丝毫改动,不然服务器就没有响应(没有响应就是返回NULL)。



  返回:VER=1.1&CMD=LOGIN&SEQ=标记&UIN=QQ号&RES=0&RS=0&HI=60&LI=300(成功)

     VER=1.1&CMD=LOGIN&SEQ=标记&UIN=QQ号&RES=0&RS=1&RA=密码错误(密码错误)

     VER=1.1&CMD=LOGIN&SEQ=标记&UIN=QQ号&RES=5(QQ号非法,如100)

     NULL(UIN为字符、PS为空、LC错误)



(2).得到好友列表

  提交数据:VER=1.1&CMD=List&SEQ=标记&UIN=QQ号&TN=160&UN=0

  说明:TN、UN还不清楚具体表示什么,但是TN的值会影响返回的结果,有没有UN对结果没有影响



  返回:VER=1.1&CMD=LIST&SEQ=标记&UIN=QQ号&RES=0&FN=9(当TN=0或没有TN参数时,FN表示好友数)

     VER=1.1&CMD=LIST&SEQ=标记&UIN=QQ号&RES=0&FN=
1&SN=9&UN=3814526,...,(当TN存在且非0时,FN=1,SN表示好友数,UN为好友列表,用","分割)

     VER=1.1&CMD=LIST&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)

     NULL(UIN、TN、UN为字符)



(3).得到在线好友列表

  提交数据:VER=1.1&CMD=Query_Stat&SEQ=标记&UIN=QQ号&TN=50&UN=0

  说明:TN、UN还不清楚具体表示什么,但是TN的值会影响返回的结果,有没有UN对结果没有影响



  返回:VER=1.1&CMD=QUERY_STAT&SEQ=标记&UIN=QQ号&RES=
0&FC=0,&FN=1&SN=1&ST=10,&UN=106814,&NK=Hackfan 
好,(当TN存在且非0时,FN=1,SN表示在线好友数,FC、ST、UN、NK的值用','分割,分别表示头像、状态、号码、昵称)

     VER=1.1&CMD=QUERY_STAT&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)

     NULL(UIN、TN、UN为字符)

  说明:FC为QQ头像的的ID,如的头像ID为270,那么其头使用的图片为91.bmp,其算法为ID/3+1;

     ST为QQ用户的状态,10为上线,20为离线(或隐身),30为忙碌;



  特别说明:当参数TN=0或不存在时,服务器返回:

VER=1.1&CMD=Query_Stat&SEQ=标记&UIN=QQ号

HTTP/1.1 200 OK

Server: tencent imserver/1.0.0

Content-Type: text/plain; charset=UTF-8

Content-Length: 56



VER=1.1&CMD=QUERY_STAT&SEQ=标记&UIN=QQ号&RES=0&FN=1

HTTP/1.1 200 OK

Server: tencent imserver/1.0.0

Content-Type: text/plain; charset=UTF-8

Content-Length: 77



VER=1.1&CMD=QUERY_STAT&SEQ=标记&UIN=QQ号&RES=0&FC=&FN=1&SN=0&ST=&UN=&NK=

  返回了2次,第一次的结果中,FN为在线好友数,第二次返回的数据基本没用。



(4).查看好友信息

  提交数据:VER=1.1&CMD=GetInfo&SEQ=标记&UIN=QQ号&LV=查询类型&UN=被查询QQ号码

  说明:LV=0,1为精简查询,LV=2为普通查询,LV>=3为详细查询



  返回:VER=1.1&CMD=GETINFO&SEQ=标记&UIN=QQ号&RES=0&LV=0&UN=106814&NK=Hackfan 好(精简查询)

     VER=1.1&CMD=GETINFO&SEQ=标记&UIN=QQ号&RES=0&AD
=地址&AG=19&EM=hackfan@qq.com&FC=0&HP=http:
//blog.hackfan.net&JB=学生

&LV=2&PC=邮编&PH=电话&PR=
The guy is updating to .NET Frameword......&PV=江苏&RN=胡吉阳&SC=
毕业院校&SX=0&UN=106814&NK=Hackfan 

好(普通查询)

     VER=1.1&CMD=GETINFO&SEQ=标记&UIN=QQ号&RES=0&AD
=地址&AG=19&BT=2&CO=6&CT=苏州&CV=%01&CY=中华人民共和国

&EM=hackfan@qq.com&FC=0&HP=http://blog.hackfan.net&ID
=-&JB=学生&LV=3&MO=136********&MT=0&MV=&PC=邮编&
PH=电话&PR=The guy is 

updating to .NET Frameword......&PV=江苏&RN=胡吉阳&SC=毕业院校&SH=3&SX=0&UN=106814&NK=Hackfan 好(详细查询)

     VER=1.1&CMD=GETINFO&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)

     NULL(UIN、LV、UN为字符)



  说明:AD为联系地址

     AG为年龄

     BT为血型

     CO为星座

     CT为城市

     CV为未知*

     CY为国家

     EM为Email

     FC为头像

     HP为网站

     ID为未知

     JB为职业

     LV为查询代码(就是发送的LV)

     MO为移动电话

     MT为未知

     MV为未知

     PC为邮编

     PH为联系电话

     PR为简介

     PV为省

     RN为真实姓名

     SC为毕业院校

     SH为生肖

     SX为性别

     UN为QQ号

     NK为昵称



     血型:0 => '',

        1 => 'A型',

        2 => 'B型',

        3 => 'O型',

        4 => 'AB型',

        5 => '其他'





     星座:0 => '',

        1 => '水瓶座',

        2 => '双鱼座',

        3 => '牡羊座',

        4 => '金牛座',

        5 => '双子座',

        6 => '巨蟹座',

        7 => '狮子座',

        8 => '处女座',

        9 => '天秤座',

        10 => '天蝎座',

        11 => '射手座',

        12 => '摩羯座'



     生肖:0 => '',

        1 => '鼠',

        2 => '牛',

        3 => '虎',

        4 => '兔',

        5 => '龙',

        6 => '蛇',

        7 => '马',

        8 => '羊',

        9 => '猴',

        10 => '鸡',

        11 => '狗',

        12 => '猪'



     性别:0 => '男',

        1 => '女'



(5).增加好友

  提交数据:VER=1.1&CMD=AddToList&SEQ=标记&UIN=QQ号&UN=对方QQ号



  返回:VER=1.1&CMD=AddToList&SEQ=标记&UIN=QQ号&RES=0&CD=0&UN=对方QQ号(允许被加为好友,此时他已经是你的好友)

     VER=1.1&CMD=AddToList&SEQ=标记&UIN=QQ号&RES=0&CD=1&UN=对方QQ号(需要验证)

     VER=1.1&CMD=AddToList&SEQ=标记&UIN=QQ号&RES=0&CD=2&UN=对方QQ号(决绝被加为好友)

     VER=1.1&CMD=AddToList&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)

     NULL(UIN、UN为字符)



(5).发送验证

  说明:1、如果你加对方为好友,你需要发送验证

     2、对方加你为好友,发送了验证,你要通过或者拒绝

     这2种情况需要发送验证消息



  提交数据:VER=1.1&CMD=Ack_AddToList&SEQ=标记&UIN=QQ号&UN=对方QQ号&CD=验证类型&RS=理由

  说明:CD为0表示“通过验证”,CD为1表示“拒决加为对方为好友”,CD为2表示“为请求对方加为好友”。



  返回:VER=1.1&CMD=Ack_AddToList&SEQ=标记&UIN=QQ号&RES=0(成功)

     VER=1.1&CMD=Ack_AddToList&SEQ=标记&UIN=QQ号&RES=3(*)

     VER=1.1&CMD=Ack_AddToList&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)

     NULL(UIN、UN、CD为字符,RS为非UTF-8字符)

  *如果服务器返回RES=3,那么这次对话的响应时间在20s。当发送验证请求的时候,必须连发2次(请求内容不必一样),其中一条RES=3,对方收不到,一条RES=0,对方能够收到。当CD>=3时,RES=3,响应时间20s。



(6).删除好友

  提交数据:VER=1.1&CMD=DelFromList&SEQ=标记&UIN=QQ号&UN=删除的QQ号



  返回:VER=1.1&CMD=DelFromList&SEQ=标记&UIN=QQ号&RES=0&(成功)

     VER=1.1&CMD=DelFromList&SEQ=标记&UIN=QQ号&RES=3(响应时间30s,重复发送的后果)

     VER=1.1&CMD=DelFromList&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)

     NULL(UIN、UN为字符)



(7).改变状态

  提交数据:VER=1.1&CMD=Change_Stat&SEQ=标记&UIN=QQ号&ST=状态代码

  说明:状态代码:10为上线,20为离线,30为忙碌,40为隐身,其他视为非法



  返回:VER=1.1&CMD=Change_Stat&SEQ=标记&UIN=QQ号&RES=0&(成功)

     VER=1.1&CMD=Change_Stat&SEQ=标记&UIN=QQ号&RES=3(失败,原因不明,响应时间20s,可能是过于频繁的改变状态引起的)

     VER=1.1&CMD=Change_Stat&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)

     NULL(UIN为字符,ST非法)



  特别说明:如果你改变好友,将会给所有好友发送一条系统信息,内容就是状态代码;如果隐身,发送的状态代码为20,表示离线。

       同理,当你的好友改变状态,你也会收到一条系统信息。



(9).获得消息

  提交数据:VER=1.1&CMD=GetMsgEx&SEQ=标记&UIN=QQ号



  返回:VER=1.1&CMD=GETMSGEX&SEQ=标记&UIN=QQ号&RES=0&
MN=4&MT=99,99,99,9,&UN=36791785,99833581,99833581,106814,&MG=
20,30,10,hi ,(MN表示信息数量,MT、UN、MG的值用","分割,分别表示消息类型、发送人号码、消息内容)

     VER=1.1&CMD=GETMSGEX&SEQ=标记&UIN=QQ号&RES=0&MN=0&MT=&UN=&MG=(表示没有信息)

     VER=1.1&CMD=GETMSGEX&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)

     NULL(UIN为字符)

  说明:关于MT:

       9为用户消息,99为系统消息,2为请求信息,3为通过验证,4为拒绝被加好友

     关于MG:

       当MT=9时,MG为用户发送的消息内容

       当MT=99时,

         MG=10(QQ_STATUS_ONLINE)表示对方上线

         MG=20(QQ_STATUS_OFFLINE)表示对方下线

         MG=30(QQ_STATUS_BUSY)表示对方进入忙碌状态

       当MT=2时,MG为对方请求你验证的信息

       当MT=3时,表示对方通过你的验证

       当MT=4时,MG为对方拒绝你理由



(10).发送消息

  提交数据:VER=1.1&CMD=CLTMSG&SEQ=标记&UIN=QQ号&UN=对方QQ号&MG=发送内容



  返回:VER=1.1&CMD=CLTMSG&SEQ=标记&UIN=QQ号&RES=0&(成功发送,对方不一定能收到哦)

     VER=1.1&CMD=CLTMSG&SEQ=标记&UIN=QQ号&RES=3(发送过快)

     VER=1.1&CMD=CLTMSG&SEQ=标记&UIN=QQ号&RES=20(没有正确登陆)

     NULL(UIN、UN为字符,MG含非UTF-8字符)

  说明:1、当你发消息时,以下情形对方可能看不到(其实是收到了,QQ不提示)你发送的消息:

       你俩互为陌生人,且对方没有和你说过话

       你在他的陌生人列表里,并且他没有和你说过话(没有验证)

     2、当你过快发送消息时,系统会给你一个惩罚,RES=3,相应时间20s

     3、当我发送含有小写字母h的信息时,服务器有可能返回NULL



(11).登出

  提交数据:VER=1.1&CMD=Logout&SEQ=标记&UIN=QQ号



  返回:VER=1.1&CMD=LOGOUT&SEQ=标记&UIN=QQ号&RES=0(成功,好像永远成功的,不管你是否登陆)

     NULL(UIN为字符)



5、总结



  通过对照以上的接口说明,我开发出了能够实现基本QQ功能的PHP类,它整合了以上所有的接口,使用更方便,可以开发QQ机器人、群发广告程序等。免费获得类的代码请到

  http://blog.hackfan.net/index.php?job=art&articleid=a_20050819_223558

  本文撰写时间仓促,难免有误,希望各位不吝赐教

2005年08月21日

ACCESS.CHM – Windows帮助文件
ACCSTAT.EXE – 辅助状态指示器
ADVAPI32.DLL – 高级Win32应用程序接口
AHA154X.MPD – SCSI驱动程序
AM1500T.VXT – 网卡驱动程序
AM2100.DOS – 网卡驱动程序
APPSTART.ANI – 动画光标
APPS.HLP – Windows帮助文件
AUDIOCDC.HLP – "易码编码解码器"帮助文件
AWARDPR32.EXE – 增加打印机工具
B ↑
BIGMEM.DRV – BIGMEM虚拟设备
BILLADD.DLL – 动态链接库(支持MSW)
BIOS.VXD – 即插即用BIOS接口
BUSLOGIC.MPD – SCSI驱动程序
C ↑
CALC.EXE – 计算器应用程序
CANNON800.DRV – 佳能打印机驱动程序
CHOICE.COM – MSDOS命令
CHS16.FON – 字体文件(16点阵中文)
CANYON.MID – MIDI文件例子
CARDDRV.EXE – PCMCIA支持程序
CDFS.VXD – CDROM文件系统
CDPLAYER.EXE – CD播放器应用程序
CDPLAYER.HLP – CD播放器帮助文件
CHIPS.DRV – 芯片技术显示驱动程序
CHKDSK.EXE – DOS磁盘检查工具
CHOOSUSR.DLL – 网络客户
CHOKD.WAV – 声音文件例子
CIS.SCP – 脚本文件(演示如何建立与Compuserve的PPP连接)
CLAIRE~1.RMI – MINI序列
CLIP.INF – 安装信息文件(剪粘板查看器)
CLOSEWIN.AVI – 影片剪辑(AVI)(如何关闭窗口)
CMC.DLL:Mail – API1.0公共信息调用
COMBUFF.VXD – COM端虚拟设备
COMCTL32.DLL – 32位Shell组件
COMDLG32.DLL – 32位公共对话库
COMIC.TIF – TrueType字体文件(Comic Sans Ms)
COMMAND.COM – 公共对话库
COMMDLG.DLL – 16位公共对话库
COMMON.HLP – OLE帮助文件
COMPOBJ.DLL – OLE16/32互操作库
CONAGEN.EXE – 32位控制支持
CONFAPI.DLL – Microsoft网络组件
CONFIG.SYS – 配置文件
CONFIG.TXT – 自述文件(配置文件中如何使用命令)
CONTROL.EXE – "控制面板"应用程序
COOL.DLL – 统一资源定位文件
COPY.INF – 安装信息文件
CP-1250.NLS – 自然语言支持文件
CPQNDIS.DOS – 网卡驱动程序
CPQNDIS3.VXD – Compaq以太控制器NDIS驱动程序
CR3240.EXE – DOS6.22中文版CR3240打印机驱动程序
CRTDLL.DLL – Microsoft C运行时间库
CSETUP.EXE – MSDOS6.22中文设置程序
CSETUP.WIN – CSetup.exe支持文件
CSMAPPER.SYS – 系统文件(支持PCMCIA)
CSPMAN.DLL – 动态链接库(SoundBlaster 16 Driver)
CTRLPAN.EXE – MSDOS命令(系统控制台程序)
CTRLPAN.EXE – MSDOS6.22中文版控制程序

D ↑
DBLBVFF.SYS – 双缓冲驱动程序
DC21X4.SYS – NDIS3驱动程序
DCIMAN.DLL – 显示控制接口
DCIMAN32.DLL – 显示控制接口
DDEML.DLL – DDE信息库
DEBMP.DLL – 光栅显示设备
DEBUG.EXE – Debug调试工具
DECPSMW4.INF – 安装信息文件(DEC打印机安装)
DECLAN.VXD – DECLAN网卡驱动程序
DEFRAG – 打开"选定驱动器"窗口
DEL.INF – 安装信息文件
DELTEMP.COM – 初始化帮助工具
DELTREE.EXE – 删除目录工具
DEMET.DLL – 向量显示工程
DESKCP16.DLL – 16位桌面控制面板
DESKTOP.MSN – Microsoft网络组件
DESS.DLL – 表格显示工程
DEWP.DLL – 字处理显示工程
DIALER.CNT – 对话帮助
DIALER.EXE – 电话拨号程序
DIALER.HLP – 电话拨号帮助文件
DIALMON.EXE – 拨号监视程序(IE2.0)
DIBENG.DLL – 独立设备的位同工程
DICONIX.DRX – 打印机驱动
DING.WAN – 声音文件例子
DIRECTCC.EXE – 直接线缆连接应用程序
DISKCOMP – 磁盘比较工具
DISKCOPY.COM – 磁盘拷贝工具
DISKDRV.INF – 安装信息
DISPLAY.TXT – 显示卡README文件
DMCOLOR.DLL – 通用打印驱动程序彩打支持库
DOSKEY.COM – DOS命令
DOSX.EXE – MSDOS配置程序
DRAGDROP.AVI – 影片剪辑(AVI)(如何使用拖拽)
DRIVER.SYS – DOS驱动程序
DRVSPACE.EXE – 磁盘压缩工具
DRVSPACE.HLP – 磁盘空间管理帮助文件

E ↑
EDIT.COM – DOS文字编辑程序
EDLIN.EXE – DOS行编辑器
EE16.VXD – 虚拟设备驱动程序
EISA.VXD – 即插即用EISA总线计数器
EK550C.ICM – 打印机简介
EMM386.EXE – 扩展内存管理程序
ENABLE.INF – 初始化信息
ENGCT.EXE – MSN支持文件
ESCP24SC.DRV – 设备驱动程序
EUDCEDIT.CNF – 帮助索引文件(造字程序)
EUDCEDIT.EXE – 造字程序
EUDCEDIT.HLP – 帮助文件(造字程序)
EUDCEDIT.INF – 安装信息文件(造字程序)
EVX16.DOS – 网卡驱动程序
EWRK3.DOS – 网卡驱动程序
EWRK3.SYS – 网卡驱动程序
EXCEL.XLS – Excel5.0文件模板
EXCEL4.XLS – Excel4.0文件模板
EXCHANGE.TXT – Inbox和Exchange的自述文件
EXCHNG.CNT – Mail/Exchange帮助文件内容
EXCHNG.HLP – Mail/Exchange组件
EXCHNG32.EXE – 对用户的交换机作初始设置
EXPLORER.AVI – 影片剪辑(AVI)(如何使用资源管理器)
EXPLORER.EXE – "资源管理器"应用程序
EXPO.HLP – 帮助文件(产品信息)
EXPOSTRT.EXE – 产品信息应用程序
EXTRACT.EXE – 解压缩工具
EXTRA.TXT – 自述文件(联机访问附加文件)

F ↑
FAQ.TXT – 疑难解答自述文件
FAXCODEC.DLL – 传真编码/译码器
FAXCOVER.EXE – 封面编辑器
FC.EXE – DOS命令,比较两个文件
FD16-700.MPD – SCSI驱动程序
FD8XX.MPD – SCSI驱动程序
FDISK.EXE – DOS命令,在硬盘上建立、删除及显示当前分区
FILESEC.VXD – 文件存取控制管理器
FILEXFER.CNT – 文件传输帮助文件内容
FILEXFER.EXE – Microsoft文件传输
FIND.AVI – 影片剪辑(如何使用查找)
FIND.EXE – 寻找指定字符串命令
FINDMVI.DLL – 媒体视觉支持
FINSTALL.DLL – 字库安装程序
FINSTALL.HLP – 字库安装帮助文件
FLSIMTD.VXD – PCMCIA支持
FLSIMTD.VXD – PCMCIA支持
FONT16.EXE – DOS6.22中文版16点阵字体驱动程序
FONTS.INF – 字体选择初始化信息
FONTVIEW.EXE – 字体浏览程序
FORMAT.COM – DOS磁盘格式化工具
FOUTLINE.EXE – 轮廓字体驱动程序
FRAMEBUF.DRV – SVGA显示器驱动程序
FTE.DLL – 声音浏览文件传输工程文件
FTP.EXE – 文件传输协议TCP工具
FURELI~1.RMI – MINI序列
G ↑
GBK.TXT – 中文Windows95GBK代码集字符定义表
GDI.EXE – 简版WIN3.1图形界面
GDI32.DLL – 32位GDI图形界面
GENERAL.IDF – 一般MIDI指示器
GRPCONV.EXE – Windows程序组转换器
GUIDE.EXE – 应用程序(MSN)

H ↑
HARDWARE.TXT – 硬件自述文件
HOSTS.SAM – TCP配置
HPCLRLSK.ICM – 打印简介
HPDESK.ICM – 打印机简介表
HPDSKJET.DRV – 打印机驱动程序
HPEISA.VXD – 网络适配器驱动程序
HPJAHLP.CNT – JetAdmin程序帮助文件
HPJD.DLL – HPJetAdmin支持程序
HPLAN.DOS – 网络适配器驱动程序
HPLJ300.DRV – HPLJ300DPI打印机驱动程序
HPLJ300.EXE – MSDOS命令(HP打印机驱动)
HPLJ-31.SPD – 打印机驱动程序
HPLJ600.DRV – HPLJ600DPI打印机驱动程序
HPLJP-V4.INF – 打印机安装信息
HPNETPRN.INF – HPJetAdmin支持程序
HPPJXL31.SPD – 打印机驱动程序
HPPLOT.DRV – 打印机驱动程序
HPPLOT.HLP – 打印机驱动程序帮助文件
HPPRARBK.DLL – HPJetAdmin支持程序
HPPRARRK.HLP – HPJetAdmin支持程序帮助文件
HPVCM.HPM – 打印机驱动程序
HSFLOP.PDR – HSFLOP虚拟设备
HTICONS.DLL – 终端设备动态链接库
HYPERTRM.CNT – 终端设备帮助文件
HYPERTRM.EXE – 终端设备应用程序
HYPERTRM.HLP – "超级终端"帮助
HZKBD.EXE – 常用输入方法程序
HZVIO95.EXE – 显示驱动程序

I ↑
I82593.DOS – 网络适配器驱动程序
IB401917.SPD – 打印机驱动程序
IBM20470.SPD – 打印机驱动程序
IBM20K.DOS – 网络适配器驱动程序
ICM32.DLL – 图象颜色匹配程序
ICMOI.DLL – 用户界面颜色匹配程序
ICONLIB.DLL – 图符库
IEXPLORE.CNT – 帮助索引文件(IE)
IEXPLORE.EXE – InternetExplore
IEXPLORE.HLP – 帮助文件(IE)
IFSHLP.SYS – 文件系统安装帮助文件
IFSMGR.VXD – 文件系统安装管理程序
IMAGEOIT.EXE – 图象编辑器光标程序
IMCLIENT.DLL – Microsoft网络组件
IME.CNT – 帮助索引文件(中文输入法)
IME.HLP – Windows帮助文件
IME.INF – 安装信息文件(中文输入法)
IMEGEN.CNF – 帮助索引文件(输入法生成器)
IMEGEN.EXE – 输入法生成器
IMEGEN.HLP – 帮助文件(输入法生成器)
IMEINFO.INI – 输入法初始化文件
IMM32.DLL – WIN32IMM应用程序界面
INBOX.EXC – 邮件组件
INDICDLL.DLL – 多语言组件
INET.TXT – IE自述文件
INET16.DLL – 动态链接库(支持IE2.0)
INETAB32.DLL – 动态链接库(支持Internet mail)
INETCFG.DLL – 动态链接库(支持IE2.0)
INETCPL.CPL – 控制面板文件(配置IE2.0)
INETMAIL.INF – 安装信息文件(Internet mail)
INETWIZ.EXE – Internet安装向导
INFORMS.WPF – 样板文件
INSTBE.BAT – Microsoft网络组件
INSTDICT.EXE – MSDOS命令(输入法安装程序)
INTB.VXD – 13号中断虚拟设备
INTL.CPL – 控制面板
INT-MAIL.CNT – 帮助索引文件(Internet mail)
IOS.INI – 设置需要安全保护的程序
IOSCLASS.DLL – CDROM安装程序
IRMATR.DOS – 网络适配器驱动程序
ISAPNP.VXD – ISA总线即插即用程序
 ↑
JOY.CPL – 游戏杆控制面板
JOYSTICK.INF – 多媒体安装信息
JP350.DRV – 打印机驱动程序
JUNGLE~1.WAV – 声音文件

K ↑
KBDBE.KBD – 比利时键盘格式
KBDBR.KBD – 巴西键盘格式
KBDCA.KBD – 法国、加拿大键盘格式
KBDOS.KBD – 美国键盘格式
KDCOLOR1.SPD – 打印机驱动程序
KERNEL32.DLL – 32位内核
KEYB.COM – 将控制键盘程序装入内存
KODAKCE.ICM – 柯达ICC配置文件
KRNL386.EXE – Core应用程序

L ↑
LABEL.EXE – DOS命令,设置磁盘名称
LFNBK.EXE – 长文件名备份文件
LFNBK.TXT – LFNBK的自述文件
LICENSE.HLP – Windows帮助文件
LMSCRIPT.EXE – LAN管理器文稿处理程序
LOGIN.EXE – Win95登录NetWare文件
LQ1600K.EXE – LQ1600K打印驱动程序

M ↑
MAILMSG.DLL – 微软网络组件
MAILOPT.INF – MAIL/MAPI设置文件
MAPI.DLL – Mail/Exchange组件
MCIAVI.DRV – 多媒体驱动程序
MCICDA.DRV – MCICD声音驱动程序
MCIOLE.DLL – MCIOLE句柄
MCIPIONR.DRV – MCI光盘驱动程序
MCISEQ.DRV – MCI定序器驱动程序
MCIVISCA.DRV – MCIVCR驱动程序
MCIWAVE.DRV – MCI Ware驱动程序
MDMNOKIA.INF – 安装信息文件(modem)
MDMNOVA.INF – 安装信息文件(modem)
MDMVV.INF – 安装信息文件(modem)
MEMMAKER.EXE – 内存管理程序
MEMMAKER.INF – 内存管理程序设置信息
MFCUIA32.DLL – OLEI公共对话动态链接库
MIDI.INF – 即插即用MIDI设备信息
MINET32.DLL – 支持Internet Mail动态链接库
MKECR5XX.MPD – SCSI驱动程序
ML3XEC16.EXE – 应用程序(MAPI)
MLSHEXT.DLL – 微软核扩展库
MMCI.DLL – 媒体类安装程序
MMDEVLDR.VXD – 即插即用设备装载程序
MMDRV.HLP – 多媒体帮助文件
MMSOUND.DRV – 多媒体驱动程序
MMSYSTEM.DLL – 多媒体系统内核
MMTASK.TSK – 多媒体背景任务交换器
MODE.COM – DOS命令
MODERN.FON – 字体文件(modem)
MORE.COM – DOS命令
MOUSE.DRV – 鼠标驱动程序
MOVEWIN.AVI – 影片剪辑(如何移动窗口)
MPLAYER.EXE – 媒体播放程序
MPR.DLL – WIN32网络接口动态链接库
MSAB32.DLL – 微软网络地址簿
MSBASE.INF – 设置信息
MSCDEX.EXE – DOS MSCDEX CDROM扩展工具
MSCDROM.INF – 类安装设置信息
MSD.EXE – 微软诊断工具
MSD.INI – 微软诊断初始化
MSDET.INF – 系统检测设置信息
MSDISP.INF – 显示设置信息
MSDLG.EXE – 数据链接控制协议
MSDOS.INF – 设置信息
MSDOSDRV.TXT – 设备驱动程序自述文件
MSFT.VRL – 统一资源定位文件
MSGSRV32.EXE – Windows32位虚拟设备信息系统
MSHDC.INF – 硬盘控制设置信息
MSJSTICK.DRV – 即插即用游戏杆驱动程序
MSMAIL.INF – Mail/MAPI初始化
MSMOUSE.INF – 鼠标设置信息
MSN.TXT – 微软网络自述文件
MSNET32.DLL – 微软32位网络API库
MSNEXCH.EXE – 微软网络设置程序
MSNPSS.HLP – 微软网络帮助文件
MSNVER.TXT – 微软网络帮助信息
MSPAINT.EXE – 画图工具
MSPCIC.DLL – PCMCIA类安装与控制工具
MSPORTS.INF – 公共设置信息
MSPP32.DLL – 微软网络打印支持程序
MSPWL32.DLL – 口令清单管理库
MSSBLST.DRV – 声霸卡驱动程序
MSSBLSI.VXD – 声霸卡驱动程序
MSSHRVI.DLL – 共享内核扩展程序
MSSNDSYS.DRV – Windows声音系统驱动程序
MSSP.VXP – Windows NT安全支持
MSTCP.DLL – TCP用户界面
MSVIEWUT.DLL – 显示设备服务数据链接库
MTMMINIP.MPD – SCSI驱动程序
MULLANG.INF – 多种语言字体支持设置信息
MVIWAVE.DRV – 声音驱动程序
N ↑
NBTSTAT.EXE – TCP工具
NDDEAPI.DLL – Workgroups DDE共享接口
NDDENB.DLL – 微软网络DDE NetBIOS接口
NDISHLP.SYS – 实模式NDIS支持驱动程序
NET.EXE – 实模式网络客户软件
NET.INF – 网络检测信息
NET.MSG – 网络客户信息
NET3COM.INF – 网络设置信息
NETAMD.INF – 网络设置信息
NETAPI.DLL – 网络应用程序接口动态链接库
NETAPI32.DLL – 32位网络API动态链接库
NETAVXT.INF – MS内部传输文件
NETBEUI.VXD – 32位NetBEUI协议
NETBIOS.DLL – NetBIOSAPI库
NETDCA.INF – 安装信息文件
NETDDE.EXE – Windows网络动态数据交换
NETDET.INI – NetWare检测文件
NETDI.DLL – 网络设备安装
NETH.MSG – 网络客户帮助信息
NETOS.DLL – NOS检测DLL
NETWATCH.EXE – 网络观测程序
NETWORK.TXT – 网络信息自述文件
NOTEPAD.EXE – 记事本应用程序
NODRIVER.INF – 即插即用设备信息
NOTEPAD.EXE – NOTEPAD文件
NSCL.VXD – NSCL虚拟设备
NW16.DLL – NetWare客户
NWAB32.DLL – 地址簿支持动态链接库
NWLSCON.EXE – 登录文稿控制台程序
NWLSPROC.EXE – NetWare登录处理器
NWNET32.DLL – NetWare客户
NWNP32.DLL – NetWare组件
NWREDIR.VXD – NetWare重定向
NWSERVER.VXD – NCP服务
NWSP.VXD – NCP服务安全提供

O ↑
OEMREVA.INF – 安装信息文件
OLE2.DLL – OLE2.0动态链接库
OLE2.INF – OLE设置信息
OLE32.DLL – 32位OLE2.0组件
OLEAUT32.DLL – OLE2-32自动化
OLECL1.DLL – 对象链接与嵌入客户库
OLEDLG.DLL – Windows OLE2.0用户接口支持
OLESVR.DLL – 对象链接与嵌入服务端库
OLETHK32.DLL – OLE形实替换程序库

P ↑
PACKAGER.EXE – 对象包装程序
PARALINK.VXD – 远程网络存取并行口驱动程序
PBRVSH.EXE – "画图"应用程序
PDOS95.BAT – 进入中文DOS状态
PERF.VXD – 系统性能监视器
PIFMGR.DLL – 程序信息文件管理服务程序
PING.EXE – TCPPing工具
PMSPL.DLL – LAN管理应用程序接口
POWER.DRV – 高级电源管理驱动程序
PPPMAC.VXD – Windows虚拟PPP驱动程序
PRINT.EXE – DOS打印文件
PRINTERS.TXT – 打印信息自述文件
PROGMAN.EXE – 程序管理器
PRTVPD.INF – 打印机升级设置信息

Q ↑
QUIKVIEW.EXE – 快速查看
QUIT.EXE – 退出中文DOS状态

R ↑
README.TXT – Windows95自述文件
REGEDIT.EXE – 注册编辑器
REGSERV.EXE – 远程注册
REGWIE.EXE – 注册工具
REGSERV.INF – 远程注册
RESTORE.EXE – DOS命令
RNAAPP.EXE – 拨号网络应用程序
RNASERV.DLL – 远程网络存取服务
RNASETUP.DLL – 远程网络存取设置动态链接库
RNATHUNK.DLL – 远程网络存取转换支持动态链接库
RNAUI.DLL – 远程网络存取用户接口DLLRNDSRV32.DLL复制服务程序
ROBOTZCL.WAV – 声音文件
ROBOTZWI.WAV – 声音文件
ROMAN.FON – 字型文件
ROUTE.EXE – TCP/IP ROUTE命令
RPCLTC1.DLL – 远程调用库
RPCNS4.DLL – 远程调用库
RPCPP.DLL – 远程调用打印驱动
RPCRT4.DLL – 远程调用库
RPCSS.EXE – 远程调用结点映象
RPLBOOT.SYS – 远程程序装入
RPLIMAGE.DLL – 远程程序装入磁盘映象器
RSRC16.DLL – 资源计量器
RSRCMTR.EXE – 资源计量器
RSRCMTR.INF – 资源计量器
RUMOR.EXE – DDE测试/游戏
RUNDLL.EXE – 把DLL作为应用程序运行
RUNDLL32.EXE – 32位壳组件
S ↑
S3.DRV – S3显示驱动
S3.VXD – S3虚拟设备
SACLIEN.DLL – Microsoft网络组件
SAMPLEVIDEOS – 图象文件
SAPNSP.DLL – Winsock数据连接库
SAVE32.COM – 安装时所需的TSR文件
SB16.VXD – 16位声卡虚拟设备
SB16SND.DRV – 16位声卡驱动
SBAWE.VXD – AWE声卡虚拟设备
SBAWE32.DRV – AWE声卡驱动
SBFM.DRV – 16位声卡驱动
SCANDISK.BAT – MSDOS6.x Scandisk的替代存根模块SCANDISK.BAT磁盘诊断工具
SCANDISK.INI – 磁盘诊断工具
SCANDISK.PIF – 安装磁盘诊断工具时的PIF文件
SCANDSKW.EXE – 磁盘扫描工具
SCANPROG.EXE – 磁盘扫描工具
SCRNSAVE.SCR – 屏幕保护
SCSI.INF – SCSI安装文件文件名描述
SCSIIHLP.VXD – SCSI支持文件
SCSIPORT.PDR – SCSI虚拟设备口
SECUR32.DLL – Microsoft Win32安全服务
SECURCL.DLL – Microsoft网络组件
SEIKO24E.DRV – 打印机驱动
SEIKOSH9.DRV – 打印机驱动
SERIAL.VXD – 串口VCOMM驱动器
SERIFE.FON – 字型文件
SERVER.HLP – 服务器帮助文件
SETMDIR.EXE – SBS文件
SETUP.BIN – 安装支持文件
SETUP.BMP – 安装Wash位图文件
SETUP.EXE – Windows95安装程序
SETUP.INF – 安装信息文件
SETUP.TXT – 安装时的README文件
SETUP4.DLL – 安装支持文件
SETUPPP.INF – 安装信息
SETUPX.DLL – 安装支持
SETVER.EXE – MSDOS版本显示,该程序可在网络上执行
SF4029.EXE – 打印机驱动
SHARE.EXE – MSDOS共享实用程序
SHELL.INF – 安装壳信息
SHELL.VXD – 虚拟壳设备
SHELL2.INF – 颜色组合
SHELL3.INF – 颜色组合
SIZE1-1.CUR – 光标
SIZE1-M.CUR – 光标
SIZE4-M.CUR – 光标
SIZENESW.ANI – 活动光标
SIZEWE.ANI- 活动光标
SKPSFA-1.SPD – 打印机驱动
SLAN.DOS – 网络适配器驱动
SLCD32.MPD – SCSI驱动器
SLENH.DLL – 高级节能选项
SMALLE.FON – 字型文件
SMALLF.FON – 字型文件
SMARTDRV.EXE – 超高速缓存程序
SMARTND.DOS – 网络适配器驱动器
SMC3000.DOS – 网络适配器驱动器
SMC9000.VXD – 网络适配器驱动器
SNAPSHOT.EXE – 抽点
SNAPSHOT.VXD – 抽点虚拟设备
SNDREC32.EXE – 录音机
SNIP.VXD – 网络适配驱动器
SOCKET.VXD – Windows虚拟Socket网卡驱动器SOCKET.VXD PCMCIA支持
SOL.CNT – 纸牌游戏
SOL.HLP – 纸牌游戏帮助文件
SORT.EXE – MSDOS分类实用程序
SOUNDREC.CNT – 录音机帮助文件内容
SOUNDREC.HLP – 录音机帮助文件
SPARROW.WPD – SCSI驱动器
SPARROWX.MPD – SCSI驱动器
SPOOL32.EXE – 打印机支持
SPOOLER.VXD – 打印机共享虚拟设备
SRAMMTD.VXD – PCMCIA支持
SSERIFE.FON – 字型文件
SSERIFF.FON – 字型文件
SSFLYWIN.SCR – 屏幕保护
SSSTARS.SCR – 屏幕保护
STAR24E.DRV – 打印机驱动
STAR9E.DRV – 打印机驱动
START.EXE – 启动程序
STATE.PBK – Microsoft网络组件
STDOLE.TLB – OLE2.0文件
STDOLE32.TLB – OLE2-32文件
STEMO409.DLL – Windows95帮助文件的DLL
STLSO4SS.SPD – 打印机驱动程序
STLS577U.SPD – 打印机驱动程序
STORAGE.DLL – OLE存储器管理库
STRN.DOS – 网络适配器驱动
SUBST.EXE – MSDOS Subst实用程序
SUEXPAND.DLL – LZ DLL安装
SUHELPER.BIN – 安装支持
SUPERVGA.DRV – 高级VGA显示驱动
SURPORT.TXT – PSS支持信息
SVCPROP.DLL – Microsoft网络组件
SVRAPI.DLL – 32位公用服务器API实用程序
SXCIEXT.DLL – Matrox显示驱动支持文件
SYMBOLE.FON – 字型文件
SYS.COM – MSDOS系统实用程序
SYSCLASS.DLL – 系统类库安装
SYSDETMG.DLL – 系统检测库
SYSEDIT.EXE – 系统编辑器
SYSLOGO.RLE – 系统标识
SYSMON.EXE – 系统监控程序
SYSMON.HLP – 系统监控帮助
SYSTEM.DRV – 最小Win3.1标准模式
SYSTHUNK.DLL – Windows系统形实替换程序库
SYSTRAY.EXE – 高级节能管理

T ↑
T128.MPD – SCSI驱动器
T160.MPD – SCSI驱动器
T20N3.VXD – 网络适配驱动器
T30ND.DOS – 网络适配驱动器
T338.MPD – SCSI驱动器
TADA.WAV – 声音文件
TAPI.DLL – API通话程序
TAPI.INF – API通话安装信息文件
TAPI32.DLL – 32位形实替换
TAPIADDR.DLL – API通话程序
TAPIEXE.EXE – API通话组件
TAPIINI.EXE – API通话组件
TASKMAM.EXE – 任务管理器
TCCARC.DOS – 网络适配驱动器
TCTOKCH.VXD – 网络适配驱动器
TELEPHON.CPL – 通话帮助
TESTPS.TXT – PostScript测试
TEXTCHAT.EXE – Microsoft网络组件
THEMIC-1.WAV – 声音文件
THINKJET.DRV – 打印机驱动
THREED.VBX – Windows95浏览
T1850.DRV – 打印机驱动
TIMEDATE.CPL – 时间/日期控制面板
TIMES.TTF – 时间字型
TIMESBD.TTF – 时间粗体字型
TIMESBI.TTF – 时间粗斜体字型
TIMESI.TTF – 时间斜体字型
TIMEZONE.INF – 安装信息
TIMLP232.SPD – 打印机驱动
TIPS.txt – 提示和技巧自述文件
TKPHZR32.SPD – 打印机驱动
TLNK.DOS – 网络适配驱动器
TLNK3.VXD – 网络适配驱动器
TMV1.MPD – SCSI驱动器
TOOLHELP.DLL – 16位开发工具帮助器
TOSHIBA.DRV – 打印机驱动
TOUR.EXE – 浏览文件
TPHAIII.ICM – 打印机简介
TRACERT.EXE – TCP/IP IRACEROUTE命令
TREE.COM – MS DOS树实用程序
TREEEDCL.DLL – Microsoft网络组件
TREENVCL.DLL – Microsoft网络组件
TRIUMPHI.SPD – 打印机驱动
TSD32.DLL – 声音压缩管理器
TSENG.DRV – ET4000W32显示驱动
TTY.DRV – 打印机驱动
TTY.HLP – TTY打印机驱动帮助
TYPELIB.DLL – OLE2.0

U ↑
U9415470.SPD – 打印机驱动
UBNEI.DOS – 网络适配器驱动
ULTRA124.MPD – SCSI驱动器
ULTRA24F.MPD – SCSI驱动器
UMDM16.DLL – 通用调制解调器驱动组件
UMDM32.DLL – 通用调制解调器驱动组件
UNIDRV.DLL – Microsoft通用打印机驱动库
UNIDRV.HLP – 通用打印机驱动帮助
UNIMODEM.VXD – 通用调制解调器驱动
USER32.DLL – 32位用户

V ↑
V86MMGR.VXD – V86MMGR虚拟设备
VCACHE.VXD – VCache虚拟设备
VCD.VXD – 虚拟COM驱动程序
VCOMM.VXD – VCOMM驱动程序
VCOND.VXD – Win32控制台
VDMAD.VXD – VDMAD虚拟设备
VER.DLL – 小型Win3.1安装程序16位版动态链接库
VER.NEW – 版本检测与文件安装库
VERSION.DLL – 32位版本动态链接库
VERX.DLL – 安装程序使用的版本动态库
VFAT.VXD – VFAT文件系统
VFD.VXD – 软盘虚拟设备
VFLATD.VXD – 虚拟平板帧缓存虚拟设备
VGA.DRV – VGA显示驱动程序
VIDCAP.INF – 即插即用VCD信息
VIDEOT.VXD – 视频虚拟设备
VIP.386 – TCP/IP虚拟IP设备
VJOYD.VXD – 游戏棒虚拟设备
VKD.VXD – 虚拟键盘设备
VLB32.DLL – Mail/Exchange组件
VMD.VXD – Win3.1虚拟鼠标驱动程序
VMM.VXD – 虚拟存储管理设备
VMM32.VXD – 虚拟存储管理设备
VMOUSE.VXD – 虚拟鼠标驱动程序
VNBT.386 – NetBIOS传输驱动程序
VNETBIOS.VXD – VNETBIOS虚拟设备
VNETSUP.VXD – 网络支持虚拟设备
VPD.VXD – 虚拟LPT驱动程序
VPICD.VXD – 虚拟可编程干扰控制器设备
VPOWERD.VXD – 高级电源管理虚拟设备
VREDIR.VXD – Microsoft网络32位客户端程序
VSAMI.DLL – AMI文件语法分析程序
VSASC8.DLL – ASCII文件语法分析程序
VSBMP.DLL – BMP文件语法分析程序
VSERVER.VXD – Microsoft网络32位服务器端程序
VSGIF.DLL – GIF文件语法分析程序
VSHARE.VXD – 32位共享虚拟设备驱动程序
VSMSW.DLL – Win写文件语法分析
VSPP.DLL – PowerPoint语法分析程序
VSRTF.DLL – RTF文件语法分析程序
VSTIFF.DLL – TIFF文件语法分析程序
VSW6.DLL – Word6文件语法分析程序
VSWORD.DLL – Word文件语法分析程序
VSWP5.DLL – WordPerfect5文件语法分析程序
VSXL5.DLL – Excel文件/图表语法分析程序
VTCP.386 – TCP/IP虚拟TCP驱动程序
VTDAPI.VXD – VTDAPI虚拟设备
VTDI.386 – 传输驱动接口支持程序
VXDLDR.VXD – 虚拟设备驱动程序装载器

W ↑
WAVE.INF – 即插即用音波设备信息
WDTOOOEX.MPD – SCSI驱动
WGPOADMN.DLL – Mail/Exchange组件
WHLP16T.DLL – 帮助动态链接库
WIN87EM.DLL – 80387数学仿真库
WINABC.HLP – 智能ABC帮助文件
WINBX.HLP – 表形码输入法帮助文件
WINCHA.HLP – 繁体仓颉输入法帮助文件
WINDOWS.CNT – Windows95帮助文件内容
WINDOWS.HLP – Windows95帮助文件
WINFILE.CNT – 文件管理器帮助文件内容
WINFILE.EXE – Windows工作组文件管理器
WINFILE.HLP – 文件管理器帮助文件
WINGB.HLP – 区位码输入法帮助文件
WINHLP23.HLP – Windows帮助文件
WINIME.HLP – 操作指南帮助文件
WINNM.HLP – GBK内码输入法帮助文件
WININIT.EXE – Windows初始化文件
WINIPCFG.EXE – TCP/IP配置工具
WINNEWS.TXT – Winnews信息
WINPHO.HLP – 繁体注音输入法帮助文件
WINPOPUP.EXE – POPUP工具
WINREG.DLL – 远程注册支持
WINPY.HLP – 全拼输入法帮助文件
WINSOCK.DLL – Windows的套接API
WINSY.HLP – 双拼输入法帮助文件
WINXSP.HLP – GBK双拼输入法帮助文件
WINXZM.HLP – GBK郑码输入法帮助
WINZM.HLP – 郑码输入法帮助文件
WNASPI32.DLL – Windows DLL32位ASPI
WPSUNI.DRV – 传真驱动程序
WPSUNIRE.DLL – WPS主机资源执行程序

X ↑
XCOPY.EXE – DOS XCOPY工具
XCOPY32.EXE – 文件拷贝程序
XGA.DRV – XGA显示驱动程序

根据市场调研机构NPD Group日前公布的调查显示,7月份全球技术分销委员会(Global Technology Distribution Council,GTDC)的笔记本和台式机销量分别达到149,797台和149,276台,笔记本销量首次超过台式机。
与去年同期相比,笔记本电脑销售量增长了14.1%,而台式机下降了0.7%。

  NPD产业分析主管Stephen Baker表示,“笔记本销量超过台式机,这是全球的发展趋势,笔记本的需求量将迅速增长,尤其是在今年下半年。小型企业将在今年夏季末期雇佣新的员工,这将促进笔记本电脑的销量。厂商最终的竞争将在笔记本电脑领域展开,现在惠普和宏基就在该领域进行激烈的竞争,而索尼正试图在SMB领域占据一席之地。”

  由于笔记本电脑的平均售价要比台式机高,其销售额也远远超过台式机。今年7月份,笔记本电脑的销售额达到2.161亿美元,合平均每台1442.66美元,而台式机为1.139亿美元,合每台762.98美元,同比分别下降了11.8%和5%。

  Phoenix方案提供商InterTech公司总裁Mike Novotny表示,“笔记本和台式机的价格差正逐步扩大,员工频繁地外出工作,这意味着笔记本电脑需求量的扩大。而笔记本电脑的价格持续下降,与相同价格的台式机相比,入门级笔记本电脑并不难买到。”尽管该公司目前的台式机销售量仍高于笔记本电脑,其目标是在今后销售更多的笔记本电脑。

  解决方案提供商Network Designs Integration Services首席执行官Angie Wong表示,现在该公司笔记本电脑和台式机销量的比例达到5:1。购买台式机的仅仅是那些需要专业图形卡等配件的高端用户,比如工程师和绘图师等等。

  Synnex公司总裁兼首席运营官John Paget表示,“笔记本电脑日渐成为更加必要的个人工具,大部分公司员工在家里都有宽带接入,越来越多的人需要无线上网。目前,英特尔公司也致力于无线宽带技术的推广,倡导城市无线接入。”