Atlanta Custom Software Development 

 
   Search        Code/Page
 

User Login
Email

Password

 

Forgot the Password?
Services
» Web Development
» Maintenance
» Data Integration/BI
» Information Management
Programming
  Database
Automation
OS/Networking
Graphics
Links
Tools
» Regular Expr Tester
» Free Tools


In this article we will explore power of winsock api. We will implement very simple Asynchronous client and server using API without using Winsock Active-X control. Before we go into more detail let me clear why one more article on the earth for winsock API ...

Why This Article ???

You might have question that Microsoft Winsock Active-X control is great for client/server application then why should I care to learn API based Socket programming ? Here are the reasons

  • If you want application without any dependancy OCX file (Winsock ocx)
  • If you want to handle several connections without putting huge memory overhead. Coz when you use winsock ocx you have to use Control array to handle multiple connection and its extra overhead to maintain Array of Winsock Control.
  • If you want more flexibility.


Steps to implement Winsock Server

  1. Initialize WSA (Winsock Service).
  2. Create a socket.
  3. Bind the socket.
  4. Listen on the socket.
  5. Accept a connection.
  6. Send and receive data.
  7. Disconnect.


Steps to implement Winsock Client

  1. Initialize WSA (Winsock Service).
  2. Create a socket.
  3. Connect to the server.
  4. Send and receive data.
  5. Disconnect.

In this article we will implement Winsock server. To learn about how to implement Winsock Client Please check Windows Socket (Winsock) Programming tutorial : Part-2

Now we will go through each step to construct winsock server

[1] Initialize WSA (Winsock Service)

The Very first step to implement winsock client/server is initialize winsock service. We will use StartWinsock and EndWinsock function to start and stop winsock service.

Implementing StartWinsock and EndWinsock routines

Click here to copy the following block
Public Function StartWinsock(Optional sDescription) As Boolean
  Dim StartupData As WSADataType
  If Not WSAStartedUp Then 'WSAStartedUp is a flag
    If Not WSAStartup(&H101, StartupData) Then
      WSAStartedUp = True
      sDescription = StartupData.szDescription
    Else
      WSAStartedUp = False
    End If
  End If
  StartWinsock = WSAStartedUp
End Function

Public Sub EndWinsock()
  Dim Ret&
  If WSAIsBlocking() Then
    Ret = WSACancelBlockingCall()
  End If
  Ret = WSACleanup()
  WSAStartedUp = False
End Sub

code Explaination

To start winsock service you must call the WSAStartup function. For each call of WSAStartup function you need to call WSACleanup in order to unload the library and clear resources allocated by the system.

Click here to copy the following block
Declare Function WSAStartup Lib "ws2_32.dll" _
  (ByVal wVersionRequired As Long, lpWSAData As WSAData) As Long

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


Click here to copy the following block
'//Some other function to check blocking while WSA shutdown
Declare Function WSACancelBlockingCall Lib "wsock32.dll" () As Long
Declare Function WSAIsBlocking Lib "wsock32.dll" () As Long

The wVersionRequired parameter defines highest version of Windows Sockets support that the caller can use. The high-order byte specifies the minor version (revision) number; the low-order byte specifies the major version number. Below are 2 examples of usage of the function in Visual Basic for 2 versions of the Winsock service:

Click here to copy the following block
RetVal = WSAStartup(&H101, udtWinsockData) 'To initialize version 1.1
RetVal = WSAStartup(&H202, udtWinsockData) 'To initialize version 2.2

The second parameter is the WSADATA data structure that is to receive details of the Windows Sockets implementation.

Click here to copy the following block
Type WSADataType
  wVersion As Integer   'Winsock version caller going to use
  wHighVersion As Integer 'Highest supported version
  szDescription As String * WSA_DescriptionSize 'Description of winsock implementation
  szSystemStatus As String * WSA_SysStatusSize 'status or configuration information
  iMaxSockets As Integer 'Maximum number of sockets
  iMaxUdpDg As Integer  'Maximum UDP datagram size
  lpVendorInfo As Long  'Vendor-specific information
End Type

[2] Create a socket

Once you initialize the Winsock service the next step is create a socket which will be used to listen on a specified port. To create a new socket we canuse following VB function

Click here to copy the following block
Public Function NewSocket(ByVal AdrFamily As Long, ByVal SckType As Long, ByVal SckProtocol As Long, HWndForMsg As Long) As Long
'********************************************************************************
'Purpose  :Creates a new socket
'Returns  :The socket handle if successful, otherwise INVALID_SOCKET
'Arguments :AddressFamily, SocketType and SocketProtocol
'********************************************************************************
  On Error GoTo errHandler
  Dim hSocket As Long  'value returned by the socket API function
  Dim lngEvents As Long

  'Call the socket Winsock API function in order create a new socket
  hSocket = socket(AdrFamily, SckType, SckProtocol)
  NewSocket = hSocket  'Assign returned value ( -1 => INVALID_SOCKET)

  If hSocket <> INVALID_SOCKET Then
    'The lngEvents variable contains a bitmask of events we are
    'going to catch with the window callback function.
    lngEvents = FD_CONNECT Or FD_READ Or FD_WRITE Or FD_CLOSE Or FD_ACCEPT

    'Force the Winsock service to send the network event notifications
    'to the window which handle is p_lngWindowHandle.
    lngRetValue = WSAAsyncSelect(hSocket, HWndForMsg, WINSOCKMSG, lngEvents)
  End If
  Exit Function
errHandler:
  NewSocket = INVALID_SOCKET
End Function

code Explaination

To create a new socket you can use socket api.

Click here to copy the following block
Declare Function socket Lib "wsock32.dll" (ByVal AddressFamily As Long _
  , ByVal SocketType As Long _
  , ByVal Protocol As Long) As Long

socket function expect 3 parameters : AddressFamily, SocketType and Protocol. If call is successful then it returns socket handle which you can use for subsecuent socket api calls. If function fails to create new socket then it returns -1 (SOCKET_ERROR). You can get socket error using WSAGetLastError

Click here to copy the following block
Declare Function WSAGetLastError Lib "wsock32.dll" () As Long

Now we will discuss each input parameter for socket api.

AddressFamily:

First parameter for socket function is Addressfamily which can be one of the following options. Here we have created enum for easy use.

In our example we will use AF_INET address family with TCP protocol and STREAM socket which are most common options with windows socket.

Click here to copy the following block
Public Enum AddressFamily
  AF_UNSPEC = 0   '/* unspecified */
'/*
' * Although AF_UNSPEC is defined for backwards compatibility , using
' * AF_UNSPEC for the "af" parameter when creating a socket is STRONGLY
' * DISCOURAGED.  The interpretation of the "protocol" parameter
' * depends on the actual address family chosen. As environments grow
' * to include more and more address families that use overlapping
' * protocol values there is more and more chance of choosing an
' * undesired address family when AF_UNSPEC is used.
' */
  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
End Enum

SocketType:

Second parameter for socket function is SocketType. Socket type can be one of the following options.

Click here to copy the following block
Public Enum SocketType
  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 */
End Enum

Protocol:

Third parameter for socket function is Protocol. Protocol can be one of the following options.

Click here to copy the following block
Public Enum SocketProtocol
  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
End Enum

There are two type of sockets programming mode available in windows.
1. Blocking socket
2. Non-Blocking socket

# Blocking socket #

For Blocking socket you have to check the status of any socket event or any error. To do that you can use select function. The select function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.

Click here to copy the following block
Declare Function vbselect Lib "ws2_32.dll" Alias "select" (ByVal nfds As Long _
    , ByRef readfds As Any _
    , ByRef writefds As Any _
    , ByRef exceptfds As Any _
    , ByRef timeout As Long) As Long

'Structures used for select function
Public Type timeval
  tv_sec As Long  'seconds
  tv_usec As Long  'and microseconds
End Type

Public Type fd_set
  fd_count As Long  '// how many are SET?
  fd_array(1 To FD_SETSIZE) As Long  '// an array of SOCKETs
End Type

Parameters:

nfds
[in] Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.

readfds
[in, out] Optional pointer to a set of sockets to be checked for readability.

writefds
[in, out] Optional pointer to a set of sockets to be checked for writability.

exceptfds
[in, out] Optional pointer to a set of sockets to be checked for errors.

timeout
[in] Maximum time for select to wait, provided in the form of a TIMEVAL structure. Set the timeout parameter to null for blocking operations.

Return Value:

The select function returns the total number of socket handles that are ready and contained in the fd_set structures, zero if the time limit expired, or SOCKET_ERROR if an error occurred. If the return value is SOCKET_ERROR, WSAGetLastError can be used to retrieve a specific error code.

Example:

You can execute select in timer event or may be in while loop but while loop can block your thread.

Click here to copy the following block
'// Here we assume that our newly created sockets is hSocket
'// If you are dealing with multiple connections then You can store socket
'// handles in Array or collections
Private Sub Timer1_Timer()
  Dim cntSocks As Long, lngRetValue As Long
  Dim udtRead_fds  As fd_set
  Dim udtWrite_fds  As fd_set
  Dim udtError_fds  As fd_set

  '////////////////////////////////////////////////////////////
  'for multiple sockets :
  'Assume that socket handles are stored in hSocketsArr() array

  '////////////////////////////////////////////////////////////
  'cntSocks = UBound(hSocketsArr)+1
  'For i = 0 To UBound(hSocketsArr)
  '  udtRead_fds.fd_array(i) = hSocketArr(i)
  '  udtWrite_fds.fd_array(i) = hSocketArr(i)
  '  udtError_fds.fd_array(i) = hSocketArr(i)
  'Next

  '////////////////////////////////////////////////////////////
  'for single socket :
  'Assume that socket handle is stored in hSocket variable

  '////////////////////////////////////////////////////////////
  udtRead_fds.fd_array(0) = hSocket
  udtWrite_fds.fd_array(0) = hSocket
  udtError_fds.fd_array(0) = hSocket

  'Call the select function
  lngRetValue = vbselect(0&, udtRead_fds, udtWrite_fds, udtError_fds, 0&)
  If lngRetValue = SOCKET_ERROR Then
    'some error handling stuff
  ElseIf lngRetValue > 0 Then
    'If returned value grater than 0 that means that
    'the select function has found some sockets that
    'can be writable, readable or in error state

    'Check for writable sockets
    If udtWrite_fds.fd_count > 0 Then
      For i = 0 To udtWrite_fds.fd_count - 1
        lngSocket = udtWrite_fds.fd_array(i)  '//get socket Handle
        '<TODO>: Write to this socket using lngSocket socket handle
        'use send api function to send the data
      Next
    End If

    'Check for readable sockets
    If udtRead_fds.fd_count > 0 Then
      For i = 0 To udtRead_fds.fd_count - 1
        lngSocket = udtRead_fds.fd_array(i)  '//get socket Handle
        '<TODO>: Read from this socket using lngSocket socket handle
        'use recv api function to read data
      Next
    End If

    'Check for sockets in error state
    If udtError_fds.fd_count > 0 Then
      For i = 0 To udtError_fds.fd_count - 1
        lngSocket = udtError_fds.fd_array(i)  '//get socket Handle
        '<TODO>: Read from this socket using lngSocket socket handle
        'use getsockopt api function with SO_ERROR option to retrive error
      Next
    End If

  End If
End Sub

# Non Blocking socket #

In Non-Blocking socket windows sends you various socket events. On successful socket creation you can Register socket for asynchrous events using WSAAsyncSelect api which takes two parameters (1) socket handle (2) Event mask (socket events which you want to receive from windows).

Click here to copy the following block
Declare Function WSAAsyncSelect Lib "ws2_32.dll" (ByVal s As Long _
  , ByVal hwnd As Long _
  , ByVal wMsg As Long _
  , ByVal lEvent As Long) As Long

Parameters:

s
[in] Descriptor that identifies the socket for which event notification is required.

hwnd
[in] Handle that identifies the window that will receive a message when a network event occurs.

wMsg
[in] Message to be received when a network event occurs. (For socket messages you have to provide WINSOCKMSG)

lEvent
[in] Bitmask that specifies a combination of network events in which the application is interested.

You can create lEvent (Event Mask) parameter using OR logical operator
e.g.

Click here to copy the following block
lEvent = FD_READ Or FD_WRITE Or FD_CONNECT

Here is a summary of some events and conditions for each asynchronous notification message.

FD_READ:
  • When WSAAsyncSelect is called, if there is data currently available to receive.
  • When data arrives, if FD_READ is not already posted.
  • After recv or recvfrom is called (with or without MSG_PEEK), if data is still available to receive.

FD_WRITE:
  • When WSAAsyncSelect called, if a send or sendto is possible.
  • After connect or accept called, when connection established.
  • After send or sendto fail with WSAEWOULDBLOCK, when send or sendto are likely to succeed.
  • After bind on a connectionless socket. FD_WRITE may or may not occur at this time (implementation-dependent). In any case, a connectionless socket is always writeable immediately after a bind operation.

FD_ACCEPT:
  • When WSAAsyncSelect called, if there is currently a connection request available to accept.
  • When a connection request arrives, if FD_ACCEPT not already posted.
  • After accept called, if there is another connection request available to accept.

FD_CONNECT:
  • When WSAAsyncSelect called, if there is currently a connection established.
  • After connect called, when connection is established (even when connect succeeds immediately, as is typical with a datagram socket).
  • After calling WSAJoinLeaf, when join operation completes.
  • After connect, WSAConnect, or WSAJoinLeaf was called with a nonblocking, connection-oriented socket. The initial operation returned with a specific error of WSAEWOULDBLOCK, but the network operation went ahead. Whether the operation eventually succeeds or not, when the outcome has been determined, FD_CONNECT happens. The client should check the error code to determine whether the outcome was successful or failed.

FD_CLOSE: Only valid on connection-oriented sockets (for example, SOCK_STREAM)
  • When WSAAsyncSelect called, if socket connection has been closed.
  • After remote system initiated graceful close, when no data currently available to receive (note: if data has been received and is waiting to be read when the remote system initiates a graceful close, the FD_CLOSE is not delivered until all pending data has been read).
  • After local system initiates graceful close with shutdown and remote system has responded with "End of Data" notification (for example, TCP FIN), when no data currently available to receive.
  • When remote system terminates connection (for example, sent TCP RST), and lParam will contain WSAECONNRESET error value.


In order to receive Windows Messages with VB6 we will use a technique called Subclassing.To implement subclassing you have to Hook a window with Windows for additional Windows Messages which you want to receive. and when you dont want any further messages you can UnHook that

When you Hook a form or any other window then windows will start sending you messages and you can process messages which you are interested in. Before we implement Hook and UnHook lets go through some declarations for subclassing.

Click here to copy the following block
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long _
    , ByVal nIndex As Long _
    , ByVal dwNewLong As Long) As Long

Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long _
    , ByVal hwnd As Long _
    , ByVal Msg As Long _
    , ByVal wParam As Long _
    , ByVal lParam As Long) As Long

Public Const GWL_WNDPROC = (-4)
Public Const WINSOCKMSG = 1025
Public PrevProc As Long

For parameters check MSDN documentation.

Here are the steps to HOOK and UNHOOK the window.

To Hook/Unhook a window

Click here to copy the following block
Public Sub HookForm(F As Form)
  PrevProc = SetWindowLong(F.hwnd, GWL_WNDPROC, AddressOf WindowProc)
End Sub
Public Sub UnHookForm(F As Form)
  If PrevProc <> 0 Then
    SetWindowLong F.hwnd, GWL_WNDPROC, PrevProc
    PrevProc = 0
  End If
End Sub

To Hook a window you have to pass a handle of the window (in our case its a VB form) and you sepecify mwssagetype (in our case WINSOCK Messages) and very last one is pointer to Windows Procedure which has to be in a certain signature which windows expect(i.e number of parameter and type of parameters). Here is the implementation of Windows Procedure.

Click here to copy the following block
'Windows Message handler (Put this procedure in a module)
Public Function WindowProc(ByVal hwnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  If uMsg = WINSOCKMSG Then
    '//If winsock message is received then process
    ProcessMessage wParam, lParam
  Else
    WindowProc = CallWindowProc(PrevProc, hwnd, uMsg, wParam, lParam)
  End If
End Function
'our Winsock-message handler
Public Sub ProcessMessage(ByVal lFromSocket As Long, ByVal lParam As Long)
  Select Case lParam
  Case FD_CONNECT  'we are connected

  Case FD_WRITE  'we can write to our connection

  Case FD_READ  'we have data waiting to be processed

  Case FD_CLOSE  'the connection with server is closed

  End Select
End Sub

[3] Bind the socket

Before you start listining on a port you have to bind your server socket with a Port and a local IP address. In this tutorial we will use the following VB function to bind a socket.

Click here to copy the following block
Public Function SocketBind(ByVal lngSocket As Long, _
  ByVal lngLocalPort As Long, _
  Optional ByVal strLocalHost As String = "127.0.0.1") As Long

'********************************************************************************
'Purpose  :Binds the socket to the local address
'Return  :If no error occurs, returns zero. Otherwise, it returns SOCKET_ERROR.
'Arguments :lngSocket  - the socket to bind
'      strLocalHost - name or IP address of the local host to bind to
'      lngLocalPort - the port number to bind to
'********************************************************************************
  On Error GoTo errHandler

  Dim udtSocketAddress As SOCKADDR
  Dim lngReturnValue As Long
  Dim lngAddress   As Long

  SocketBind = SOCKET_ERROR

  With udtSocketAddress
    .sin_family = AF_INET

    '////////////////////////////////////////////////////////
    '[1] The strRemoteHost may contain the host name
    '  or IP address - GetAddressLong returns a valid
    '  value anyway
    '////////////////////////////////////////////////////////
    '.sin_addr = inet_addr(strLocalHost) 'Will accept only IP
    .sin_addr = GetAddressLong(strLocalHost)


    '////////////////////////////////////////////////////////
    '[2] convert the port number to the network byte ordering
    '////////////////////////////////////////////////////////
    .sin_port = htons(lngLocalPort)
    If .sin_port = INVALID_SOCKET Then
      SocketBind = INVALID_SOCKET
      Exit Function
    End If
  End With

  '//Set option to reuse the port if its already in use
  Dim optvalue As Boolean
  optvalue = True
  If setsockopt(lngSocket, SOL_SOCKET, SO_REUSEADDR, optvalue, Len(optval)) Then
    ShowLog "Error setting SO_REUSEADDR option : " & WSAGetLastError()
  End If

  SocketBind = bind(lngSocket, udtSocketAddress, LenB(udtSocketAddress))

  Exit Function
errHandler:
  SocketBind = SOCKET_ERROR
End Function

code Explaination

To bind s socket Binds the socket to a local address we can use bind api. Bind takes 3 arguments.

Click here to copy the following block
Declare Function bind Lib "ws2_32.dll" ( _
  ByVal s As Long, _
  ByRef addr As SOCKADDR, _
  ByVal namelen As Long) As Long

Parameters:

s
[in] Descriptor identifying an unbound socket.

addr
[in] Address to assign to the socket from the sockaddr structure.

Click here to copy the following block
Type SOCKADDR
  sin_family As Integer 'Address family
  sin_port As Integer  'Port
  sin_addr As Long    'IP address
  sin_zero As String * 8
End Type

namelen
[in] Length of the value in the name parameter, in bytes.

Returns Values :

If no error occurs, bind returns zero. Otherwise, it returns SOCKET_ERROR, and a specific error code can be retrieved by calling WSAGetLastError.

Convert Host or IP to long

SOCKADDR structure requires sin_addr which is Long. To retrive Long number from IP or Host name we can use following helper function.

Click here to copy the following block
Function GetHostByNameAlias(ByVal hostname$) As Long
  On Error Resume Next
  Dim phe&
  Dim heDestHost As HostEnt
  Dim addrList&
  Dim retIP&

  'If no error occurs, inet_addr returns an unsigned long Value
  'containing a suitable binary representation of the Internet
  'address given. If the string in the cp parameter does not
  'contain a legitimate Internet address,
  'for example if a portion of an "a.b.c.d" address exceeds 255,
  'then inet_addr returns the value INADDR_NONE.

  retIP = inet_addr(hostname)

  If retIP = INADDR_NONE Then
    phe = gethostbyname(hostname)
    If phe <> 0 Then
      MemCopy heDestHost, ByVal phe, hostent_size
      MemCopy addrList, ByVal heDestHost.h_addr_list, 4
      MemCopy retIP, ByVal addrList, heDestHost.h_length
    Else
      retIP = INADDR_NONE
    End If
  End If
  GetHostByNameAlias = retIP
  If Err Then GetHostByNameAlias = INADDR_NONE
End Function

[4] Listen on the socket

After successful socket bind you can call listen function so server can accept incoming connection requests. We will use following VB function to start listen.

Click here to copy the following block
Function StartListen(hSocket As Long, Port As Long) As Long
'********************************************************************************
'Purpose  :Turns a socket into a listening state.
'Return  :If no error occurs, returns zero. Otherwise, it returns SOCKET_ERROR.
'Arguments :lngSocketHandle - the socket to turn into a listening state.
'********************************************************************************
  Dim lngRetValue As Long

  If SocketBind(hSocket, Port) = SOCKET_ERROR Then StartListen = SOCKET_ERROR
  lngRetValue = listen(hSocket, SOMAXCONN)
  '
  StartListen = lngRetValue
End Function

code Explaination

Listen function takes 2 parameters.

Click here to copy the following block
Declare Function listen Lib "ws2_32.dll" ( _
    ByVal s As Long, _
    ByVal backlog As Long) As Long

'Other functions used

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

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

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

Parameters:

s
[in] Descriptor identifying a bound, unconnected socket.

backlog
[in] Maximum length of the queue of pending connections. If set to SOMAXCONN, the underlying service provider responsible for socket s will set the backlog to a maximum reasonable value. There is no standard provision to obtain the actual backlog value.

We can use following declaration

Click here to copy the following block
'Maximum queue length specifiable by listen.
Public Const SOMAXCONN = &H7FFFFFFF

Return Values :

If no error occurs, listen returns zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

[5] Accept a connection

Once socket goes into listining mode you can start accepting new connection requests from client. To accept connection request you can use following VB function to make your interface more clean. You can call SocketAccept function when you receive FD_ACCEPT event which means that client is waiting for connection. Once server accept incoming request for new connection client will receive FD_CONNECT event.

Click here to copy the following block
Public Function SocketAccept(ByVal lngSocketHandle As Long) As Long
'********************************************************************************
'Purpose  :Accepts a connection request, and creates a new socket.
'Return  :If no error occurs, returns the new socket's handle.
'      Otherwise, it returns INVALID_SOCKET.
'Arguments :lngSocketHandle - the listening socket.
'********************************************************************************

  Dim lngRetValue    As Long
  Dim udtSocketAddress  As SOCKADDR
  Dim lngBufferSize   As Long
  '
  'Calculate the buffer size
  lngBufferSize = LenB(udtSocketAddress)

  'Call the accept Winsock API function in order to create a new socket
  lngRetValue = accept(lngSocketHandle, udtSocketAddress, lngBufferSize)

  SocketAccept = lngRetValue
  'You can use collection to store handles of sockets (colConnections is a collection)
  If lngRetValue <> INVALID_SOCKET Then colConnections.Add lngRetValue
End Function

code Explaination

accept function takes 3 parameters.

Click here to copy the following block
Private Declare Function accept Lib "ws2_32.dll" ( _
  ByVal s As Long, _
  ByRef addr As SOCKADDR, _
  ByRef addrlen As Long) As Long

Parameters:

s
[in] Descriptor that identifies a socket that has been placed in a listening state with the listen function. The connection is actually made with the socket that is returned by accept.

addr
[out] Optional pointer to a buffer that receives the address of the connecting entity, as known to the communications layer. The exact format of the addr parameter is determined by the address family that was established when the socket from the sockaddr structure was created.

addrlen
[in, out] Optional pointer to an integer that contains the length of addr.

Return Values

If no error occurs, accept returns a value of type SOCKET that is a descriptor for the new socket. This returned value is a handle for the socket on which the actual connection is made. Otherwise, a value of INVALID_SOCKET is returned, and a specific error code can be retrieved by calling WSAGetLastError.

[6] Send and receive data

After connection is established you can start send/receive data. We will use Send following functions for this purpose.

Click here to copy the following block
Dim strData As String ' Global variable to hold received data
Dim ReadBuffer(1 to 1024) As Byte 'Buffer of received bytes 'Max 1k
'************************************************************
'Sending data
'************************************************************
Public Function SendData(ByVal lToSocket As Long, vMessage As Variant) As Long
  Dim TheMsg() As Byte, sTemp$
  TheMsg = ""
  Select Case VarType(vMessage)
  Case 8209  'byte array
    sTemp = vMessage
    TheMsg = sTemp
  Case 8  'string, if we recieve a string, its assumed we are linemode
    sTemp = StrConv(vMessage, vbFromUnicode)
  Case Else
    sTemp = CStr(vMessage)
    sTemp = StrConv(vMessage, vbFromUnicode)
  End Select
  TheMsg = sTemp
  If UBound(TheMsg) > -1 Then
    SendData = send(lToSocket, TheMsg(0), (UBound(TheMsg) - LBound(TheMsg) + 1), 0)
  End If
End Function

'************************************************************
'Reading data
'************************************************************
Function RecvDataToBuffer(lFromSocket As Long) As Long
  Dim X As Long
  'start reading the data
  Do
    'Read in 1K chunk , ReadBuffer is a bytearray
    X = recv(lFromSocket, ReadBuffer(1), 1024, 0)
    If X > 0 Then
      strData = strData & Left$(StrConv(ReadBuffer, vbUnicode), X)
    End If
    If X <> 1024 Or X = SOCKET_ERROR Then Exit Do
  Loop
  RecvDataToBuffer = X
End Function

code Explaination

Send function takes 4 parameters

Click here to copy the following block
Declare Function send Lib "ws2_32.dll" ( _
  ByVal s As Long, _
  ByVal buf As String, _
  ByVal lLen As Long, _
  ByVal flags As Long) As Long

Parameters:

s
[in] Descriptor identifying a connected socket.

buf
[in] Buffer containing the data to be transmitted.

len
[in] Length of the data in buf, in bytes.

flags
[in] Indicator specifying the way in which the call is made.

Return Values

If no error occurs, send returns the total number of bytes sent, which can be less than the number indicated by len. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

recv function takes 4 parameters

Click here to copy the following block
Declare Function recv Lib "ws2_32.dll" ( _
  ByVal s As Long, _
  ByVal buf As String, _
  ByVal lLen As Long, _
  ByVal flags As Long) As Long

Parameters:

s
[in] The descriptor that identifies a connected socket.

buf
[out] The buffer for incoming data.

len
[in] The length, in bytes, of buf.

flags
[in] The flag that specifies the way in which the call is
made.

Return Values

If no error occurs, recv returns the number of bytes received. If the connection has been gracefully closed, the return value is zero. Otherwise, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

[7] Disconnect

To assure that all data is sent and received on a connected socket before it is closed, an application should use shutdown to close connection before calling closesocket. For example, to initiate a graceful disconnect:

  1. Call WSAAsyncSelect to register for FD_CLOSE notification.
  2. Call shutdown with how=SD_SEND.
  3. When FD_CLOSE received, call recv until zero returned, or SOCKET_ERROR.
  4. Call closesocket.

Click here to copy the following block
Declare Function shutdown Lib "ws2_32.dll" ( _
  ByVal s As Long, _
  ByVal how As Long) As Long

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


Submitted By : Nayan Patel  (Member Since : 5/26/2004 12:23:06 PM)

Job Description : He is the moderator of this site and currently working as an independent consultant. He works with VB.net/ASP.net, SQL Server and other MS technologies. He is MCSD.net, MCDBA and MCSE. In his free time he likes to watch funny movies and doing oil painting.
View all (893) submissions by this author  (Birth Date : 7/14/1981 )


Home   |  Comment   |  Contact Us   |  Privacy Policy   |  Terms & Conditions   |  BlogsZappySys

© 2008 BinaryWorld LLC. All rights reserved.