'
' udp.inc
'----------------------------------------------------------------------------------------
'A series of winsock functions for network use
'Requires Ws2_32.lib

'UDP_Startup() returns 1 if successful, 0 if not
'UDP_Shutdown()
'UDP_GetMyIP() returns local machine's IP address as a string
'UDP_GetMyName() returns local machine's host name as a string
'UDP_ResolveIP() returns the ip address of a host name
'UDP_SendText(ip,port,text) sends <text> to <ip> on <port>
'UDP_Listen(port) listens on <port> and calls the HandleMessage function in your app
'
'  Your HandleMessage function must take as its parameters two strings - ip, and text
'  eg.	SUB HandleMessage(ip as string, text as string)
'			   print "User IP = " + ip
'			   print "User Message: " + text
'			   Return
'		   ENDSUB

'  The HandleMessage function will be sent the ip address of the sender and their message
'	whenever it's received.
'----------------------------------------------------------------------------------------

'Declares:

$use "Ws2_32.lib"

Declare Import, WSACleanup(), int
Declare Import, bind(sock:uint, SOCKADDR_IN:pointer, saLen:int),int
Declare Import, WSAStartup(version:word, WSADATA:pointer), int
Declare Import, closesocket(s:uint), int
Declare Import, inet_ntoa(inn:int), string
Declare Import, sendto(sock:uint, SendBuf:pointer, BufLen:int, flags/*0*/:int, SOCKADDR_IN:pointer, SOCKADDR_IN_len:int),int/*sent or SOCKET_ERROR*/
Declare Import, recvfrom(sock:uint, SendBuf:pointer, BufLen:int, opt flags/*0*/:int, opt SOCKADDR_IN=NULL:pointer, opt SOCKADDR_IN_len=0:pointer),int
Declare Import, socket(opt af=AF_INET:uint, opt s_type=SOCK_STREAM:int, opt protocol=IPPROTO_TCP:int), uint
Declare Import, htons(hostshort:word), int
Declare Import, inet_addr(cp:pointer), uint
Declare Import, CreateThread(lpThreadAttributes:pointer, dwStackSize:int, lpStartAddress:uint, lpParameter:uint, dwCreationFlags:uint, lpThreadId:pointer),uint
Declare Import, gethostbyname(hostname:string),HostEnt 
Declare Import, gethostname(hostname:string, namelen :int),int 
Declare Import, _RtlZeroMemory ALIAS RtlZeroMemory(dest AS POINTER,numBytes AS INT)
Declare Import, _CreateThread ALIAS CreateThread(lpThreadAttributes AS SECURITY_ATTRIBUTES,dwStackSize AS INT,lpStartAddress AS INT BYREF,lpParameter AS POINTER,dwCreationFlags AS INT,lpThreadId AS INT BYREF),INT
Declare Import, _CloseHandle ALIAS CloseHandle(hObject AS INT),INT

Const INVALID_SOCKET         = -1
Const AF_INET                = 2
Const INADDR_NONE            = -1
Const INADDR_ANY             = 0
Const SOCK_DGRAM             = 2
Const IPPROTO_UDP            = 17
Const WSADESCRIPTION_LEN     = 256
Const WSASYS_STATUS_LEN      = 128
Const UDM_SETRANGE32         = 0x400+111
Const WSA_MESSAGE  = 65535
Const MAX_MSG_SIZE = 1024
Const WSVER1 = 0x101 
Const WSVER2 = 0x202 

DEF WSA_NoName:string 
WSA_NoName = "Unknown"

Type SOCKADDR_IN
	WORD sin_family
	WORD sin_port
	UINT sin_addr
	CHAR sin_zero[8]
EndType

Type WSADataType 
   DEF wVersion:WORD  
   DEF wHighVersion:WORD  
   DEF strDescription[257]:ISTRING  
   DEF strSystemStatus[129]:ISTRING  
   DEF iMaxSockets:INT  
   DEF iMaxUdpDg:INT  
   DEF lpVendorInfo:POINTER  
EndType 

Type HostEnt 
    DEF h_name :POINTER 
    DEF h_aliases :POINTER 
    DEF h_addrtype:WORD 
    DEF h_length:WORD 
    DEF addrlist :POINTER 
EndType 

UINT listen_sock, send_sock

'------------EXTERNAL FUNCTIONS----------------------

Sub UDP_Startup(),int
	'Starts up
	DEF StartupData:WSADataType 
	if WSAStartup( WSVER2 , StartupData)=0
		Return 1
	EndIf
	Return 0
EndSub

Sub UDP_Shutdown()
	'Shuts down
	closesocket(listen_sock)
	closesocket(send_sock)
	listen_sock=0
	WSACleanup()
	Return
EndSub

Sub UDP_GetMyIP(),string
	'return the local host's IP address
	string ip
	HostEnt he
	he = gethostbyname(UDP_GetMyName()) 
	ip = inet_ntoa(he.*<POINTER>*<POINTER>addrlist)
	Return ip
EndSub

Sub UDP_GetMyName(),string
	'return the local host's name
    int retc
	string namestr
	namestr = ""
   	retc=gethostname(namestr, 256) 
	If retc <>0 
		namestr = WSA_NoName 
	Endif 
	Return namestr
EndSub

Sub UDP_ResolveIP(name as string),string
	'get an ip address from a host name
	POINTER he 
	he = gethostbyname(name)
	if he<>NULL
		return inet_ntoa(*<HostEnt>he.*<POINTER>*<POINTER>addrlist)
	Else
		return "Unknown"
	EndIf
EndSub


Sub UDP_SendText(ip as string, port as UInt, text as string),int
	'Sends text
	INT  size, a
	sockaddr_in sa
	if send_sock=0 then send_sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
	'preset the send-structure
	_RtlZeroMemory(&sa, len(sa))
	sa.sin_family = AF_INET
	sa.sin_port   = htons(port)
	sa.sin_addr   = inet_addr(ip)
	size=len(sa)
	a = sendto(send_sock, &text, len(text), 0, &sa, size)
	Return 1
EndSub


Sub UDP_Listen(port as UInt)
	'Sets up a listening port
	INT result, size
	UINT threadID, thread_handle
	sockaddr_in  sa
	result = FALSE
	'Close any existing listening port
	if listen_sock
		closesocket(listen_sock)
		listen_sock = 0
	endif
	'Create the listening port
	if listen_sock = 0 then listen_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)
	if listen_sock <> INVALID_SOCKET
		size = len(sa)
		_RtlZeroMemory(&sa, size)
		sa.sin_family = AF_INET
		sa.sin_port   = htons(port)
		sa.sin_addr   = INADDR_ANY
		'and bind it to a new thread to receive incoming data
		if bind(listen_sock, sa, size) = 0
			thread_handle = CreateThread(NULL, MAX_MSG_SIZE+64, &ThreadProc, listen_sock, 0, &threadID)
			_CloseHandle(thread_handle)
			result = thread_handle
		endif
	endif
	return result
EndSub

'--------------INTERNAL FUNCTION - do not call -------------------

sub ThreadProc(sock:uint),int
	'this function is running parallel (as thread),
	'and will always receive data, then notify the parent window
	INT size, received
	sockaddr_in sa
	ISTRING buff[MAX_MSG_SIZE]
	received = 1
	' if you close socket listen_sock then this thread will terminate
	while received > 0
		size = len(sa)
		' the socket is in blocking mode, so don't worry
		' about cpu usage
		received = recvfrom(sock, buff, MAX_MSG_SIZE-1, 0, &sa, size)
		if received > 0
			' remove invalid data
			buff[received] = 0
			' and call the HandleMessage function
			HandleMessage(inet_ntoa(sa.sin_addr),buff)
		endif
	wend
	return 0
endsub
