Bay Six Software Forum Index Bay Six Software
Beyond the Basics
 
 FAQFAQ   SearchSearch   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

[BETA] Minimal TCP/HTTP client using Winsock

 
Post new topic   Reply to topic    Bay Six Software Forum Index -> Internet Programming
View previous topic :: View next topic  
Author Message
Brent
Site Admin


Joined: 01 Jul 2005
Posts: 797

PostPosted: Oct 27th, 2010, 3:11am    Post subject: [BETA] Minimal TCP/HTTP client using Winsock Reply with quote

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

_________________
Brent


Last edited by Brent on Oct 22nd, 2013, 5:19pm; edited 1 time in total
Back to top
View user's profile Send private message Send e-mail
RichardRussell
Full Member


Joined: 28 Jan 2012
Posts: 57
Location: Downham Market, UK

PostPosted: Oct 20th, 2013, 11:47am    Post subject: Re: [BETA] Minimal TCP/HTTP client using Winsock Reply with quote

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.
Back to top
View user's profile Send private message Visit poster's website
Brent
Site Admin


Joined: 01 Jul 2005
Posts: 797

PostPosted: Oct 22nd, 2013, 5:37pm    Post subject: Re: [BETA] Minimal TCP/HTTP client using Winsock Reply with quote

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.

_________________
Brent
Back to top
View user's profile Send private message Send e-mail
RichardRussell
Full Member


Joined: 28 Jan 2012
Posts: 57
Location: Downham Market, UK

PostPosted: Oct 22nd, 2013, 7:39pm    Post subject: Re: [BETA] Minimal TCP/HTTP client using Winsock Reply with quote

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.
Back to top
View user's profile Send private message Visit poster's website
Display posts from previous:   
Post new topic   Reply to topic    Bay Six Software Forum Index -> Internet Programming All times are GMT
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



Lo-Fi Version
Powered by phpBB © 2001, 2005 phpBB Group