一,WinSock简介
Socket(套接字)最初是由加利福尼亚大学Berkeley(伯克利)分校为UNIX操作系统开发的网络通信接口,随着UNIX的广泛使用,Socket成为当前最流行的网络通信应用程序接口之一。20世纪90年代初,由Sun Microsystems,JSB,FTP software,Microdyne和Microsoft等几家公司共同定制了一套标准,即Windows Socket规范,简称WinSock。

VB编写网络程序主要有两种方式:1.winsock控件 2.winsockAPI

二,WinSock控件的使用
1.WinSock控件的主要属性
a.Protocol属性
通过Protocol属性可以设置WinSock控件连接远程计算机使用的协议。可选的协议是TCP和UDP对应的VB的常量分别是sckTCPProtocol和sckUDPProtocol,Winsock控件默认协议是TCP。注意:虽然可以在运行时设置协议,但必须在连接未建立或断开连接后。

b.SocketHandle属性
SocketHandle返回当前socket连接的句柄,这是只读属性。

c.RemoteHostIP属性
RemoteHostIP属性返回远程计算机的IP地址。在客户端,当使用了控件的Connect方法后,远程计算机的IP地址就赋给了RemoteHostIP属性,而在服务器端,当ConnectRequest事件后,远程计算机(客户端)的IP地址就赋给了这个属性。如果使用的是UDP协议那么当DataArrival事件后,发送UDP报文的计算机的IP才赋给了这个属性。

d.ByteReceived属性
返回当前接收缓冲区中的字节数

e.State属性
返回WinSock控件当前的状态

常数 值 描述
sckClosed 0 缺省值,关闭。
SckOpen 1 打开。
SckListening 2 侦听
sckConnectionPending 3 连接挂起
sckResolvingHost 4 识别主机。
sckHostResolved 5 已识别主机
sckConnecting 6 正在连接。
sckConnected 7 已连接。
sckClosing 8 同级人员正在关闭连接。
sckError 9   错误


2.WinSock主要方法
a.Bind方法
用Bind方法可以把一个端口号固定为本控件使用,使得别的应用程序不能再使用这个端口。

b.Listen方法
Listen方法只在使用TCP协议时有用。它将应用程序置于监听检测状态。

c.Connect方法
当本地计算机希望和远程计算机建立连接时,就可以调用Connect方法。
Connect方法调用的规范为:
Connect RemoteHost,RemotePort

d.Accept方法
当服务器接收到客户端的连接请求后,服务器有权决定是否接受客户端的请求。

e.SendData方法
当连接建立后,要发送数据就可以调用SendData方法,该方法只有一个参数,就是要发送的数据。

f.GetData方法
当本地计算机接收到远程计算机的数据时,数据存放在缓冲区中,要从缓冲区中取出数据,可以使用GetData方法。GetData方法调用规范如下:
GetData data,[type,][maxLen]
它从缓冲区中取得最长为maxLen的数据,并以type类型存放在data中,GetData取得数据后,就把相应的缓冲区清空。

g.PeekData方法
和GetData方法类似,但PeekData在取得数据后并不把缓冲区清空。


3.Winsock控件主要事件

a.ConnectRequest事件
当本地计算机接收到远程计算机发送的连接请求时,控件的ConnectRequest事件将会被触发。

b.SendProgress事件
当一端的计算机正在向另一端的计算机发送数据时,SendProgress事件将被触发。SendProgress事件记录了当前状态下已发送的字节数和剩余字节数。

c.SendComplete事件
当所有数据发送完成时,被触发。

d.DataArrival事件
当建立连接后,接受到了新数据就会触发这个事件。注意:如果在接受到新数据前,缓冲区中非空,就不会触发这个事件。

e.Error事件
当在工作中发生任何错误都会触发这个事件。

例子见附件



三,WinSockAPI的使用

1.WSAStartup 函数

为了在你的应用程序当中调用任何一个Winsock API 函数,首先第一件事情你就是必须通过WSAStartup函数完成对Winsock 服务的初始化,因此需要调用WSAStartup函数。

Declare Function WSAStartup Lib "ws2_32.dll" _
(ByVal wVersionRequired As Long, lpWSAData As WSAData) As Long

这个函数有两个参数: wVersionRequired 和 lpWSAData。wVersionRequired 参数定义Windows Sockets 提供能使用的最高版本,它的高位字节定义的是次版本号,低位字节定义的是主版本号。下面的2个Winsock版本在VB中使用的例子:

初始化1.1版本

lngRetVal = WSAStartup(&H101, udtWinsockData)


初始化2.2版本

lngRetVal = WSAStartup(&H202, udtWinsockData)


第二个参数是WSADATA 的数据结构 ,它是接收Windows Sockets 执行时的数据。

Type WSAData
wVersion As Integer
wHighVersion As Integer
szDescription As String * WSADESCRIPTION_LEN
szSystemStatus As String * WSASYS_STATUS_LEN
iMaxSockets As Integer
iMaxUdpDg As Integer
lpVendorInfo As Long
End Type

数据成员的描述在下表中:

Field 描述
wVersion Windows Sockets 版本信息。
wHighVersion 通过加载库文件得到的最高的支持Winsock 的版本,
它通常和wVersion值相同。
szDescription Windows Sockets 执行时的详细描述
szSystemStatus 包含了相关的状态和配置的信息
iMaxSockets 表示同时打开的socket最大数,为0表示没有限制。
iMaxUdpDg 表示同时打开的数据报最大数,为0表示没有限制。
lpVendorInfo 厂商指定信息预留

在Winsock的1.1和2.2版本中没有lpVendorInfo的返回值。因为winsock 2支持多个传输协议,所以iMaxSockets 和iMaxUdpDg只能在仅支持TCP/TP的winsock1.1中使用。为了在Winsock 2中获得这些值,你可以使用WSAEnumProtocols 函数。

如果成功或者返回一个错误代码,则函数返回 0。

错误代码 含义
WSASYSNOTREADY 指出网络没有为传输准备好。
WSAVERNOTSUPPORTED 当前的WinSock实现不支持应用程序指定的Windows Sockets规范版本
WSAEINPROGRESS 一个阻塞WinSock调用正在进行
WSAEPROCLIM 请求的协议没有在系统中配置或没有支持它的实现存在。
WSAEFAULT lpWSAData 不是有效的指针



2.WSACleanup 函数

每次调用了WSAStartup函数,你都需要调用WSACleanup函数,通知系统来卸载库文件及清除已分配的资源,这个函数十分简单,没有任何参数:

Declare Function WSACleanup Lib "ws2_32.dll" () As Long


3.建立Socket函数

Declare Function socket Lib "ws2_32.dll" (ByVal af As Long, _
ByVal s_type As Long,
ByVal Protocol As Long) As Long

函数有3个参数定义建立何种socket,三个参数分别是:
Argument Description Enum Type
af Address family specification. AddressFamily
s_type Type specification for the new socket. SocketType
Protocol Protocol to be used with the socket SocketProtocol
that is specific to the indicated address
family.

AddressFamily:
AF_UNSPEC = 0 '/* unspecified */
AF_UNIX = 1 '/* local to host (pipes, portals) */
AF_INET = 2 '/* internetwork: UDP, TCP, etc. */
AF_IMPLINK = 3 '/* arpanet imp addresses */
AF_PUP = 4 '/* pup protocols: e.g. BSP */
AF_CHAOS = 5 '/* mit CHAOS protocols */
AF_NS = 6 '/* XEROX NS protocols */
AF_IPX = AF_NS '/* IPX protocols: IPX, SPX, etc. */
AF_ISO = 7 '/* ISO protocols */
AF_OSI = AF_ISO '/* OSI is ISO */
AF_ECMA = 8 '/* european computer manufacturers */
AF_DATAKIT = 9 '/* datakit protocols */
AF_CCITT = 10 '/* CCITT protocols, X.25 etc */
AF_SNA = 11 '/* IBM SNA */
AF_DECnet = 12 '/* DECnet */
AF_DLI = 13 '/* Direct data link interface */
AF_LAT = 14 '/* LAT */
AF_HYLINK = 15 '/* NSC Hyperchannel */
AF_APPLETALK = 16 '/* AppleTalk */
AF_NETBIOS = 17 '/* NetBios-style addresses */
AF_VOICEVIEW = 18 '/* VoiceView */
AF_FIREFOX = 19 '/* Protocols from Firefox */
AF_UNKNOWN1 = 20 '/* Somebody is using this! */
AF_BAN = 21 '/* Banyan */
AF_ATM = 22 '/* Native ATM Services */
AF_INET6 = 23 '/* Internetwork Version 6 */
AF_CLUSTER = 24 '/* Microsoft Wolfpack */
AF_12844 = 25 '/* IEEE 1284.4 WG AF */
AF_MAX = 26

Socket types:
SOCK_STREAM = 1 ' /* stream socket */
SOCK_DGRAM = 2 ' /* datagram socket */
SOCK_RAW = 3 ' /* raw-protocol interface */
SOCK_RDM = 4 ' /* reliably-delivered message */
SOCK_SEQPACKET = 5 ' /* sequenced packet stream */

Protocols:
IPPROTO_IP = 0 '/* dummy for IP */
IPPROTO_ICMP = 1 '/* control message protocol */
IPPROTO_IGMP = 2 '/* internet group management protocol */
IPPROTO_GGP = 3 '/* gateway^2 (deprecated) */
IPPROTO_TCP = 6 '/* tcp */
IPPROTO_PUP = 12 '/* pup */
IPPROTO_UDP = 17 '/* user datagram protocol */
IPPROTO_IDP = 22 '/* xns idp */
IPPROTO_ND = 77 '/* UNOFFICIAL net disk proto */
IPPROTO_RAW = 255 '/* raw IP packet */
IPPROTO_MAX = 256


该函数可以建立使用特定协议的网络套接字,例如对于UDP协议可以这样写:
s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
s=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)



4.关闭Socket函数

Declare Function closesocket Lib "ws2_32.dll" (ByVal s As Long) As Long

函数有一个参数为建立socket时的Handle




5.连接函数

Declare Function connect Lib "ws2_32.dll" (ByVal s As Long, _
ByRef name As sockaddr_in, _
ByVal namelen As Long) As Long

参数

s 连接的socket句柄。

name 建立连接的地址。

namelen 连接地址的长度。

返回值

成功时返回0。否则返回SOCKET_ERROR以及一个对应的错误号 Err.LastDllError。

显然在调用这个函数时我们需要知道socket句柄,将连接的电脑的端口号和主机名称(或主机IP地址)。我们知道Winsock 控件的Connect方法依靠两个变量:RemoteHost 和RemotePort。此方法不需要socket句柄,因其已经被封装在COM对象中。你也许认为connect函数应该也接受相同的变量设置,然而,事实并非如此。connect函数的主机地址和端口号的传送是依靠 sockaddr_in 结构。

Public Type sockaddr_in
sin_family As Integer
sin_port As Integer
sin_addr As Long
sin_zero(1 To 8) As Byte
End Type




6.套接字帮定函数

Declare Function bind Lib "ws2_32.dll" (ByVal s As Long, _
ByRef name As sockaddr_in, _
ByRef namelen As Long) As Long



s是使用Socket函数创建好的套接字,name指向描述通信对象的结构体的指针,namelen是该结构的长度。该结

构体中的分量包括:
IP地址:对应name.sin_addr.s_addr

端口号:对应name.sin_port
端口号用于表示同一台计算机上不同的进程(即应用程序),其分配方法有两种:
第一种分配方法是,进程让系统为套接字自动分配一端口号,这只要在调用bind前将端口号指定为0即可。由系统自动分配的端口号位于1024~5000之间,而1~1023之间的任一TCP或UDP端口都是保留的,系统不允许任一进程使用保留端口,除非其有效用户ID是零(即超级用户)。
第二种分配方法是,进程为套接字指定一特定端口。这对于需要给套接字分配一众所周知的端口的服务器是很有用的。指定范围在1024~65536之间。

地址类型:对应name.sin_family,一般都赋成AF_INET,表示是internet地址(即IP 地址)。IP地址通常使用点分表示法表示,但它事实上一个32位的长整数,这两者之间可通过inet_addr()函数转换。


7.套接字监听函数

Declare Function listen Lib "ws2_32.dll" (ByVal s As Long, ByVal backlog As Long) As Long

listen函数用来设定Socket为监听状态,这种状态表明Socket准备被连接了。注意,此函数一般在服务程序上使用,其中s是使用Socket函数创建好的套接字,backlog参数用于设定等待连接的客户端数。



8.接受连接请求

Declare Function accept Lib "ws2_32.dll" (ByVal s As Long, ByRef addr As sockaddr_in, _
ByRef addrlen As Long) As Long


服务端应用程序调用此函数来接受客户端Socket连接请求,accept()函数的返回值为一新的Socket,新Socket就可用来完成服务端和客户端之间的信息传递与接收,而原来Socket仍可以接受其他可户端的连接请求。



9.接收信息

Declare Function recv Lib "ws2_32.dll" (ByVal s As Long, _
ByRef buf As Any, _
ByVal buflen As Long, _
ByVal flags As Long) As Long


s 一个已连接的socket的识别符
buf 接受到的数据的缓冲区
len 缓冲区长度
flags 指定从哪调用的标识

第一个参数是socket的句柄-为socket函数返回值。那就是说:我们需要告诉recv函数,哪一个socket正访问函数。

第二个参数是:函数执行之后能装载一些数据的缓冲区。但它不是必须要有足够的长度接收Winsock缓冲区的所有数据,缓冲区的大小限制为8192 字节 (8 Kbytes)。因此如果Winsock缓冲区的数据的大小大于recv函数的缓冲区,你必需多次调用此函数,直到获取所有的数据。

如果应用程序定义缓冲区的长度,则recv函数必须知道缓冲区可以存放多少字节。第三个参数就是为了这个目的。

最后一个参数是可选的,今天我们不使用。该参数有两个选择标志: MSG_PEEK 和 MSG_OOB,用于改变函数的行为。

MSG_PEEK 从输入数据中取数。数据拷入缓冲区,但不从输入队列中移走。函数返回当前准备接收的字节数。
MSG_OOB 处理OOB(Out-of-band带外)数据。在网络上有两种类型的数据包,正常包和带外包。带外包可以通过检验一个TCP/IP包头的一个特定标志来决定。



10.发送信息

Declare Function send Lib "ws2_32.dll" (ByVal s As Long, _
ByRef buf As Any, _
ByVal buflen As Long, _
ByVal flags As Long) As Long


参数参看接收信息




四,服务器与客户机交互

目前最常用的方法是:服务程序在一个众所周知的地址(其中包括端口信息)监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一个客户对这个服务的地址提出了连接请求。这个时刻,服务程序被唤醒并对客户的请求作出适当的反应。注意,服务器与客户机之间的交互可以是面向连接的(基于流套接字),也可以是无连接的(基于数据报套接字)。


服务器

socket()
|
bind()
|
listen() 客户机
|
| socket()
| 建立连接 |
accept() <------------------------- connect()
| 请求数据 |
recv() <----------------------------- send()
| |
| 处理服务请求 |
| 应答数据 |
send() ------------------------------> recv()
| |
close() close()




五,其他

比较:WinSock控件
优点:使用简单,工作量小。
缺点:功能少仅支持TCP,UDP协议,需要WinSock控件(系统默认安装不带MSWINSCK.OCX文件)
适合于初学者
WinSockAPI
优点:功能强大,支持多种协议,使用灵活,WinSockAPI调用的wsock32.dll(28K)或ws2_32.dll(69K)为Windows系统自带函数库不必担心缺少文件。
缺点:使用复杂,编程量大,需要一定基础
适合于要求较高的网络程序

Inet控件- Microsoft Internet Transfer Control 6.0 -MSINET.OCX
AccessType 属性 设置或返回一个值,决定该控件用来与 Internet 网进行通讯的访问类型
icUseDefault 0 控件使用在注册表中找到的缺省设置值来访问 Internet 网。
icDirect 1 直接连到 Internet 网。控件直接连到 Internet 网。
icNamedProxy 2 指示控件使用 Proxy 属性中指定的代理服务器。

Protocol 属性 设置或返回一个值,指定和 Execute 方法一起使用的协议
icUnknown 0 未知的。
icDefault 1 缺省协议。
icFTP 2 FTP。文件传输协议。
icReserved 3 为将来预留。
icHTTP 4 HTTP。超文本传输协议。
icHTTPS 5 安全 HTTP

StillExecuting 属性 返回一个布尔值,指明此 Internet Transfer 控件是否处于忙状态
True -1 该控件忙。
False 0 该控件空闲。

UserName 属性 设置或返回与请求一起发送到远程计算机的用户名称
Password 属性 设置或返回一个密码,该密码将和请求一道被发送,用以在远程计算机上登录
Document 属性 返回或设置与 Execute 方法一起使用的文件或文档
hInternet 属性 从下一级的 Wininet.dll API 返回 Internet 句柄
Proxy 属性 设置或返回用以和 Internet 网进行通讯的代理服务器的名称和端口
RequestTimeout 属性 设置或返回在超时截止之前,按秒计算的等待时间长度
ResponseCode 属性 StateChanged 事件中出现 icError (11) 状态时,从连接返回错误码
ResponseInfo 属性 返回最后发生的错误的文本
URL 属性 设置或返回 Execute 或 OpenURL 方法使用的 URL

StateChanged 事件 连接中状态发生改变,就会引发该事件
语法:object_StateChanged(ByVal State As Integer) 'State返回一个值提示发生了如下改变
icNone 0 无状态可报告。
icHostResolvingHost 1 该控件正在查询所指定的主机的 IP 地址。
icHostResolved 2 该控件已成功地找到所指定的主机的 IP 地址。
icConnecting 3 该控件正在与主机连接。
icConnected 4 该控件已与主机连接成功。
icRequesting 5 该控件正在向主机发送请求。
icRequestSent 6 该控件发送请求已成功。
icReceivingResponse 7 该控件正在接收主机的响应。
icResponseReceived 8 该控件已成功地接收到主机的响应。
icDisconnecting 9 该控件正在解除与主机的连接。
icDisconnected 10 该控件已成功地与主机解除了连接。
icError 11 与主机通讯时出现了错误。
icResponseCompleted 12 该请求已经完成,并且所有数据均已接收到。


Cancel 方法 取消当前请求,并关闭当前创建的所有连接
语法:object.Cancel
---------------------------------------------------------------------
Execute 方法 执行对远程服务器的请求,只能发送对特定的协议有效的请求
语法:object.Execute url, operation, data, requestHeaders
url 可选的,指定控件将要连接的 URL。默认时使用 URL 属性中指定的 URL。
operation 可选的,指定将要执行的操作类型。参见下面的“设置值”。
data 可选的,指定用于操作的数据。参见下面的“设置值”。
requestHeaders 可选的,指定由远程服务器传来的附加的标头。格式为:header name: header value vbCrLf

支持 HTTP 命令的operation 有效设置值:
Get 检索由 URL 属性指定的 URL 中的数据。
HEAD 发送请求的标头。
POST 传递数据给服务器。该数据在 data 参数中。这是 GET 的替代方法,附加的指令在 data 参数中指定。
PUT Put 操作。被替代的页面名在 data 参数中。

支持 FTP 命令的operation 有效设置值:
CD file1 改变到 file1 中指定的目录。
CDUP 改变到父目录。等效于“CD..”。
CLOSE 关闭当前的 FTP 连接。
DELETE file1 删除 file1 中指定的文件。
DIR file1 搜索 file1 中指定的目录。使用 GetChunk 方法返回目录数据
GET file1 file2 检索 file1 中指定的远程文件,并创建 file2 中指定的新本地文件。
LS file1 搜索 file1 中指定的目录。使用 GetChunk 方法返回文件目录数据。
MKDIR file1 创建 file1 中指定的目录。创建是否成功取决于用户在远程主机上的权限。
PUT file1 file2 复制 file1 指定的本地文件到 file2 指定的远程主机上。
PWD 返回当前目录名。使用 GetChunk 方法返回数据。
Quit 终止当前用户。
RECV file1 file2 检索 file1 中指定的远程文件,并创建 file2 中指定的本地新文件。等效于 GET。
RENAME file1 file2 将 file1 中命名的远程文件重命名为 file2 中指定的新名称。需要权限。
RMDIR file1 删除 file1 中指定的远程目录。需要权限。
SEND file1 file2 复制 file1 指定的本地文件到 file2 指定的远程主机上。等效于 PUT。
SIZE file1 返回 file1 指定的目录的大小。

FTP 操作的语法为:operationName "主机网址" "operation 有效设置值"
-----------------------------------------------------------------
GetChunk 方法 从 StateChanged 事件中检索数据
语法:object.GetChunk( size [,datatype] )
Size 必需的。长整型数值表达式,决定被检索的块的大小。
datatype 可选的。整数,决定如下被检索块的数据类型。
icString 0 缺省值。把数据作为字符串来检索。
icByteArray 1 把数据作为字节数组来检索。
----------------------------------------
GetHeader方法 用于检索 HTTP 文件的标头文本
语法:object.GetHeader (hdrName)
hdrName 可选的。字符串,指定如下将被检索的标头。
Date 返回文档传输的日期和时间。返回的数据格式为:Wednesday, 27-April-96 19:34:15 GMT。
MIME-version 返回 MIME 协议的版本号,目前为 1.00。
Server 返回服务器的名称。
Content-length 返回数据的字节长度。
Content-Type 返回数据的 MIME 的 当前类型。
Last-modified 返回最后一次修改文档的日期和时间。
-------------------------------------------------
OpenURL 方法 打开并返回指定 URL 的文档
语法:object.OpenUrl url [,datatype]
url 必需的。被检索文档的 URL。
datatype 可选的。整数值如下
icString 0 缺省值。把数据作为字符串来检索。
icByteArray 1 把数据作为字节数组来检索。

Warning: file_get_contents(lang.tmp) [function.file-get-contents]: failed to open stream: No such file or directory in C:\soft\xampp\xampp\htdocs\xampp\index.php on line 2

Warning: Cannot modify header information - headers already sent by (output started at C:\soft\xampp\xampp\htdocs\xampp\index.php:2) in C:\soft\xampp\xampp\htdocs\xampp\index.php on line 4

Solution: Create a lang.tmp file in C:\soft\xampp\xampp\htdocs\xampp, and then OK