#cs ---------------------------------------------------------------------------- AutoIt Version: 3.3.0.0 Author: Hyperzap. Design concepts were first coined by Kip in his "Event driven TCP UDF" Zatorg's Asynchronous Sockets UDF is also used. Script Function: This UDF is a re-write of Kip's 'Event driven TCP UDF'. Thx Kip. It aims to generate simple TCP, event driven functionality for P2P programs, As opposed to client-server oriented communication as in Kip's UDF. Evidently, this functionality is not possible in Kips original releases. Apart from the obvious conponents of such a code, like Recv, Send, Connect, Listen, other important P2P routines have been coded. These include things like; Universal identifiers, bootstrapping mechanisms, peers discovery, and message routing. MINOR DRAWBACKS: You can never use the string #8i8# or #8i8TERMINATE# in your communicatons. You cannot use the # (hash) symbol in either your Node ID or any part of a long range message. #ce ---------------------------------------------------------------------------- $UDF_Version = "1.31 STABLE" $console_out = True $STANDARD_MESSAGE_LIFE = 6 #cs Functions: _P2P_Start_Node( Node Identifier, Port, Max peers, Bootstrap mode, Boostrap max, local/global, IP) _P2P_Stop_Node() _P2P_Connect( IP) _P2P_Send( Socket, Data) _P2P_Disconnect_Peer( Socket) _P2P_Send_Message( Address, Data) _P2P_Register_Event($iEvent, $sFunction) Register event values: $P2P_AUX_DATA ; Function ($hSocket, $DataType, $Data);When things like IP and Node identifier are discovered. $P2P_MESSAGE ; Function ($hSocket, $message, $iError) ;When long distance messages are recieved. $P2P_RECEIVE ; Function ($hSocket, $sReceived, $iError) $P2P_CONNECT ; Function ($hSocket, $iError) $P2P_DISCONNECT ; Function ($hSocket, $iError) $P2P_NEWCONNECTION ; Function ($hSocket, $iError) Also, please call the function peer_broadcast() periodically in your program. Failing to do this will Seriously cripple the bootstrapping mechanism (unless you program is ALWAYS recieving data at least once every 40 seconds, In which case the mechanism will trigger automatically). #ce Global Const $FD_READ = 1 Global Const $FD_WRITE = 2 Global Const $FD_OOB = 4 Global Const $FD_ACCEPT = 8 Global Const $FD_CONNECT = 16 Global Const $FD_CLOSE = 32 Global $hWs2_32 = -1 ;Kip, What is this? (I left it in case it was something important) Global Const $TCP_SEND = 1 Global Const $TCP_RECEIVE = 2 Global Const $TCP_CONNECT = 4 Global Const $TCP_DISCONNECT = 8 Global Const $TCP_NEWCLIENT = 16 Global Const $IPLOC_LOCAL = 64 Global Const $IPLOC_GLOBAL = 128 Global Const $P2P_MESSAGE = 256 Global Const $P2P_RECEIVE = 512 Global Const $P2P_CONNECT = 1024 Global Const $P2P_DISCONNECT = 2048 Global Const $P2P_NEWCONNECTION = 4096 Global Const $P2P_AUX_DATA = 8192 ;NOTE - SOCKET ARRAYS & OTHER ARRAYS WILL BE DECLARED IN THE '_P2P_START_NODE' FUNCTION. ; THIS IS BECAUSE I DO NOT KNOW HOW TO DECLARE AN ARRAY AT THE BEGINNING WITHOUT ; DEFINING A DIMENSION RANGE(I do not know max peers at this stage). ; I'M SURE THERE IS A WAY. WHEN I FIND IT OUT, I WILL PUT IT IN THE NEXT RELEASE. Global $total_connected = 0 Global $max_connections Global $Listening_Socket Global $node_IP Global $node_Port Global $node_Identifier Global $node_ext_IP Global $Bootstrap_mode Global $Bootstrap_max Global $peer_timer Global $total_ID_known = 0 Global $Known_ID[200] Global $connectfunc = "" Global $recievefunc = "" Global $disconnectfunc = "" Global $newconnectionfunc = "" Global $messagefunc = "" global $auxfunc = "" Global $Main_Socket_Address = "" TCPStartup() Global Const $__TCP_WINDOW = GUICreate("Async Sockets UDF") ;I know, Copy paste. ; #FUNCTION# ;=============================================================================================================== ; ; Name...........: _P2P_Start_Node ; Description ...: Initializes the P2P Node. Starts listening for connections. ; Syntax.........: _P2P_Start_Node( $node_id, $Port, $Max_peers, $Bootstrapmode, $bootstrapmax, $location, $IP="0.0.0.0") ; Parameters ....: $node_id - The Unique Identifier assigned to you, as a user of the program. Should not change ; between excutions for a specific user. This ID is used as an address for long range messages. ; $Port - The port to listen for incoming connections on. ; $Max_peers - The maximum number of peers that can be connected to your node at any one time. ; $bootstrapmode - Either 0 or 1. If 0, this UDF will not automatically connect you to other nodes. ; $bootstrapmax - The number of peers connected before the UDF will stop bootstrapping. (autoconnecting) ; $location - Either 0 or 1. Put 0 if working on a LAN or WLAN. Put 1 if your node works over the ; Internet. ; $IP - (Optional) The IP address to host the node on. Really, the default value will work 99% ; of the time. You shouldn't change it unless you know exactly what your doing. ; Return values .: Err...Nothing. ; Author ........: Hyperzap and Kip. ; Modified.......: ; Remarks .......: Only 1 P2P node can be created per script. By why on earth would you need another? ; Related .......: ; Link ..........; ; Example .......; See my 'example scripts' post. (If applicable) ; ; ;========================================================================================================================== Func _P2P_Start_Node( $node_id, $Port, $Max_peers, $Bootstrapmode, $bootstrapmax, $location, $IP="0.0.0.0") local $startuptimer = TimerInit() $Listening_Socket = ___ASocket() $Main_Socket_Address = $Listening_Socket ___ASockSelect( $Listening_Socket, $__TCP_WINDOW, 0x0400, $FD_ACCEPT) GUIRegisterMsg( 0x0400, "Listensocket_data_" ) ___ASockListen( $Listening_Socket, $IP, $Port ) if $Bootstrapmode = 0 then $Bootstrap_mode = $IPLOC_LOCAL if $Bootstrapmode = 1 then $Bootstrap_mode = $IPLOC_GLOBAL $bootstrap_max = $bootstrapmax $max_connections = $Max_peers $node_IP = $IP $node_Port = $Port $node_Identifier = $node_id $peer_timer = TimerInit() if $location = 1 then $node_ext_IP = _Get_IP() ;GetIP command. if $location = 0 then $node_ext_IP = @IPAddress1 Global $Socket_Handle_array[$Max_peers + 1] Global $Node_Identifier_array[$Max_peers + 1] Global $Node_IP_array[$Max_peers + 1] Global $Node_Peer_Reachable_list[$Max_peers + 1] For $x = 0 to $Max_peers step 1 $Socket_Handle_array[$x] = -1 $Node_Identifier_array[$x] = -1 $Node_IP_array[$x] = -1 $Node_Peer_Reachable_list[$x] = -1 Next if $console_out = True then ConsoleWrite( @CRLF & @CRLF & "P2P_: Node started") if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Port: " & $node_Port) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Ext IP: " & $node_ext_IP) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Max peers: " & $max_connections) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Identifier: " & $node_Identifier) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Engine Version: " & $UDF_version) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Startup time: " & TimerDiff( $startuptimer) & "ms" & @CRLF) EndFunc ; #FUNCTION# ;=============================================================================== ; ; Name...........: _P2P_Stop_Node ; Description ...: Stops the node, and closes all connections. ; Syntax.........: _P2P_Stop_Node() ; Parameters ....: ; Return values .: None. ; Author ........: Hyperzap ; Modified.......: ; Remarks .......: ; Related .......: ; Link ..........; ; Example .......; ; ; ;========================================================================================== Func _P2P_Stop_Node() ___ASockShutdown($Listening_Socket) TCPCloseSocket($Listening_Socket) For $x = 0 to $max_connections step 1 $Socket_Handle_array[$x] = -1 $Node_Identifier_array[$x] = -1 $Node_IP_array[$x] = -1 $Node_Peer_Reachable_list[$x] = -1 Next $max_connections = 0 $Listening_Socket = "" $node_IP = "" $node_Port = "" $node_Identifier = -1 $node_ext_IP = "" $Bootstrap_mode = "" $Bootstrap_max = 0 $connectfunc = "" $recievefunc = "" $disconnectfunc = "" $newconnectionfunc = "" $messagefunc = "" $auxfunc = "" if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Engine shutdown successful.") EndFunc Func Listensocket_data_($hWnd, $iMsgID, $WParam, $LParam) Local $Socketinquestion = $WParam Local $iError = ___HiWord( $LParam ) Local $iEvent = ___LoWord( $LParam ) Abs($hWnd) ; Stupid AU3Check... If $iMsgID = 0x400 Then If $iEvent = $FD_ACCEPT Then If Not $iError Then $arrayslot = Findfreearrayslot() if $arrayslot = "error" then ;No more spots for new peers. Connection dropped. $newsocket = TCPAccept($Socketinquestion) TCPSend( $newsocket, "#8i8#NOSLOT#8i8TERMINATE#") TCPCloseSocket( $newsocket) EndIf $newsocket = TCPAccept($Socketinquestion) ___ASockSelect($newsocket, $__TCP_WINDOW, 0x0400 + $arrayslot, BitOR($FD_READ, $FD_CONNECT, $FD_CLOSE)) GUIRegisterMsg( 0x0400 + $arrayslot, "Opensocket_data_" ) $Socket_Handle_array[$arrayslot] = $newsocket $Node_Identifier_array[$arrayslot] = -1 $Node_IP_array[$arrayslot] = -1 TCPSend( $newsocket, "#8i8#IP#" & $node_ext_IP & "#8i8TERMINATE#") TCPSend( $newsocket, "#8i8#ID#" & $node_Identifier & "#8i8TERMINATE#") peer_broadcast_to_peer( $newsocket) $total_connected += 1 Call( $newconnectionfunc, $newsocket, $iError) Else Call( $newconnectionfunc, 0, $iError) EndIf ElseIf $iEvent = $FD_CONNECT Then Call( $connectfunc, $newsocket, $iError) EndIf EndIf EndFunc Func Findfreearrayslot() $newConnNum = -1 For $x = 1 To $max_connections If $Socket_Handle_array[$x] = -1 Then $newConnNum = $x ExitLoop EndIf Next If $newConnNum = -1 Then Return "error";Didn't want it to be a number. Return $newConnNum EndFunc Func Opensocket_data_( $hWnd, $iMsgID, $WParam, $LParam ) Local $iError = ___HiWord( $LParam ) Local $iEvent = ___LoWord( $LParam ) Abs($hWnd) local $Array_Slot = $iMsgID-0x400 ;No more loops to slow down message delievery! local $SocketID = $Socket_Handle_array[$Array_Slot] if $SocketID = -1 then return 0 Switch $iEvent Case $FD_READ $rawrecv = TCPRecv($SocketID, 1024*5) recvprocess( $rawrecv, $SocketID, $Array_Slot, $iError) Case $FD_CLOSE ___ASockShutdown($SocketID) TCPCloseSocket($SocketID) Call( $disconnectfunc, $SocketID, $iError) $Socket_Handle_array[$Array_Slot] = -1 $Node_Identifier_array[$Array_Slot] = -1 $Node_IP_array[$Array_Slot] = -1 $Node_Peer_Reachable_list[$Array_Slot] = -1 $total_connected -= 1 if TimerDiff( $peer_timer) > 40000 Then peer_broadcast() EndIf Case $FD_CONNECT If $iError Then if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Connection failed: " & $SocketID) $Socket_Handle_array[$Array_Slot] = -1 $Node_Identifier_array[$Array_Slot] = -1 $Node_IP_array[$Array_Slot] = -1 $Node_Peer_Reachable_list[$Array_Slot] = -1 $total_connected -= 1 Call( $connectfunc, $SocketID, $iError) Else Call( $connectfunc, $SocketID, $iError) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Connection established: " & $SocketID) TCPSend( $Socket_Handle_array[$Array_Slot], "#8i8#IP#" & $node_ext_IP & "#8i8TERMINATE#") TCPSend( $Socket_Handle_array[$Array_Slot], "#8i8#ID#" & $node_Identifier & "#8i8TERMINATE#") peer_broadcast_to_peer( $Socket_Handle_array[$Array_Slot]) EndIf if TimerDiff( $peer_timer) > 40000 Then peer_broadcast() EndIf EndSwitch EndFunc Func recvprocess( $rawrecv, $SocketID, $Array_Slot, $iError) if stringlen($rawrecv) < 1 then return 0 local $Single_Data = StringSplit( $rawrecv, "#8i8TERMINATE#", 1) for $k = 1 to $Single_Data[0] step 1 if $Single_Data[$k] = "" then ContinueLoop ;Probably isn't nessesary. if $Single_Data[$k] = " " then ContinueLoop ;Disregard noise and crappy coders. if StringInStr( $Single_Data[$k], "#8i8#", 1) > 0 Then node_data_process( $SocketID, $Single_Data[$k], $Array_Slot, $iError);must be node info to process. Else Call( $recievefunc, $SocketID, $Single_Data[$k], $iError) ;Sends to user script EndIf Next if TimerDiff( $peer_timer) > 40000 Then peer_broadcast() EndIf EndFunc Func node_data_process( $SocketID, $Data, $Array_Slot, $iError) local $split = StringSplit( $Data, "#") if $split[3] = "IP" then $Node_IP_array[$Array_Slot] = $split[4] Call( $auxfunc, $SocketID, "IP", $split[4]) EndIf if $split[3] = "ID" then $Node_Identifier_array[$Array_Slot] = $split[4] Call( $auxfunc, $SocketID, "ID", $split[4]) EndIf if $split[3] = "PEER" then bootstrap( $SocketID, $split[4], $Array_Slot, $iError) if $split[3] = "IDLIST" then $Node_Peer_Reachable_list[$Array_Slot] = $split[4] Call( $auxfunc, $SocketID, "NODE-REACHABLE", $split[4]) EndIf if $split[3] = "MESSAGE" then ;#8i8#MESSAGE Route_Message( $SocketID, $Data, $Array_Slot) EndIf EndFunc Func bootstrap( $SocketID, $split, $Array_Slot, $iError) if $Bootstrap_mode = $IPLOC_LOCAL then return 9;Bootstrapping disabled. if $total_connected >= $Bootstrap_max then return 8;Already connected to enough peers. local $connectpeers = Round( ($Bootstrap_max/$total_connected/2), 0) ;calculate how many peers we should attempt to connect to. if $connectpeers < $Bootstrap_max and $connectpeers = 0 then $connectpeers = 1 if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Bootstrap stats: " & $connectpeers & ":" & $total_connected & "/" & $Bootstrap_max) $peers = StringSplit( $split, ";") For $d = 1 to $connectpeers step 1 $error = False $rndarray = Random( 1, $peers[0], 1) if $peers[$rndarray] = "" or $peers[$rndarray] = " " then ContinueLoop For $u = 0 to $max_connections step 1 ;checks to see if already connected if $peers[$rndarray] = $Node_IP_array[$u] then if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Bootstrap: connectee already connected! reselecting...") $error = True ExitLoop EndIf Next if $error = True then ContinueLoop _P2P_Connect( $peers[$rndarray]) Next EndFunc Func peer_broadcast() $peer_timer = TimerInit() local $peers = "" local $IDLIST = "" For $a = 0 to $max_connections step 1 if $Node_IP_array[$a] <> -1 then $peers &= $Node_IP_array[$a] & ";" if $Node_Identifier_array[$a] <> -1 then $IDLIST &= $Node_Identifier_array[$a] & ";" Next _P2P_Broadcast( "#8i8#PEER#" & $peers) _P2P_Broadcast( "#8i8#IDLIST#" & $IDLIST) EndFunc Func peer_broadcast_to_peer( $socket) local $peers = "" local $IDLIST = "" For $a = 0 to $max_connections step 1 if $Node_IP_array[$a] <> -1 then $peers &= $Node_IP_array[$a] & ";" if $Node_Identifier_array[$a] <> -1 then $IDLIST &= $Node_Identifier_array[$a] & ";" Next TCPSend( $socket, "#8i8#PEER#" & $peers & "#8i8TERMINATE#") TCPSend( $socket, "#8i8#IDLIST#" & $IDLIST & "#8i8TERMINATE#") EndFunc Func Route_Message( $Socket, $Message, $array_slot) ;process long distance messages if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message##########Routing started") local $timer = TimerInit() local $split = StringSplit( $Message, "#") if $split[0] < 6 then return 1 Local $ttl = $split[4] Local $address = $split[5] Local $identifier = $split[6] Local $begin = "#8i8#MESSAGE#" & $ttl & "#" & $address & "#" & $identifier & "#" Local $cutlen = StringLen( $Message) - StringLen( $begin) if idknown( $split[6]) = True then ;Have we seen it before? if yes-Terminate. if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message-ID known.") if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message##########Routing Finished: " & Round( TimerDiff( $timer), 1)) return 1 EndIf if $address = $node_Identifier then ;Is it for us? if yes-deliever. local $messagetrim = StringRight( $Message, $cutlen) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message-Inbound") if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message##########Routing Finished: " & Round( TimerDiff( $timer), 1)) call( $messagefunc, $Socket, $messagetrim, 0) return 1 EndIf if $ttl = 0 then ;Is it too old? if yes-Terminate. if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message-TTL Exceeded.") if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message##########Routing Finished: " & Round( TimerDiff( $timer), 1)) return 1 EndIf ;Time to route the message. $messagefull = "#8i8#MESSAGE#" & ($ttl-1) & "#" & $address & "#" & $identifier & "#" & StringRight( $Message, $cutlen) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Starting First level Routing: " & $total_connected) For $qwe = 0 to $max_connections step 1 ;Send if we are directly connected to the destination. if $address = $Node_Identifier_array[$qwe] then _P2P_Send( $Socket_Handle_array[$qwe], $messagefull) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message-Destination located.") if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message##########Routing Finished: " & Round( TimerDiff( $timer), 1)) return 1 EndIf Next if $Socket = -1 Then ;If we created the message: Flood to all peers _P2P_Broadcast( $messagefull) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message##########Routing Finished: " & Round( TimerDiff( $timer), 1)) return 1 Else ;Or check every peer to see if it is connected to destination. For $qwe = 0 to $max_connections step 1 ;See if our peers are connected to the destination. if $Socket_Handle_array[$qwe] = -1 then ContinueLoop if StringInStr( $Node_Peer_Reachable_List[$qwe], $address) > 0 Then _P2P_Send( $Socket_Handle_array[$qwe], $messagefull) if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message-2nd Level destination located. ") if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message##########Routing Finished: " & Round( TimerDiff( $timer), 1)) return 1 EndIf Next EndIf For $qwe = 0 to $max_connections step 1 ;So the destination is not reachable within two levels. Time to pass it on. Don't pass it on to sender or sender's peers. if $Socket_Handle_array[$qwe] = -1 then ContinueLoop if $node_Identifier_array[$qwe] = $node_Identifier_array[$array_slot] then ContinueLoop if StringInStr( $Node_Peer_Reachable_List[$array_slot], $node_Identifier_array[$qwe]) > 0 Then ContinueLoop _P2P_Send( $Socket_Handle_array[$qwe], $messagefull) Next if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message##########Routing Finished: " & Round( TimerDiff( $timer), 1)) EndFunc ; #FUNCTION# ;=============================================================================== ; ; Name...........: _P2P_Send_Message ; Description ...: Sends a long range message to a peer somewhere in the network. ; Syntax.........: _P2P_Send_Message( $Identifier, $data) ; Parameters ....: $Identifier - The node Identifier/ID/Address of the destination. ; $Data - Data/Message to send. ; Return values .: ; Author ........: Hyperzap ; Modified.......: ; Remarks .......: This, as opposed to the _P2P_Send function, will send a message to the ; destination regardless of whether they are connected or not, That is, you need ; not be directly TCP connected to someone for the message to reach them. This ; System works making your message 'hop' from node to node until one knows where ; your destination is. At that point, it is sent directly to the destination To ; be unpacked and processed.Please note that there is no way to know if the message ; reached it's destination, but if it is in range and online it will reach it ; 99.5% of the time. ; Related .......: ; Link ..........; ; Example .......; ; ; ;========================================================================================== Func _P2P_Send_Message( $IDaddress, $INdata) local $messageID = Round( ((Random( 0, 99999999999, 1)*@YDAY)/@MIN)*(Random(5, 1376, 1)/(@SEC*100)), 0) ;As random as it'l ever get. local $ttl = $STANDARD_MESSAGE_LIFE local $fullmessage = "#8i8#MESSAGE#" & $ttl & "#" & $IDaddress & "#" & $messageID & "#" & $INdata & "#8i8TERMINATE#" if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Message generated: " & $messageID) Route_Message( -1, $fullmessage, -1) EndFunc Func idknown ($ID) for $IDcount = 0 to $total_ID_known step 1 if $Known_ID[$IDcount] = $ID then Return True Next $total_ID_known += 1 if $total_ID_known > 198 then $total_ID_known = 0 $Known_ID[$total_ID_known] = $ID return False EndFunc ; #FUNCTION# ;=============================================================================== ; ; Name...........: _P2P_Send ; Description ...: Sends data to a peer. ; Syntax.........: _P2P_Send( $Socket, $Data) ; Parameters ....: $Socket - peer socket handle. ; $Data - Data to send. ; Return values .: ; Author ........: Hyperzap ; Modified.......: ; Remarks .......: This UDF will automatically keep your individual messages separate, So ; Don't worry about adding delays or message-split-buffer systems. ; Related .......: ; Link ..........; ; Example .......; ; ; ;========================================================================================== Func _P2P_Send( $Socket, $Data) TCPSend( $Socket, $Data & "#8i8TERMINATE#") EndFunc ; #FUNCTION# ;=============================================================================== ; ; Name...........: _P2P_Disconnect_Peer ; Description ...: Disconnects a peer (someone your connected to) from the system. ; Syntax.........: _P2P_Disconnect_Peer($Socket) ; Parameters ....: $Socket - Socket of peer. ; Return values .: Success - True ; Failure - False ; Author ........: Hyperzap ; Modified.......: ; Remarks .......: The peer socket ($Socket) is the socket handler of ANY peer connected. ; Related .......: ; Link ..........; ; Example .......; ; ; ;========================================================================================== Func _P2P_Disconnect_Peer( $socket) local $it_socket = "" local $array_slot = "" For $count = 0 to $max_connections step 1 if $Socket_Handle_array[$count] = $socket then $it_socket = $socket $array_slot = $count ExitLoop EndIf Next if $it_socket = "" then return False ___ASockShutdown($it_socket) TCPCloseSocket($it_socket) $Socket_Handle_array[$Array_Slot] = -1 $Node_Identifier_array[$Array_Slot] = -1 $Node_IP_array[$Array_Slot] = -1 $Node_Peer_Reachable_list[$Array_Slot] = -1 if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Peer " & $it_socket & " closed.") return true EndFunc ; #FUNCTION# ;=============================================================================== ; ; Name...........: _P2P_Connect ; Description ...: Initiates a new connection to another node. ; Syntax.........: __P2P_Connect( $Conn_IP) ; Parameters ....: $Conn_IP - The IP address to connect to. ; Return values .: 0, 1, 2, 3 indicate failure due to criteria. Listen to the ; _P2P_Register_Event( $P2P_CONNECT etc to determine if it connected or not. ; Author ........: Hyperzap ; Modified.......: ; Remarks .......: ; Related .......: ; Link ..........; ; Example .......; ; ; ;========================================================================================== Func _P2P_Connect( $Conn_IP) For $u = 0 to $max_connections step 1 ;checks to see if already connected if $Conn_IP = $Node_IP_array[$u] then if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Unable to connect-IP is already connected!") return 1 EndIf Next if $Conn_IP = "" then Return 0 if $Conn_IP = " " then Return 0 if $Conn_IP = "0" then Return 0 if $Conn_IP = ";" then Return 0 if $Conn_IP = @CRLF then Return 0 $arrayslot = Findfreearrayslot() if $arrayslot = "error" then if $Listening_Socket = "" Then if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Unable to connect-node services offline!") Return 3 EndIf if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Unable to connect-peer limit reached!") return 2 ;no free slots to put your new connection! EndIf if $console_out = True then ConsoleWrite( @CRLF & "P2P_: Attempting Connection: " & $Conn_IP) local $SocketID = ___ASocket() ___ASockSelect( $SocketID, $__TCP_WINDOW, 0x400 + $arrayslot, BitOR( $FD_CONNECT, $FD_READ, $FD_CLOSE)) GUIRegisterMsg( 0x400 + $arrayslot, "Opensocket_data_" ) $Socket_Handle_array[$arrayslot] = $SocketID $Node_Identifier_array[$arrayslot] = -1 $Node_IP_array[$arrayslot] = $Conn_IP $return = ___ASockConnect( $SocketID, $Conn_IP, $node_Port ) $total_connected += 1 If @extended Then if $console_out = True then ConsoleWrite( @CRLF & "_P2P: Connection Established.") Call( $connectfunc, $SocketID, 0);connected immediately! EndIf EndFunc ; #FUNCTION# ;=============================================================================== ; ; Name...........: _P2P_Broadcast ; Description ...: Sends data to all connected peers. ; Syntax.........: _P2P_Broadcast($Data) ; Parameters ....: $Data - The data to send. ; Return values .: True ; Author ........: Hyperzap ; Modified.......: ; Remarks .......: ; Related .......: ; Link ..........; ; Example .......; ; ; ;========================================================================================== Func _P2P_Broadcast($Data) Local $i For $i = 0 to $max_connections step 1 If $Socket_Handle_array[$i] <> -1 Then TCPSend($Socket_Handle_array[$i], $Data & "#8i8TERMINATE#") Next Return True EndFunc ; #FUNCTION# ;=============================================================================== ; ; Name...........: _P2P_Peerlist ; Description ...: Returns the sockets, IDs, and IPs of all connected peers. ; Syntax.........: _P2P_Peerlist() ; Parameters ....: ; Return values .: A 3 dimensional array of all connected peers. The first spot in the array, ; AKA [0][0] defines the number of connected peers. The remaining spots ; In the array are the actual peers in the format of dimension [0] socket ; handle, [1] ID, and [2] IP. So the first peer will be [1][0], [1][1]. [1][2] ; and the second [2][0], [2][1], [2][2] etc. ; Author ........: Hyperzap ; Modified.......: ; Remarks .......: ; Related .......: ; Link ..........; ; Example .......; ; ; ;========================================================================================== Func _P2P_Peerlist() Local $aList[$max_connections+1][3], $alist_count = 1 For $i = 1 to $max_connections If $Socket_Handle_array[$i] <> -1 Then $aList[$alist_count][0] = $Socket_Handle_array[$i] $aList[$alist_count][1] = $Node_Identifier_array[$i] $aList[$alist_count][2] = $Node_IP_array[$i] $alist_count += 1 EndIf Next $aList[0][0] = $alist_count Return $alist EndFunc ; #FUNCTION# ;=============================================================================== ; ; Name...........: _P2P_Register_Event ; Description ...: Registers an event.So, if I register my function "ant" with the event ; $P2P_RECEIVE, it will look like: _P2P_Register_Event($P2P_RECEIVE, "ant"). ; It will mean my function 'ant' will get called with the specified parameters ; (see below) when data is recieved. ; Syntax.........: _P2P_Register_Event($iEvent, $sFunction) ; Parameters ....: $iEvent - Event number. It can be any these values: ; Event number This is the required syntax of YOUR called function ; $P2P_AUX_DATA ; Function ($hSocket, $DataType, $Data) ;When things like IP and Node identifier are discovered. ; $P2P_MESSAGE ; Function ($hSocket, $message, $from, $iError) ;When long distance messages are recieved. ; $P2P_RECEIVE ; Function ($hSocket, $sReceived, $iError) ;When data is recieved directly from a connected peer. ; $P2P_CONNECT ; Function ($hSocket, $iError) ;This is called when an attempted connection is successful or unsuccessful. ; $P2P_DISCONNECT ; Function ($hSocket, $iError) ;This is called whenever a peer disconnects from you. ; $P2P_NEWCONNECTION ; Function ($hSocket, $iError) ;This is called whenever a peer connects to you. ; Return values .: Success - True ; Failure - False ; Author ........: Kip ; Modified.......: Hyperzap ; Remarks .......: ; Related .......: ; Link ..........; ; Example .......; ; ; ;========================================================================================== Func _P2P_Register_Event($iEvent, $sFunction) if $iEvent = $P2P_AUX_DATA then $auxfunc = $sFunction if $iEvent = $P2P_RECEIVE then $recievefunc = $sFunction if $iEvent = $P2P_CONNECT then $connectfunc = $sFunction if $iEvent = $P2P_DISCONNECT then $disconnectfunc = $sFunction if $iEvent = $P2P_NEWCONNECTION then $newconnectionfunc = $sFunction if $iEvent = $P2P_MESSAGE then $messagefunc = $sFunction EndFunc ;================================================================================================================== ; ; Zatorg's Asynchronous Sockets UDF Starts from here. ; ;================================================================================================================== Func ___ASocket($iAddressFamily = 2, $iType = 1, $iProtocol = 6) If $hWs2_32 = -1 Then $hWs2_32 = DllOpen( "Ws2_32.dll" ) Local $hSocket = DllCall($hWs2_32, "uint", "socket", "int", $iAddressFamily, "int", $iType, "int", $iProtocol) If @error Then SetError(1, @error) Return -1 EndIf If $hSocket[ 0 ] = -1 Then SetError(2, ___WSAGetLastError()) Return -1 EndIf Return $hSocket[ 0 ] EndFunc ;==>_ASocket Func ___ASockShutdown($hSocket) If $hWs2_32 = -1 Then $hWs2_32 = DllOpen( "Ws2_32.dll" ) Local $iRet = DllCall($hWs2_32, "int", "shutdown", "uint", $hSocket, "int", 2) If @error Then SetError(1, @error) Return False EndIf If $iRet[ 0 ] <> 0 Then SetError(2, ___WSAGetLastError()) Return False EndIf Return True EndFunc ;==>_ASockShutdown Func ___ASockClose($hSocket) If $hWs2_32 = -1 Then $hWs2_32 = DllOpen( "Ws2_32.dll" ) Local $iRet = DllCall($hWs2_32, "int", "closesocket", "uint", $hSocket) If @error Then SetError(1, @error) Return False EndIf If $iRet[ 0 ] <> 0 Then SetError(2, ___WSAGetLastError()) Return False EndIf Return True EndFunc ;==>_ASockClose Func ___ASockSelect($hSocket, $hWnd, $uiMsg, $iEvent) If $hWs2_32 = -1 Then $hWs2_32 = DllOpen( "Ws2_32.dll" ) Local $iRet = DllCall( _ $hWs2_32, _ "int", "WSAAsyncSelect", _ "uint", $hSocket, _ "hwnd", $hWnd, _ "uint", $uiMsg, _ "int", $iEvent _ ) If @error Then SetError(1, @error) Return False EndIf If $iRet[ 0 ] <> 0 Then SetError(2, ___WSAGetLastError()) Return False EndIf Return True EndFunc ;==>_ASockSelect ; Note: you can see that $iMaxPending is set to 5 by default. ; IT DOES NOT MEAN THAT DEFAULT = 5 PENDING CONNECTIONS ; 5 == SOMAXCONN, so don't worry be happy Func ___ASockListen($hSocket, $sIP, $uiPort, $iMaxPending = 5); 5 == SOMAXCONN => No need to change it. Local $iRet Local $stAddress If $hWs2_32 = -1 Then $hWs2_32 = DllOpen( "Ws2_32.dll" ) $stAddress = ___SockAddr($sIP, $uiPort) If @error Then SetError(@error, @extended) Return False EndIf $iRet = DllCall($hWs2_32, "int", "bind", "uint", $hSocket, "ptr", DllStructGetPtr($stAddress), "int", DllStructGetSize($stAddress)) If @error Then SetError(3, @error) Return False EndIf If $iRet[ 0 ] <> 0 Then $stAddress = 0; Deallocate SetError(4, ___WSAGetLastError()) Return False EndIf $iRet = DllCall($hWs2_32, "int", "listen", "uint", $hSocket, "int", $iMaxPending) If @error Then SetError(5, @error) Return False EndIf If $iRet[ 0 ] <> 0 Then $stAddress = 0; Deallocate SetError(6, ___WSAGetLastError()) Return False EndIf Return True EndFunc ;==>_ASockListen Func ___ASockConnect($hSocket, $sIP, $uiPort) Local $iRet Local $stAddress If $hWs2_32 = -1 Then $hWs2_32 = DllOpen( "Ws2_32.dll" ) $stAddress = ___SockAddr($sIP, $uiPort) If @error Then SetError(@error, @extended) Return False EndIf $iRet = DllCall($hWs2_32, "int", "connect", "uint", $hSocket, "ptr", DllStructGetPtr($stAddress), "int", DllStructGetSize($stAddress)) If @error Then SetError(3, @error) Return False EndIf $iRet = ___WSAGetLastError() If $iRet = 10035 Then; WSAEWOULDBLOCK Return True; Asynchronous connect attempt has been started. EndIf SetExtended(1); Connected immediately Return True EndFunc ;==>_ASockConnect ; A wrapper function to ease all the pain in creating and filling the sockaddr struct Func ___SockAddr($sIP, $iPort, $iAddressFamily = 2) Local $iRet Local $stAddress If $hWs2_32 = -1 Then $hWs2_32 = DllOpen( "Ws2_32.dll" ) $stAddress = DllStructCreate("short; ushort; uint; char[8]") If @error Then SetError(1, @error) Return False EndIf DllStructSetData($stAddress, 1, $iAddressFamily) $iRet = DllCall($hWs2_32, "ushort", "htons", "ushort", $iPort) DllStructSetData($stAddress, 2, $iRet[ 0 ]) $iRet = DllCall($hWs2_32, "uint", "inet_addr", "str", $sIP) If $iRet[ 0 ] = 0xffffffff Then; INADDR_NONE $stAddress = 0; Deallocate SetError(2, ___WSAGetLastError()) Return False EndIf DllStructSetData($stAddress, 3, $iRet[ 0 ]) Return $stAddress EndFunc ;==>__SockAddr Func ___WSAGetLastError() If $hWs2_32 = -1 Then $hWs2_32 = DllOpen( "Ws2_32.dll" ) Local $iRet = DllCall($hWs2_32, "int", "WSAGetLastError") If @error Then ;if $console_out = True then ConsoleWrite("+> _WSAGetLastError(): WSAGetLastError() failed. Script line number: " & @ScriptLineNumber & @CRLF) SetExtended(1) Return 0 EndIf Return $iRet[ 0 ] EndFunc ;==>_WSAGetLastError ; Got these here: ; http://www.autoitscript.com/forum/index.php?showtopic=5620&hl=MAKELONG Func ___MakeLong($LoWord, $HiWord) Return BitOR($HiWord * 0x10000, BitAND($LoWord, 0xFFFF)); Thanks Larry EndFunc ;==>_MakeLong Func ___HiWord($Long) Return BitShift($Long, 16); Thanks Valik EndFunc ;==>_HiWord Func ___LoWord($Long) Return BitAND($Long, 0xFFFF); Thanks Valik EndFunc ;==>_LoWord ;----------------------------------OTHER AUTOIT INBUILT FUNCS----## ;=============================================================================== ; ; Function Name: _GetIP() ; Description: Get public IP address of a network/computer. ; Parameter(s): None ; Requirement(s): Internet access. ; Return Value(s): On Success - Returns the public IP Address ; On Failure - -1 and sets @ERROR = 1 ; Author(s): Larry/Ezzetabi & Jarvis Stubblefield ; ;=============================================================================== Func _Get_IP() Local $ip, $t_ip If InetGet("http://checkip.dyndns.org/?rnd1=" & Random(1, 65536) & "&rnd2=" & Random(1, 65536), @TempDir & "\~ip.tmp") Then $ip = FileRead(@TempDir & "\~ip.tmp", FileGetSize(@TempDir & "\~ip.tmp")) FileDelete(@TempDir & "\~ip.tmp") $ip = StringTrimLeft($ip, StringInStr($ip, ":") + 1) $ip = StringTrimRight($ip, StringLen($ip) - StringInStr($ip, "/") + 2) $t_ip = StringSplit($ip, '.') If $t_ip[0] = 4 And StringIsDigit($t_ip[1]) And StringIsDigit($t_ip[2]) And StringIsDigit($t_ip[3]) And StringIsDigit($t_ip[4]) Then Return $ip EndIf EndIf If InetGet("http://www.whatismyip.com/?rnd1=" & Random(1, 65536) & "&rnd2=" & Random(1, 65536), @TempDir & "\~ip.tmp") Then $ip = FileRead(@TempDir & "\~ip.tmp", FileGetSize(@TempDir & "\~ip.tmp")) FileDelete(@TempDir & "\~ip.tmp") $ip = StringTrimLeft($ip, StringInStr($ip, "Your ip is") + 10) $ip = StringLeft($ip, StringInStr($ip, " ") - 1) $ip = StringStripWS($ip, 8) $t_ip = StringSplit($ip, '.') If $t_ip[0] = 4 And StringIsDigit($t_ip[1]) And StringIsDigit($t_ip[2]) And StringIsDigit($t_ip[3]) And StringIsDigit($t_ip[4]) Then Return $ip EndIf EndIf SetError(1) Return -1 EndFunc ;==>_Get_IP