VB Socket通信函數

對網絡知識有所遺忘的話,可以先簡單補習一下“套接字”概念,再來看這些函數就會覺得他們的用途很好理解,如果想更詳細的學習,可以參看 Socket通信原理

關於各函數返回值,可以參看 socket函數返回值分析

一,服務器與客戶機交互


       先簡單俯瞰一下目前最常用的方法:服務程序在一個衆所周知的地址(其中包括端口信息)監聽對服務的請求,也就是說,服務進程一直處於休眠狀態,直到一個客戶對這個服務的地址提出了連接請求。這個時刻,服務程序被喚醒並對客戶的請求作出適當的反應。注意,服務器與客戶機之間的交互可以是面向連接的(基於流套接字),也可以是無連接的(基於數據報套接字)。


           服務器


          socket()
             |
           bind()
             |
          listen()                                       客戶機
             |
             |                                           socket()
             |                     建立連接               |
          accept()   <-------------------------    connect()
             |                     請求數據               |
           recv()   <-----------------------------     send()
             |                                                |
        處理服務請求                                    |
             |                      應答數據              |
           send()    ------------------------------>   recv()
             |                                                |
          close()                                        close()

二,逐個介紹函數

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函數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函數closesocket()

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

函數有一個參數爲建立socket時的Handle




5.連接函數connect()函數

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.套接字綁定函數bind()函數

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.套接字監聽函數listen()函數

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

listen函數用來設定Socket爲監聽狀態,這種狀態表明Socket準備被連接了。注意,此函數一般在服務程序上使用,其中s是使用Socket函數創建好的套接字,backlog參數用於設定等待連接的客戶端數。



8.接受連接請求accept()函數

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.接收信息recv()函數

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.發送信息Send()函數

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系統自帶函數庫不必擔心缺少文件。
              缺點:使用複雜,編程量大,需要一定基礎
              適合於要求較高的網絡程序

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章