'Connect to a server, send and receive data
'the port and messages are specifically used for www.dict.org

 '% filename  "Dict_Send_Receive.exe"
  % filename  "t.exe"
  includepath "$/inc/"
 'include     "RTL64.inc"
 'include     "minwin.inc"
  include     "console.inc"

% SOCKET_ERROR          = -1
% SOCK_STREAM           = 1
% AF_INET               = 2 
% INVALID_SOCKET        = 0xFFFFFFFF
% INADDR_NONE           = 0xFFFFFFFF
% WSAEWOULDBLOCK        = 10035


type WSADATA 'Requires Windows Sockets 2.0
  wVersion       as word          'WORD                       
  wHighVersion   as word          'WORD                       
  szDescription  as char[257]     'char [WSADESCRIPTION_LEN+1] '257
  szSystemStatus as char[129]     'char [WSASYS_STATUS_LEN+1]  '129
  iMaxSockets    as short         'unsigned short
  iMaxUdpDg      as short         'unsigned short
  lpVendorInfo   as sys           'char far*
end type

'in_addr structure
type s_un_byte 
   s_b1 as byte
   s_b2 as byte
   s_b3 as byte
   s_b4 as byte
end type

type s_un_word 
   s_w1 as short
   s_w2 as short
end type

type in_addr 
  union
    s_un_byte s_un_b
    s_un_word s_un_w
    dword s_addr
  end union
end type
'#recordof in_addr

/*
type in_addr 
    s_un_byte s_un_b
    =
    s_un_word s_un_w
    =
    dword s_addr
end type
#recordof in_addr
*/


type sockaddr
    sa_family as short   ' address family, AF_xxx
    sa_data[14] as char  ' 14 bytes of protocol address};
end type

' IPv4 AF_INET sockets:
type sockaddr_in 
    sin_family as short   ' e.g. AF_INET, AF_INET6
    sin_port as short     ' e.g. htons(80)
    sin_addr as in_addr   ' see struct in_addr, below
    sin_zero[8] as char   ' zero this if you want to
end type

type hostent 
   char   *h_name
   sys     h_aliases   'char**
   short   h_addrtype
   short   h_length
   sys     h_addr_list 'char**
end type

'FBIONBIO
#define IOCPARM_MASK	0x7f     // Parameters must be < 128 bytes
#define IOC_IN	  0x80000000     // Copy in parameters
#define _IOW(x,y,t)	(IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
#define FIONBIO	_IOW('f', 126, unsigned long)

#define FIONBIO 0x80000000 or (4<<16) or (102<<8) or 126


'SELECTED PROCEDURES
====================

extern lib "ws2_32.dll" 

! WSAStartup (word wVersionRequested, WSADATA *lpWSAData) as int
! WSAGetLastError () as int
! socket(int af, int type, int protocol) as int
! closesocket(int s) as int
! WSACleanup () as int
! inet_addr (char *cp) as long 'cp: NULL-terminated character string for a number using ".'' (dotted) notation.
! htons(short hostshort) as short 'returns the value in TCP/IP network byte order
! connect(sys hSocket, sys name, int namelen) as int 'returns zero if no error
! send(sys s, char *buf, int len, int flags) as int
! recv(sys s, char *buf, int len, int flags) as int
! gethostbyname(char *name) as sys  ' ptr to struct hostent
! inet_ntoa(in_addr in) as char *
! ioctlsocket(sys s, long cmd, sys *argp) as int

end extern

! Sleep lib "kernel32.dll" (dword dwMilliseconds)

'Helper functions 
=================

sub endSocket(sys skt)
   closesocket(skt)
   WSACleanup()
   print "Socket closed" & cr      
end sub

function findAddress(string host)
   string ip_addr
   sys pHE
   sys address
   
   'try dotted IP address
   address = inet_addr(host)
   if address = INADDR_NONE then
     'no dotted IP address, try DNS
     print "Calling gethostbyname with "  host " waiting ..." & cr
     pHE = gethostbyname(host)
     if pHE = NULL then
        print "gethostbyname failed Error: "  WSAGetLastError() & cr
        return INADDR_NONE
     end if 
     hostent remoteHost at pHE  
     sys *ad = remoteHost.h_addr_list
     'only try first address
     if ad then
       ip_addr = inet_ntoa(byval *ad)
       address=inet_addr(ip_addr)
       if address = INADDR_NONE then
          print "gethostbyname failed Error: "  WSAGetLastError() & cr
          return INADDR_NONE
       end if      
     else
       print "failed ..."
       return INADDR_NONE
     end if
   end if 
      
   return address            
end function 


function connectHost(string host, int port)
   sys skt
   WSADATA wsa
   sockaddr_in server
   dword n[8] 'STACK SPILL ! 

   print cr & "Initialising Winsock..." & cr
    
   if WSAStartup(0x0202, wsa) then    
      print "Initialising Socket failed. Error Code : " WSAGetLastError() & cr        
      exit sub    
   end if         

   print "Initialised." & cr 

   ' Create a TCP socket 
   skt = socket(AF_INET , SOCK_STREAM , 0 )   

   if skt = INVALID_SOCKET then    
      print "Could not create socket. Error: "   WSAGetLastError() & cr
      exit sub    
   end if     
   print "Socket created: " & hex(skt) cr
    
   server.sin_addr.s_addr = findAddress(host)
   server.sin_family = AF_INET;
   server.sin_port = htons(port)
   '
   print "Server Address: " hex(server.sin_addr.s_addr) & cr
   print "Server Family:  " hex(server.sin_family) & cr
   print "Server Port:    " hex(server.sin_port) " (" hex(port) ")" & cr
   '
   'Connect to remote server
   if connect(skt , &server , sizeof(server))<0 then   
      print "Connect error: " & hex(WSAGetLastError()) & cr 
      endSocket(skt)
      skt=-1          
   end if
         
   return skt

end function


function sendData (sys s, string message)
   string msg = message & chr(13) & chr(10)
   
   int result = send(s , msg , len(msg) , 0)
   if result < 0 then 
      print result & ": Send failed" & cr
      endSocket(s)
   end if               
   return result 
end function

function receiveData(sys s) as string
   string serv_reply=""
   string buffer = ""
   int length=1024
   int recv_size
   
   unsigned long iMode=1 '0=blocking mode, 1=nonblocking mode 
   int iResult = ioctlsocket(s, FIONBIO, &iMode)
   if iResult != 0 then
     print "ioctlsocket failed with error: " iResult & cr
     endSocket(s)
     exit sub
   end if
   
   while 1
      serv_reply = nuls length  
      recv_size = recv(s , serv_reply , length , 0)    
 
      if recv_size  = SOCKET_ERROR then 
         int err = WSAGetLastError() 
         if err = WSAEWOULDBLOCK then Sleep(1000): continue while    
         print "recv failed" & cr
         serv_reply = "ERROR"
         endSocket(s)          
      else         
         buffer &= left(serv_reply, recv_size)
      end if
      if recv_size < length then exit while
   wend
   
   return buffer
end sub
 
 
sub main()

   sys skt
   string s_reply

   string host_name = "www.dict.org" '216.18.20.172
   'string host_name = "dict1.us.dict.org"  '216.18.20.172
   'string host_name = "216.18.20.172"
            
   skt = connectHost(host_Name, 2628)
   if skt < 0 then exit sub
   print "Connected" & cr & cr

   'Send some data    
   print "Data Send (SHOW DB)" & cr
   int result = sendData(skt ,"SHOW DB")    
   if result < 0 then exit sub            

   'Receive a reply from the server
   s_reply = receiveData(skt)
   print s_reply
   print cr  
   
   print "Send QUIT message" & cr

   result = sendData(skt , "QUIT")
   if result < 0 then exit sub

   s_reply = receiveData(skt)
   print s_reply
   print cr

   'close
   endSocket(skt)

end sub

main()


printl "Enter ..." : waitkey()