When we were trying to get the Tempest based code connected, and I went on about bind and clients, etc...I decided to go ahead and bind the (TCP)socket myself. A couple of interesting things happened when I did that. First of all, I got into GatorHex's log file. Next, my own use of the TCP sockets stabilized dramatically. It is as solid as TCP claims to be. Almost all of the errors are timed out connections... (< 1.5%). Occasionally, I'll catch the ragged edge, and recv will return 0, but that is a good thing. (Except that somehow I missed the data!) You can close directly when recv returns 0.
Let me explain a little, because I think it may be significant. I am using the dll ws2_32.dll, same as you, but...I am using the only sockets available in my scenario...blocking sockets. Connect, as you know, will block on the client side until the connection is established. (It being the client's version of listen in that respect.) When that times out, the socket is sort of stuck in the ether because the client bind does not take place (I suspect) until the connection is (at least partially) established. Anyway, every bit of socket documentation I have studied says to reuse the socket, because (again, apparently) you cannot shutdown or close a socket that has never been bound. The practical upshot was that eventually, the thing would block...permanently. This usually took a large number of failed connections, but not always. My use of expired Norton Utilities may have been complicating things...I took care of that already, however. It has not blocked at all since then.
I tried the telnet because...(once again, this is my suspicion) my stack (including perhaps my, or GatorHex's ISP) considers it a trusted application. I think that this is probably the most relevant fact to come from the experiment - a proper (or is it trusted?) client can make the TCP connection, which gets the server going.
If I could hang a sniffer on my router, I could suss it out. My ISP is very specific about not wanting them on my network. I don't necessarily agree with that because I write alot of communications apps, and don't currently desire to run a game server through this skinny pipe anyway. That is more philosophy than practical info...they don't want it there in the first place. My method of writing communications programs is based on being able to have a neutral third party watching everything...makes post mortem analysis much easier for one thing.
Which is why I am using things that are not based on any of the code being used (besides DBPro and Winsock). I'm basically just trying to help, and also it helps my efforts, too. (Besides, I think that 3 or 4 people trying the same thing is probably sufficient.)
Here is the function that does that. I already loaded winsock2 and called WSAStartup and also EnumProtocols for giggles, but...that is not at all necessary because you can simply ask for the typical protocols by name, or number. Another thing is that the array timeServers is basically a group of UDTs to hold IPV4 addresses and names. It is a TCP client, and the one I used to attempt to connect to the Tempest server. It is dirt simple, and still somewhat experimental, although it adheres to the protocol well (such as it is), and the servers always (98.5%) answer with an ASCII string...perfect.
function jzGetDAYTIME(server as integer)
local temp_blk as integer
local ptr as dword
local ptr2 as dword
local i as integer
local buildstr as string = "Illegal server number."
if server < 0 or server > array count(timeServers())
exitfunction buildstr
endif
buildstr = "Could not open socket."
if this.timeSocket.descriptor < 1
this.timeSocket.descriptor = call dll(windows.winsock2, ...
"WSASocketA", AF_INET, SOCK_STREAM, ...
IPPROTO_TCP, NULL, ...
0x00000000, 0x00000000)
if this.timeSocket.descriptor > 0
inc this.total1
endif
endif
if this.timeSocket.descriptor > 0
ptr = get memblock ptr(windows.winsock2blk)
fill memory ptr, 0x00, 8192
write memblock word windows.winsock2blk, 0, int(AF_INET)
write memblock word windows.winsock2blk, 2, ...
int(IPPORT_DAYTIME << 8)
write memblock dword windows.winsock2blk, 4, ADDR_ANY
buildstr = "BIND failed - error #"
this.winsock2Return = call dll(windows.winsock2, "bind", ...
this.timeSocket.descriptor, ptr)
if this.winsock2Return <> 0
this.winsock2Return = call dll(windows.winsock2, "WSAGetLastError")
buildstr = buildstr + str$(this.winsock2Return)
this.winsock2Return = call dll(windows.winsock2, "closesocket", ...
this.timeSocket.descriptor)
if this.winsock2Return = 0
inc this.total2
endif
this.timeSocket.descriptor = 0
buildstr = buildstr + " close : " + str$(this.winsock2Return)
exitfunction buildstr
endif
fill memory ptr, 0x00, 8192
write memblock word windows.winsock2blk, 0, int(AF_INET)
write memblock word windows.winsock2blk, 2, ...
int(timeServers(server).ipaddress.port)
write memblock byte windows.winsock2blk, 4, timeServers(server).ipaddress._0
write memblock byte windows.winsock2blk, 5, timeServers(server).ipaddress._1
write memblock byte windows.winsock2blk, 6, timeServers(server).ipaddress._2
write memblock byte windows.winsock2blk, 7, timeServers(server).ipaddress._3
for i = 0 to len(timeServers(server).name) - 1
write memblock byte windows.winsock2blk, 8 + i, ...
asc(mid$(timeServers(server).name, i))
next i
i = 9 + len(timeServers(server).name)
buildstr = "Could not connect to " + timeServers(server).commonname
this.winsock2Return = call dll(windows.winsock2, "connect", ...
this.timeSocket.descriptor, ptr, i)
if this.winsock2Return <> 0
inc this.total3
this.winsock2Return = call dll(windows.winsock2, "WSAGetLastError")
buildstr = "connect error #" + str$(this.winsock2Return) + " " + ...
timeServers(server).commonname
rem This would not work if the socket was not already bound above...
this.winsock2Return = call dll(windows.winsock2, "closesocket", ...
this.timeSocket.descriptor)
if this.winsock2Return = 0
inc this.total2
endif
this.timeSocket.descriptor = 0
buildstr = buildstr + " close : " + str$(this.winsock2Return)
exitfunction buildstr
else
temp_blk = jzGetNextAvailableMemblockID(1)
make memblock temp_blk, 8192
ptr = get memblock ptr(temp_blk)
fill memory ptr, 0x00, 8192
this.winsock2Return = call dll(windows.winsock2, "recv", ...
this.timeSocket.descriptor, ...
ptr, 8192, 0x00000000)
if this.winsock2Return > 0
buildstr = jzMakeDBString(temp_blk, this.winsock2Return)
buildstr = buildstr + " " + timeServers(server).commonname
else
rem this.winsock2Return = call dll(windows.winsock2, "WSAGetLastError")
fill memory ptr, 0x00, 8192
this.winsock2Return = call dll(windows.winsock2, "getsockopt", ...
this.timeSocket.descriptor, ...
SOL_SOCKET, SO_ERROR, ptr, 8192)
if this.winsock2Return = 0
buildstr = jzMakeDBString(temp_blk, 8192)
buildstr = "recv error : " + buildstr
else
buildstr = "recv error #" + str$(this.winsock2Return) + " " + ...
timeServers(server).commonname
endif
endif
delete memblock temp_blk
this.winsock2Return = call dll(windows.winsock2, "shutdown", ...
this.timeSocket.descriptor, 0x0002)
if this.winsock2Return <> 0
this.winsock2Return = call dll(windows.winsock2, "WSAGetLastError")
buildstr = "shutdown error #" + str$(this.winsock2Return)
else
this.winsock2Return = call dll(windows.winsock2, "closesocket", ...
this.timeSocket.descriptor)
if this.winsock2Return <> 0
this.winsock2Return = call dll(windows.winsock2, "WSAGetLastError")
buildstr = "closesocket error #" + str$(this.winsock2Return)
else
this.timeSocket.descriptor = 0
inc this.total2
endif
endif
endif
endif
endfunction buildstr
I named "the manifest constant INADDR_ANY"...ADDR_ANY, but it is still all balls (dword 0). I also was successful binding to my private IP. Those are the only two choices; winsock2 will not allow you to bind to an address that is not the address assigned to one of the NICs in the machine it is running on. (No IP masquerading from Winsock2.)
So, I'm going to drink a gallon of coffee and dig into this one. Cheers.