Engine Design: Benjamin's Amazing Winsock Code.
Although I'm not a fan of using DLLs, Benjamin's WinsockLib looks beautiful and utilizes the winsock dll that comes with the Windows standard distrubition. Http here I come.
` Structure sizes ------------------------
#constant SIZEOF_WSADATA 400
#constant SIZEOF_SOCKET 4
#constant SIZEOF_SOCKADDR 16
` ----------------------------------------
` Other constants ------------------------
#constant WINSOCK_DLL 255
#constant PORT_NUMBER 1337
#constant IP_ADDRESS "127.0.0.1"
#constant SOCKET_ERROR -1
#constant INVALID_SOCKET -1
` ----------------------------------------
` Load the Winsock DLL
load dll "ws2_32.dll", WINSOCK_DLL
` Make memory for variables
addr = make memory(SIZEOF_SOCKADDR)
` Initialize variables (to show what we're using)
socket = 0
clientSocket = 0
` Initialize Winsock (returns 0 on success, unlike most other functions here)
result = WSAStartup()
if result <> 0
print "WSAstartup failed: "+str$(result)
wait key
end
endif
` Make socket
socket = MakeSocket()
if socket = SOCKET_ERROR
print "MakeSocket failed: "+str$(WSAGetLastError())
wait key
end
endif
` Set address details
SetAddress(PORT_NUMBER, IP_ADDRESS, addr)
` Bind
result = Bind(socket, addr)
if result = SOCKET_ERROR
print "Bind failed: "+str$(WSAGetLastError())
wait key
end
endif
` Listen
result = Listen(socket)
if result = SOCKET_ERROR
print "Listen failed: "+str$(WSAGetLastError())
wait key
end
endif
` Wait for new connection (this blocks until a new connection)
print "Waiting for client..."
clientSocket = Accept(socket)
if clientSocket = INVALID_SOCKET
print "Accept failed: "+str$(WSAGetLastError())
wait key
end
endif
print "Client connected."
` Send a string
result = SendString(clientSocket, "Hello world.")
if result = SOCKET_ERROR
print "Send failed: "+str$(WSAGetLastError())
wait key
end
endif
wait key
end
` Winsock functions ----------------------
function WSAStartup() ` Initializes Winsock
wsdata = make memory(SIZEOF_WSADATA)
result = call dll(WINSOCK_DLL, "WSAStartup", 0x202, wsdata)
delete memory wsdata
endfunction result
function MakeSocket() ` Makes a socket
result = call dll(WINSOCK_DLL, "socket", 2, 1, 6)
endfunction result
function Bind(socket as dword, addrPtr as dword) ` Associates socket with address
result = call dll(WINSOCK_DLL, "bind", socket, addrPtr, SIZEOF_SOCKADDR)
endfunction result
function Listen(socket as dword) ` Allows socket to accept connections
result = call dll(WINSOCK_DLL, "listen", socket, 10)
endfunction result
function Accept(socket as dword) ` Accepts a connection (blocks until)
result = call dll(WINSOCK_DLL, "accept", socket, 0, 0)
endfunction result
function WSAGetLastError() ` Retrieves last error code
result = call dll(WINSOCK_DLL, "WSAGetLastError")
endfunction result
function htons(number as word) ` Converts value to big endian
result = call dll(WINSOCK_DLL, "htons", number)
endfunction result
function inet_addr(ip as string) ` Converts IP string to DWORD
result = call dll(WINSOCK_DLL, "inet_addr", ip)
endfunction result
` ----------------------------------------
` Other functions ------------------------
function SetAddress(port as word, ip as string, addrPtr as dword)
familyAndPort = 2 or (htons(port) << 16)
*addrPtr = familyAndPort
tempPtr = addrPtr + 4
*tempPtr = inet_addr(ip)
endfunction
function SendString(socket as dword, strData as string)
strData = strData + chr$(13) + chr$(10)
result = call dll(WINSOCK_DLL, "send", socket, strData, len(strData)+1, 0)
endfunction result
` ----------------------------------------
WinsockLib.dba
` Winsock DBP library coded by Benjamin Wharton -------------------
`
` LINKS
` Functions - http://msdn2.microsoft.com/en-us/library/ms741394.aspx
` Error codes - http://msdn2.microsoft.com/en-us/library/ms740668.aspx
` -----------------------------------------------------------------
` Structure sizes -------------------------------------------------
#constant SIZEOF_WSADATA 400
#constant SIZEOF_SOCKADDR 16
` -----------------------------------------------------------------
` Other constants -------------------------------------------------
#constant SOCKET_ERROR -1
#constant INVALID_SOCKET -1
` -----------------------------------------------------------------
` Winsock functions -----------------------------------------------
function InitWinsock() ` WSAStartup() - loads and initializes Winsock
if dll exist(WINSOCK_DLL) then exitfunction 0
load dll "ws2_32.dll", WINSOCK_DLL
wsdata = make memory(SIZEOF_WSADATA)
result = call dll(WINSOCK_DLL, "WSAStartup", 0x202, wsdata)
delete memory wsdata
endfunction result
function FreeWinsock() ` WSACleanup() - frees any allocated resources and DLL
if not dll exist(WINSOCK_DLL) then exitfunction
call dll WINSOCK_DLL, "WSACleanup"
delete dll WINSOCK_DLL
endfunction
function WSAGetLastError() ` WSAGetLastError() - retrieves last error code
result = call dll(WINSOCK_DLL, "WSAGetLastError")
endfunction result
function MakeSocket() ` socket() - makes a socket
result = call dll(WINSOCK_DLL, "socket", 2, 1, 6)
endfunction result
function CloseSocket(socket as dword) ` closesocket() - disconnects if connected, and frees socket
call dll WINSOCK_DLL, "closesocket", socket
endfunction
function Connect(socket as dword, addrPtr as dword) ` connect() - connects to a server
result = call dll(WINSOCK_DLL, "connect", socket, addrPtr, SIZEOF_SOCKADDR)
endfunction result
function Bind(socket as dword, addrPtr as dword) ` bind() - associates a socket with an ip/port combination
result = call dll(WINSOCK_DLL, "bind", socket, addrPtr, SIZEOF_SOCKADDR)
endfunction result
function Listen(socket as dword) ` listen() - allows socket to accept connections
result = call dll(WINSOCK_DLL, "listen", socket, 10)
endfunction result
function Accept(socket as dword) ` accept() - blocks until a connection is received
result = call dll(WINSOCK_DLL, "accept", socket, 0, 0)
endfunction result
function Send(socket as dword, bufferPtr as dword, amount as integer) ` send()
result = call dll(WINSOCK_DLL, "send", bufferPtr, amount, 0)
endfunction result
function Recv(socket, bufferPtr as dword, amount) ` recv()
result = call dll(WINSOCK_DLL, "recv", socket, bufferPtr, amount, 0)
endfunction result
function IpToDword(ip as string) ` inet_addr() - converts IP string to DWORD
result = call dll(WINSOCK_DLL, "inet_addr", ip)
endfunction result
function BigEndian(number as word) ` htons() - converts value to big endian
result = call dll(WINSOCK_DLL, "htons", number)
endfunction result
` -----------------------------------------------------------------
` Non-Winsock functions
function MakeAddress(ip as string, port as word) ` Allocates and sets address structure
addrPtr = make memory(SIZEOF_SOCKADDR)
familyAndPort = 2 || (BigEndian(port) << 16)
*addrPtr = familyAndPort
tempPtr = addrPtr + 4
*tempPtr = IpToDword(ip)
endfunction addrPtr
function FreeAddress(addrPtr as dword) ` Frees address structure
delete memory addrPtr
endfunction
` -----------------------------------------------------------------
` Composite functions ---------------------------------------------
function SendString(socket as dword, strData as string)
result = call dll(WINSOCK_DLL, "send", socket, strData, len(strData)+1, 0)
endfunction result
function RecvString(socket as dword)
strData = make memory(256)
result = call dll(WINSOCK_DLL, "recv", socket, strData, 256, 0)
` 0 means connection closed, -1 means error, anything else is amount recv'd
if result < 1
exitfunction ""
endif
for x = 0 to result -1
tempPtr = strData + x
tempString$ = tempString$ + chr$((*tempPtr) && 0xFF)
next x
delete memory strData
endfunction tempString$
` -----------------------------------------------------------------
Server.dba
` Constants for WinsockLib ---------------
#constant WINSOCK_DLL 255
` ----------------------------------------
` Other constants ------------------------
#constant IP_ADDRESS "127.0.0.1"
#constant PORT_NUMBER 1337
#constant MAX_LINES 20
` ----------------------------------------
` Initialize Winsock
result = InitWinsock()
if result > 0
FatalError("Failed to initialize Winsock: "+str$(result))
endif
` Make address structure
addrPtr = MakeAddress(IP_ADDRESS, PORT_NUMBER)
` Make socket
socket = MakeSocket()
if socket = INVALID_SOCKET
FatalError("Failed to make socket: "+str$(WSAGetLastError()))
endif
` Associate socket with address
result = Bind(socket, addrPtr)
if result = SOCKET_ERROR
FatalError("Failed to bind: "+str$(WSAGetLastError()))
endif
` Allow connections to our bound socket
result = Listen(socket)
if result = SOCKET_ERROR
FatalError("Listen failed "+str$(WSAGetLastError()))
endif
print "Waiting for client..."
sync
` Wait for client
clientSocket = Accept(socket)
if clientSocket = INVALID_SOCKET
FatalError("Accept failed: "+str$(WSAGetLastError()))
endif
` Wait for string
myString$ = RecvString(clientSocket)
if myString$ = ""
FatalError("Error / client closed connection! : "+str$(WSAGetLastError()))
endif
print "Message: " + myString$
` Send reply
result = SendString(clientSocket, "Pong!")
if result = SOCKET_ERROR
FatalError("Failed to send data: "+str$(WSAGetLastError()))
endif
print "Sent response: Pong!"
wait key
end
function FatalError(errorString as string) ` Displays message box then quits
exit prompt "Fatal error!", errorString
end
endfunction
Client.dba
` Constants for WinsockLib ---------------
#constant WINSOCK_DLL 255
` ----------------------------------------
` Other constants ------------------------
#constant IP_ADDRESS "127.0.0.1"
#constant PORT_NUMBER 1337
#constant MAX_LINES 20
` ----------------------------------------
` Initialize Winsock
result = InitWinsock()
if result > 0
FatalError("Failed to initialize Winsock: "+str$(result))
endif
` Make address structure
addrPtr = MakeAddress(IP_ADDRESS, PORT_NUMBER)
` Make socket
socket = MakeSocket()
if socket = INVALID_SOCKET
FatalError("Failed to make socket: "+str$(WSAGetLastError()))
endif
` Connect to server
result = Connect(socket, addrPtr)
if result = SOCKET_ERROR
FatalError("Failed to connect: "+str$(WSAGetLastError()))
endif
print "Connected to server."
` Send a string
result = SendString(socket, "Ping!")
if result = SOCKET_ERROR
FatalError("Failed to send data: "+str$(WSAGetLastError()))
endif
print "Sent message: Ping!"
` Wait for reply
myString$ = RecvString(socket)
if myString$ = ""
FatalError("Error / server closed connection! : "+str$(WSAGetLastError()))
endif
print "Response: " + myString$
wait key
end
function FatalError(errorString as string) ` Displays message box then quits
exit prompt "Fatal error!", errorString
end
endfunction
Quote: "Note that Accept/Connect/Send/Recv 'block' (sleep) until the operation completes, so this way you have to do operations in a certain sequence. When I have some more time I'll explain how to use non-blocking sockets.
It's also important to remember that my RecvString function will return a blank string when the connection is closed or when there is an error, which will need an alternative solution as you can't differentiate between a blank string and the aforementioned events this way."