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 |
Subject: [BETA] Minimal TCP/HTTP client using Winsock
Last edited by Brent on Oct 22nd, 2013, 5:19pm; edited 1 time in total
Last edited by Brent on Oct 22nd, 2013, 5:19pm; edited 1 time in total
Subject: Re: [BETA] Minimal TCP/HTTP client using Winsock
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:
For this to work you will also need to make paddrinfo global.
Richard.
Brent wrote: | ||
|
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.
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
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.
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
You cannot post new topics in this forumYou 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