zoukankan      html  css  js  c++  java
  • 谈谈"WinSock控件及WinSockAPI"

     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主要方法

    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控件主要事件

    3.Winsock控件主要事件

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

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

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

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

    e.Error事件
       当在工作中发生任何错误都会触发这个事件。
    (三)WinSockAPI的使用(1)

    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 不是有效的指针

    (三)WinSockAPI的使用(2)

    2.WSACleanup 函数

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

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

    (三)WinSockAPI的使用(3)

    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)
    (三)WinSockAPI的使用(4)

    4.关闭Socket函数

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

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

    (三)WinSockAPI的使用(5)

    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
    (三)WinSockAPI的使用(6)

    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()函数转换。

    三)WinSockAPI的使用(7)

    7.套接字监听函数

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

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

    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仍可以接受其他可户端的连接请求。
    WINSOCK 是把  WINSOCKAPI  搞简单点.

    (三)WinSockAPI的使用(9)

    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包头的一个特定标志来决定。

    (三)WinSockAPI的使用(10)

    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系统自带函数库不必担心缺少文件。
                   缺点:使用复杂,编程量大,需要一定基础
                   适合于要求较高的网络程序

  • 相关阅读:
    【11.3】
    WPF中内嵌网页的两种方式
    .net的winform中DialogResult属性的使用
    MVC过滤器使用方法
    C#调用C++的DLL错误解决方法
    WPF中UserControl的属性和事件
    React架构之路
    读完这一篇,字符串格式化界的“白富美”(f-strings)抱回家!
    关于使用format()方法格式化字符串,读这一篇就够了!
    String,StringBuffer与StringBuilder
  • 原文地址:https://www.cnblogs.com/googlegis/p/2979178.html
Copyright © 2011-2022 走看看