Subject: [BETA] Minimal TCP/HTTP client using Winsock
Code:
' Minimal TCP/HTTP client
' By Brent D. Thorn, 10/2010
' A minimalistic TCP client which just happens to send the right string of
' bytes to get an HTTP server to respond. In this example, an IP discovery tool
' returns your IP.
' PUBLIC DOMAIN

host$ = "www.b6sw.com"
port = 80
path$ = "/test/my-ip.shtml"
crlf$ = Chr$(13)+Chr$(10) ' Carriage return + line feed
request$ = _ ' Bytes sent to host server
    "GET "+path$+" HTTP/1.1"+crlf$+_
    "Host: "+host$+crlf$+_
    "User-Agent: Minimal client"+crlf$+_
    "Content-Length: 0"+crlf$+_
    crlf$
response$ = Space$(64) ' Buffer size - may be increased

    Open "ws2_32" For DLL As #wsock

    Call WinsockInit

    ' Start up Winsock
    e = WSAStartup(MAKEWORD(2, 2))
    If e <> 0 Then e$ = "WSAStartup" : GoTo [CloseWS]

    ' Resolve host name to its IP
    e =  ResolveIP(host$, port)
    If e <> 0 Then e$ = "ResolveIP" : GoTo [CloseWS]

    ' Create a TCP socket
    sock = socket(2, 1, 6) ' AF_INET = 2, SOCK_STREAM = 1, IPPROTO_TCP = 6
    If sock = -1 Then e$ = "socket" : e = WSAGetLastError() : GoTo [Cleanup]

    ' Connect to host server
    r = connect(sock)
    If r = -1 Then e$ = "connect" : e = WSAGetLastError() : GoTo [CloseSock]

    ' Send request string
    r = send(sock, request$, Len(request$), 0)
    If r > 0 _
      Then Print r;" bytes sent" _
      Else e$ = "send" : e = WSAGetLastError() : GoTo [CloseSock]

REM ' Flush output buffer
REM r = shutdown(sock, 1) ' SD_SEND = 1
REM If r = -1 Then e$ = "shutdown" : e = WSAGetLastError() : GoTo [CloseSock]

    ' Read in response
    Do
        r = recv(sock, response$, Len(response$), 0)

        Select Case
            Case r > 0 : Print "Response> "; Left$(response$, r)
            Case r = 0 : Print "Connection closed"
            Case Else : e$ = "recv" : e = WSAGetLastError()
        End Select
    Loop While r > 0

[CloseSock] ' Close socket
    r = closesocket(sock)

[Cleanup]
    Call WSACleanup

[CloseWS] ' Display any error message, close DLL
    If e <> 0 Then Print "Winsock error ";e;" in call to ";e$;"."
    Close #wsock

    End

Sub WinsockInit
' Initializes structs used in Winsock calls.
    Struct addrinfo, _
        aiflags As Long, _
        aifamily As Long, _
        aisocktype As Long, _
        aiprotocol As Long, _
        aiaddrlen As Long, _
        aicanonname As Ptr, _
        aiaddr As ULong, _
        ainext As ULong
    Struct sockaddr, _
        sinfamily As Short, _
        sinport As UShort, _
        sinaddr As ULong, _
        sinzero As Char[8]
    Struct WSAData, _
        wVersion As Word, _
        wHighVersion As Word, _
        szDescription As Char[257], _
        szSystemStatus As Char[129], _
        iMaxSockets As Word, _
        iMaxUdpDg As Word, _
        lpVendorInfo As Long
End Sub

Function WSAStartup( wVersionRequested )
    CallDLL #wsock, "WSAStartup", _
        wVersionRequested As Word, _
        WSAData As Struct, _
        WSAStartup As Long
End Function

Function ResolveIP( Host$, port )
' Host can be IP or name. Does a DNS lookup on latter and gets the first IP.
' Fills structs addrinfo and sockinfo for use later.

    addrinfo.aifamily.struct = 2 'AF_INET
    addrinfo.aisocktype.struct = 1 'SOCK_STREAM
    addrinfo.aiprotocol.struct = 6 'IPPROTO_TCP

    Struct local1, paddrinfo As ULong

    CallDLL #wsock, "getaddrinfo", _
        Host$ As Ptr, _
        _NULL As Long, _
        _NULL As Long, _
        local1 As Struct, _
        ResolveIP As Long

    If ResolveIP <> 0 Then Exit Function

    paddrinfo = local1.paddrinfo.struct
     addrinfo.struct = paddrinfo
     sockaddr.struct = addrinfo.aiaddr.struct
    sockaddr.sinport.struct = htons(port)

    ' Free memory allocated by getaddrinfo
    CallDLL #wsock, "freeaddrinfo", _
        paddrinfo As ULong, _
        ret As Void
End Function

Function htons( hostshort )
    CallDLL #wsock, "htons", _
        hostshort As Word, _
        htons As Word
End Function

Function socket( af, type, protocol )
    CallDLL #wsock, "socket", _
        af As Long, _
        type As Long, _
        protocol As Long, _
        socket As Long
End Function

Function connect( s )
    namelen = Len(sockaddr.struct)
    CallDLL #wsock, "connect", _
        s As Long, _
        sockaddr As Struct, _
        namelen As Long, _
        connect As Long
End Function

Function closesocket( s )
    CallDLL #wsock, "closesocket", _
        s As Long, _
        closesocket As Long
End Function

Sub WSACleanup
    CallDLL #wsock, "WSACleanup", _
        r As Void
End Sub

Function recv( s, ByRef buf$, buflen, flags )
    CallDLL #wsock, "recv", _
        s As Long, _
        buf$ As Ptr, _
        buflen As Long, _
        flags As Long, _
        recv As Long
End Function

Function send( s, buf$, buflen, flags )
    CallDLL #wsock, "send", _
        s As Long, _
        buf$ As Ptr, _
        buflen As Long, _
        flags As Long, _
        send As Long
End Function

Function shutdown( s, how )
    CallDLL #wsock, "shutdown", _
        s As Long, _
        how As Long, _
        shutdown As Long
End Function

Function WSAGetLastError()
    CallDLL #wsock, "WSAGetLastError", _
        WSAGetLastError As Long
End Function

Function MAKEWORD( b1, b2 )
    MAKEWORD = b1 Or (256 * b2)
End Function



Last edited by Brent on Oct 22nd, 2013, 5:19pm; edited 1 time in total
Subject: Re: [BETA] Minimal TCP/HTTP client using Winsock
Brent wrote:
Code:
    ' Free memory allocated by getaddrinfo
    CallDLL #wsock, "freeaddrinfo", _
        paddrinfo As ULong, _
        ret As Void

By freeing the memory allocated by getaddrinfo you are in fact (at least potentially) invalidating the contents of the sockaddr structure, which has been set to point into that memory!

For safety that code is best deleted from function ResolveIP and placed where the sockaddr structure is no longer needed. For example it could be moved into WSACleanup:

Code:
Sub WSACleanup
    ' Free memory allocated by getaddrinfo
    CallDLL #wsock, "freeaddrinfo", _
        paddrinfo As ULong, _
        ret As Void
    CallDLL #wsock, "WSACleanup", _
        r As Void
End Sub

For this to work you will also need to make paddrinfo global.

Richard.

Subject: Re: [BETA] Minimal TCP/HTTP client using Winsock
Richard, I'd prefer to keep the call to "freeaddrinfo" in "ResolveIP" because I'd bet that in a real program someone might want to call it more than once, and your solution would introduce a memory leak, in that case.

Anyway, I found a problem when I tried to run the program on my W7 64-bit system. It did not return any response from the server--just saying it sent the data, and then the connection was closed. The program worked flawlessly on 32-bit WXP, which I was using at the time of the OP.

I googled around for a while until I found someone with the same problem. I REM'd out the offending lines in my OP and it now works on W7.

Subject: Re: [BETA] Minimal TCP/HTTP client using Winsock
Brent wrote:
I'd prefer to keep the call to "freeaddrinfo" in "ResolveIP" because I'd bet that in a real program someone might want to call it more than once, and your solution would introduce a memory leak, in that case.

Running the original program under LBB it worked fine in Windows 8, but not in XP - and it turned out to be because of the memory being freed 'prematurely' resulting in sockaddr pointing to junk.

This code in MSDN doesn't call freeaddrinfo until the very end, immediately before the call to WSACleanup:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms737530.aspx

If you want to avoid a memory leak you could point a temporary sockaddr structure to the volatile memory, and then copy the relevant members into the global sockaddr before freeing the memory.

Richard.


Page 1 of 1


Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum

Full Version
Powered by phpBB © phpBB Group
Design by phpBBXS.Com | Lo-Fi Mod.