 |
Bay Six Software Beyond the Basics
|
View previous topic :: View next topic |
Author |
Message |
Brent Site Admin
Joined: 01 Jul 2005 Posts: 793
|
Posted: Oct 27th, 2010, 3:11am Post 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 |
_________________ Brent
Last edited by Brent on Oct 22nd, 2013, 5:19pm; edited 1 time in total |
|
Back to top |
|
 |
RichardRussell Full Member
Joined: 28 Jan 2012 Posts: 57 Location: Downham Market, UK
|
Posted: Oct 20th, 2013, 11:47am Post 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. |
|
Back to top |
|
 |
Brent Site Admin
Joined: 01 Jul 2005 Posts: 793
|
Posted: Oct 22nd, 2013, 5:37pm Post 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. _________________ Brent |
|
Back to top |
|
 |
RichardRussell Full Member
Joined: 28 Jan 2012 Posts: 57 Location: Downham Market, UK
|
Posted: Oct 22nd, 2013, 7:39pm Post 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. |
|
Back to top |
|
 |
|
|
|
|
|
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
|
|
|