一步步学习Winsock API(1)——建立Visual Basic项目模板

  在本系列文章的第一部分,我们要学习一些基本的Winsock API函数。这些函数在如初始化Winsock服务、域名解析、异常处理等方面提供了必要的功能。如果你不理解这些函数,你就不能完成一些有趣的事情如:建立socket、发送接收数据等。以下就是这一部分涉及的函数的列表:

  • WSAStartup
  • WSACleanup
  • gethostbyaddr
  • gethostbyname
  • gethostname
  • getservbyname
  • getprotobynumber
  • getprotobyname
  • getservbyport
  • inet_addr
  • inet_ntoa
  • htons
  • htonl
  • ntohl
  • ntohs

       为了测试这些函数的功能及使用方法大约需要十来个示例程序,所以我建议最好的方法是建立一个VB项目模板,里面包含所有这些API函数、自定义函数、一些窗体及代码用来运行我们将要进行的测试示例程序。

    建立项目

    1. 所有的示例都是标准的EXE程序,所以选择菜单File | New Project,在新弹出的对话框里选择,点击OK就建立了项目。
    2. 将项目改名为BasicWinsockAPI
    3. 将默认窗体改名为frmMain
    4. 选择菜单Project | Add Module建立一个代码模块,该模块用来包含一些API函数的声明及一些自定义函数的定义。
    5. 将代码模块改名为modWinsock

    插入API声明

      打开modWinsock模块的代码,插入以下代码。现在我们将不对这些声明作解释,在以后的文章里有这些函数的详细说明。

    Option Explicit

    Public Const
    INADDR_NONE = &HFFFF
    Public Const
    SOCKET_ERROR = -1
    Public Const
    WSABASEERR = 10000
    Public Const WSAEFAULT = (WSABASEERR + 14)
    Public Const WSAEINVAL = (WSABASEERR + 22)
    Public Const WSAEINPROGRESS = (WSABASEERR + 50)
    Public Const WSAENETDOWN = (WSABASEERR + 50)
    Public Const WSASYSNOTREADY = (WSABASEERR + 91)
    Public Const WSAVERNOTSUPPORTED = (WSABASEERR + 92)
    Public Const WSANOTINITIALISED = (WSABASEERR + 93)
    Public Const WSAHOST_NOT_FOUND = 11001
    Public Const WSADESCRIPTION_LEN = 257
    Public Const WSASYS_STATUS_LEN = 129
    Public Const WSATRY_AGAIN = 11002
    Public Const WSANO_RECOVERY = 11003
    Public Const WSANO_DATA = 11004

    Public 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

    Public Type HOSTENT
        hName     As Long
        hAliases  As Long
        hAddrType As Integer
        hLength   As Integer
        hAddrList As Long
    End Type

    Public Type servent
        s_name    As Long
        s_aliases As Long
        s_port    As Integer
        s_proto   As Long
    End Type

    Public Type protoent
        p_name    As String 'Official name of the protocol
        p_aliases As Long 'Null-terminated array of alternate names
        p_proto   As Long 'Protocol number, in host byte order
    End Type

    Public Declare Function WSAStartup _
        Lib "ws2_32.dll" (ByVal wVR As Long, lpWSAD As WSAData) As Long

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

    Public Declare Function gethostbyaddr _
        Lib "ws2_32.dll" (addr As Long, ByVal addr_len As Long, _
                          ByVal addr_type As Long)
    As Long

    Public Declare Function gethostbyname _
        Lib "ws2_32.dll" (ByVal host_name As String)
    As Long

    Public Declare Function gethostname _
        Lib "ws2_32.dll" (ByVal host_name As String, _
                          ByVal namelen As Long)
    As Long

    Public Declare Function getservbyname _
        Lib "ws2_32.dll" (ByVal serv_name As String, _
                          ByVal proto As String)
    As Long

    Public Declare Function getprotobynumber _
        Lib "ws2_32.dll" (ByVal proto As Long)
    As Long

    Public Declare Function getprotobyname _
        Lib "ws2_32.dll" (ByVal proto_name As String)
    As Long

    Public Declare Function getservbyport _
        Lib "ws2_32.dll" (ByVal port As Integer, ByVal proto As Long) As Long

    Public Declare Function inet_addr _
        Lib "ws2_32.dll" (ByVal cp As String)
    As Long

    Public Declare Function inet_ntoa _
        Lib "ws2_32.dll" (ByVal inn As Long) As Long

    Public Declare Function htons _
        Lib "ws2_32.dll" (ByVal hostshort As Integer)
    As Integer

    Public Declare Function htonl _
        Lib "ws2_32.dll" (ByVal hostlong As Long)
    As Long

    Public Declare Function ntohl _
        Lib "ws2_32.dll" (ByVal netlong As Long)
    As Long

    Public Declare Function ntohs _
        Lib "ws2_32.dll" (ByVal netshort As Integer) As Integer

    Public Declare Sub RtlMoveMemory _
        Lib "kernel32" (hpvDest As Any, _
                        ByVal hpvSource As Long, _
                        ByVal cbCopy As Long)

    Public Declare Function lstrcpy _
        Lib "kernel32" Alias "lstrcpyA" (ByVal lpString1 As String, _
                                        ByVal lpString2 As Long)
    As Long

    Public Declare Function lstrlen _
        Lib "kernel32" Alias "lstrlenA" (ByVal lpString As Any) As Long

    建立辅助函数

    Winsock API广泛地使用C/C++的无符号数据类型,如:无符号短整型(unsigned short,2字节)、无符号长整型(unsigned long,4字节)。Visual Basic同样有2或4字节的整型数据类型Integer和Long,但是,不幸的是它们都是有符号的,VB并不直接支持无符号数据类型。

    语言数据类型长度范围
    C/C++u_short2 bytes0 to 65535
    C/C++u_long4 bytes0 to 4294967295
    VBInteger2 bytes-32768 to +32767
    VBLong4 bytes-2147483648 to +2147483647
     
    不过,即然Winsock API接收的参数或返回的值都一些字节,我们就能用VB的数据类型来代替,不过我们需要一些子程序来进行这些值的转换。幸运的是我们并不需要发明什么算法,微软的支持团队已经解决了这个问题。查看一下微软知识库 HOWTO: Convert Between Signed and Unsigned Numbers,我们将要使用这些函数,所以将以下代码插入modWinsock代码模块。
     

    Private Const OFFSET_4 = 4294967296#
    Private Const MAXINT_4 = 2147483647
    Private Const OFFSET_2 = 65536
    Private Const MAXINT_2 = 32767

    Public Function UnsignedToLong(Value As Double) As Long
        '
        'The function takes a Double containing a value in the 
        'range of an unsigned Long and returns a Long that you 
        'can pass to an API that requires an unsigned Long
        '

        If Value < 0 Or Value >= OFFSET_4 Then Error 6 ' Overflow
        '
        If Value <= MAXINT_4 Then
            UnsignedToLong = Value
        Else
            UnsignedToLong = Value - OFFSET_4
        End If
       
    '
    End Function


    Public Function LongToUnsigned(Value As Long) As Double
        '
        'The function takes an unsigned Long from an API and 
        'converts it to a Double for display or arithmetic purposes
        '

        If Value < 0 Then
            LongToUnsigned = Value + OFFSET_4
        Else
            LongToUnsigned = Value
        End If
       
    '
    End Function


    Public Function UnsignedToInteger(Value As Long) As Integer
        '
        'The function takes a Long containing a value in the range 
        'of an unsigned Integer and returns an Integer that you 
        'can pass to an API that requires an unsigned Integer
        '

        If Value < 0 Or Value >= OFFSET_2 Then Error 6 ' Overflow
        '
        If Value <= MAXINT_2 Then
            UnsignedToInteger = Value
        Else
            UnsignedToInteger = Value - OFFSET_2
        End If
       
    '
    End Function


    Public Function IntegerToUnsigned(Value As Integer) As Long
        '
        'The function takes an unsigned Integer from and API and 
        'converts it to a Long for display or arithmetic purposes
        '

        If Value < 0 Then
            IntegerToUnsigned = Value + OFFSET_2
        Else
            IntegerToUnsigned = Value
        End If
       
    '
    End Function

     

    一些API函数返回的值为指向字符的指针,这些值并不能直接存入VB中的String型变量。Win32API为我们提供了一个函数lstrcpy让我们将一指针指向的字符串Copy到另一变量内。

     

    Public Declare Function lstrcpy Lib _
        "kernel32" Alias "lstrcpyA" (ByVal lpString1 As String, _
                                     ByVal lpString2 As Long) As Long
    参数lpString2就是字符串的指针,参数lpString1就是存储字符串的buffer。该buffer必须要有足够的长度来容纳字符串,所以我们在调用该函数前必须知道该字符串的长度。另一个Win32API函数lstrlen帮我们完成此功能——得到一个指针所指向的字符串的长度。

    Public Declare Function lstrlen Lib "kernel32" Alias "lstrlenA" (ByVal lpString As Any) As Long

    现在,我们就可以写一个函数StringFromPointer通过一个指针来得到一个字符串。将以下代码插入modWinsock模块:

    Public Function StringFromPointer(ByVal lPointer As Long) As String
        '
        Dim strTemp As String
        Dim lRetVal As Long
        '
        'prepare the strTemp buffer

        strTemp = String$(lstrlen(ByVal lPointer), 0)
        '
        'copy the string into the strTemp buffer

        lRetVal = lstrcpy(ByVal strTemp, ByVal lPointer)
        '
        'return a string

        If lRetVal Then StringFromPointer = strTemp
        '
    End Function

    好了,modWinsock模块已经准备好了,我们现在将要修改这个项目的默认窗体。

    修改默认窗体

    打开frmMain窗体的设计界面,增加两个Button,将这两个按钮的属性改为如下表:

    NameCaptionDefaultCancel
    cmdGet&GetTrue 
    cmdExitE&xit True

     
     
    打开代码设计界面,将以下代码加入Form_Load事件处理过程。
    Private Sub Form_Load()
        '
        Dim lngRetVal      As Long
        Dim strErrorMsg    As String
        Dim udtWinsockData As WSAData
        Dim lngType        As Long
        Dim lngProtocol    As Long
        '
        'start up winsock service

        lngRetVal = WSAStartup(&H101, udtWinsockData)
        '
        If lngRetVal <> 0 Then
            '
            '

            Select Case lngRetVal
                Case WSASYSNOTREADY
                    strErrorMsg = "The underlying network subsystem is not " & _
                        "ready for network communication."
                Case WSAVERNOTSUPPORTED
                    strErrorMsg = "The version of Windows Sockets API support " & _
                        "requested is not provided by this particular " & _
                        "Windows Sockets implementation."
                Case WSAEINVAL
                    strErrorMsg = "The Windows Sockets version specified by the " & _
                        "application is not supported by this DLL."
            End Select
            '
            MsgBox strErrorMsg, vbCritical
            '
        End If
        '
    End Sub
    在我们的程序调用任何Winsock API函数前,我们需要初始化Winsock服务。为此我们需要在窗体的Form_Load事件处理过程中调用WSAStartup函数。因为我们在任何用到Winsock的程序中用到这个函数,所以我们将这个函数加入项目模板,这样今后就不用重复拷贝这个函数了。
    同样,在窗体Unload时也有一些工作要做,在窗体的Form_Unload事件处理过程中我们调用WSACleanup函数来告之系统不再需要使用Winsock服务,ShowErrorMsg用来显示Winsock的错误描述对话框。

    Private Sub Form_Unload(Cancel As Integer)
        Call WSACleanup
    End Sub

    Private Sub ShowErrorMsg(lngError As Long)
        '
        Dim strMessage As String
        '
        Select Case lngError
            Case WSANOTINITIALISED
                strMessage = "A successful WSAStartup call must occur " & _
                             "before using this function."
            Case WSAENETDOWN
                strMessage = "The network subsystem has failed."
            Case WSAHOST_NOT_FOUND
                strMessage = "Authoritative answer host not found."
            Case WSATRY_AGAIN
                strMessage = "Nonauthoritative host not found, or server failure."
            Case WSANO_RECOVERY
                strMessage = "A nonrecoverable error occurred."
            Case WSANO_DATA
                strMessage = "Valid name, no data record of requested type."
            Case WSAEINPROGRESS
                strMessage = "A blocking Windows Sockets 1.1 call is in " & _
                             "progress, or the service provider is still " & _
                             "processing a callback function."
            Case WSAEFAULT
                strMessage = "The name parameter is not a valid part of " & _
                             "the user address space."
            Case WSAEINTR
                strMessage = "A blocking Windows Socket 1.1 call was " & _
                             "canceled through WSACancelBlockingCall."
        End Select
        '
        MsgBox strMessage, vbExclamation
        '
    End Sub

     
    哦,还有一个别忘了加上:

    Private Sub cmdExit_Click()
        Unload Me
    End Sub

    所有该项目的工作都已经完成,现在就剩将该项目存为模板了。

    将项目存为模板

      Visual Basic项目模板就是存储于子目录Template/Projects下的普通项目,该目录位于VB安装目录下。

    所以,选择菜单File | Save Project As就可以将我们的项目保存为项目模板,在弹出的对话框里选择VB/Template/Projects 目录并命名为Basic Winsock API.vbp,项目中所有的内容将会保存至此目录。现在,选择菜单File | New Project在弹出的New Project窗口中我们将看到我们刚才新加的模板。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值