Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

Socket Class (überarbeitet)


  • Please log in to reply
30 replies to this topic
Bentschi
  • Moderators
  • 120 posts
  • Last active: Sep 05 2014 02:12 AM
  • Joined: 26 Nov 2008

Hast wohl recht.

Hab das jetzt nicht so genau unter die Lupe genommen, aber ich hab das mal im 1. Post geändert.



clackwell
  • Members
  • 2 posts
  • Last active: Oct 27 2013 07:25 PM
  • Joined: 26 Oct 2013

Hast wohl recht.

Hab das jetzt nicht so genau unter die Lupe genommen, aber ich hab das mal im 1. Post geändert.

 

Prima, danke.

 

Nun ist da allerdings eine Race Condition entstanden:

 

- Die Event Verarbeitung wird gestartet bevor man die onRecv Funktion registrieren kann. Wenn also der Server diese Funktion nicht registriert bevor die ersten Daten des Client eintreffen, gehen die Daten verloren.

 

Ein Test dafür:

 

server.ahk:

#persistent
#singleinstance force
#include socket.ahk

; Server

main() {
    s := new SocketTCP()
    s.bind( "127.0.0.1", 12345 )
    s.onAccept := Func( "on_accept" )
    s.listen()
}

on_accept( socket ) {
    s2 := socket.accept()
    ; Sleep long before registering onRecv function:
    sleep 4000
    s2.sendText( "Ping!" )
    s2.onRecv := Func( "on_recv" )
}

on_recv( s ) {
    msgbox % "From Client: " s.recvText()
}

main()

client.ahk:

#persistent
#singleinstance force
#include socket.ahk

main() {
    s := new SocketTCP()
    s.onRecv := Func( "on_tcp_recv" )
    s.connect( "127.0.0.1", 12345 )
    s.sendText("Client message right after connect!")
}

on_tcp_recv( socket ) { msgbox % "From Server: " socket.recvText() socket.sendText( "Pong!" ) } main()

 

Das Pong! vom Server kommt bei mir in ca. 50% der Fälle.

 

Allerdings: Wenn ich die accept() Methode ändere und dort die onRecv Funktion registriere - kommen immer noch nur in 50% der Fälle das Pong!.

 

Grr.



Bentschi
  • Moderators
  • 120 posts
  • Last active: Sep 05 2014 02:12 AM
  • Joined: 26 Nov 2008

Die Funktion wird registriert sobald der Socket besteht.

Viel früher kannst du es gar nicht registrieren.

Eventuell das erste Recv manuel abfragen (ohne den Callbacks)



GeekDude
  • Spam Officer
  • 391 posts
  • Last active: Oct 05 2015 08:13 PM
  • Joined: 23 Nov 2009

Just thought you would want to know that there's an off-by-one error in SendText that causes it to send a null at the end of every message. I figured this out after troubleshooting a handshake with Wireshark.



nnnik
  • Members
  • 1625 posts
  • Last active: Apr 11 2017 02:13 PM
  • Joined: 28 Jul 2012
Lol thats obvious
The end of every string is 0char

Visit the new forum ahkscript.org.

http://ahkscript.org


Bentschi
  • Moderators
  • 120 posts
  • Last active: Sep 05 2014 02:12 AM
  • Joined: 26 Nov 2008

Thats the terminator.

Simply recalculate the length of the string -1 (Unicode -> -2) and you dont get the 0-terminator, if you dont want it.

The function/method is only to send text, so i thought it's better to include the 0-terminator, that marks the end of strings.

Otherwise there could be something wrong if other Apps want to read it, except this lib, if they don't find an end of a string.

 

if you want to make something like a HTTP request it is ok to keep the terminator, cause the end is calculated on a \r\n\r\n or the length if its content (the server will ignore the terminator).



GeekDude
  • Spam Officer
  • 391 posts
  • Last active: Oct 05 2015 08:13 PM
  • Joined: 23 Nov 2009
myTcp := new SocketTCP()
myTcp.bind("addr_any", 26656)
myTcp.listen()
myTcp.onAccept := Func("OnTCPAccept")
return

OnTCPAccept(){
    global myTcp
    newTcp := myTcp.accept()
    Text := newTcp.recvText()
    GuiControlGet, Channel
    IRC.Chat(Channel, Text)
;    newTcp.__Delete()
}

Clients aren't being closed when the OnTCPAccpet function is over, and I can't initiate new connections if I manually call newTcp.__Delete() at the end of the function

 

Edit: Problem solved. On line 163 a.remove(obj.socket) should be a.remove(obj.socket, ""), to avoid messing up every other socket ID by decrementing them by 1. But still, seeing as the contents of a[obj.socket] is obj, the socket never gets automatically deleted at the end of the function by the garbage collector



GeekDude
  • Spam Officer
  • 391 posts
  • Last active: Oct 05 2015 08:13 PM
  • Joined: 23 Nov 2009

Is there any way I can detect when a socket dies?



Bentschi
  • Moderators
  • 120 posts
  • Last active: Sep 05 2014 02:12 AM
  • Joined: 26 Nov 2008

How about socket.disconnect() and socket.onDisconnect := Func("myFunction")



Bruttosozialprodukt
  • Members
  • 457 posts
  • Last active: Oct 18 2015 08:47 AM
  • Joined: 20 Oct 2012

Hier mal ein paar nützliche Infos:
Um die Klasse über das Netzwerk (LAN) zu nutzen, muss das Script eventuell als Admin ausgeführt werden. (Es sei denn UAC ist deaktiviert.)
Außerdem könnte die Firewall Probleme verursachen.
Um Daten über das Internet zu empfangen, müssen die Ports im Router entsprechend zugewiesen werden.

Hier mal das UDP-Chat Beispiel, umgeschrieben für die neue Klasse:

#include Socket.ahk
myUdpIn := new SocketUDP() ;Erstelle ein neues udp-Objekt 'myUdpIn'
myUdpIn.bind("addr_any", 12345) ;Binde an 'addr_any'
myUdpIn.onRecv := Func("myRecvCallback") ;Führe das Callback 'myRecvCallback', bei einer eingehenden Nachricht aus.

myUdpOut := new SocketUDP() ;Erstelle ein neues udp-Objekt 'myUdpOut'
myUdpOut.connect("addr_broadcast", 12345) ;Verbinde mit 'addr_broadcast'
myUdpOut.enableBroadcast() ;Erlaube Broadcast-Nachrichten

Loop
{
  Inputbox, send_msg
  if (send_msg="")
    break
  myUdpOut.sendText(send_msg) ;Sende Text-Nachricht
}
ExitApp

myRecvCallback(this)
{
  Loop, 20
    line .= "__"
  MsgBox, % "Eingehende Nachricht:`n" line "`n`n" this.recvText() ;Empfange Text-Nachricht
}

(getestet und funktioniert)



Bruttosozialprodukt
  • Members
  • 457 posts
  • Last active: Oct 18 2015 08:47 AM
  • Joined: 20 Oct 2012

Also da scheint irgendein Fehler in der Klasse zu sein.

Es scheint nicht möglich zu sein (bei dem UDP Chat Beispiel mit onRecv) an eine bestimmte IP zu binden.

"addr_any" funktioniert problemlos, "192.168.2.105" funktioniert nicht.

Zu der Adresse connecten um Nachrichten hinzusenden klappt jedoch problemlos.

 

Hilfe :(



struppi81
  • Members
  • 1 posts
  • Last active: Oct 17 2014 05:45 PM
  • Joined: 10 Jul 2013

Hallo,
 
für alle die, die Dateien über TCP schicken/senden wollen, habe ich mal für "Bentschi's Socket Class (überarbeitet)"

eine kleine Erweiterungen geschrieben.

 

SendFilePackages()

RecvFilePackages()

 
Hier mal der Code:

  SendFilePackages(filepath, package_size=1024)
  {
    FileObj := fileopen( filepath, "r")
    VarSetCapacity(buf, FileObj.length)
    FileObj.RawRead(&buf, FileObj.length)
    
    Stringsplit, filepath, filepath, \
    filename := filepath%filepath0%                            ; Dateiname Rausfiltern
    
    len := FileObj.length
    pos := &buf

    this.sendText(filename ":" len ":" package_size)           ; Header
    this.recvText() 

    while (len>0)
          {
          current_len := (len>=package_size) ? package_size : len
          this.send(pos, current_len)

          len -= current_len  
          pos += current_len
          this.recvText()
          }
    FileObj.close()
  }

  RecvFilePackages(filepath="")
  {
    Header := this.recvText()

    Stringsplit, Header, Header, :

    num_packages := Ceil(Header2/Header3)                      ; Anzahl der Packete aufgerundet 

    If (filepath = "") 
       {
       IfExist, %A_Scriptdir%\%Header1%
              filepath := A_Scriptdir "\" "Kopie von " Header1
           else
              filepath := A_Scriptdir "\" Header1
       }
    else
       {
       filepath := filepath "\" Header1
       }

    FileObj := fileopen(filepath, "w") 

    this.sendText("#")

    Loop, % num_packages
        {     
        len := this.recv(buffer)
        FileObj.RawWrite(&buffer, len)
        If (A_Index = num_packages)
           {
           FileObj.close()
           }
        this.sendText("#")
        }          
  }


GeekDude
  • Spam Officer
  • 391 posts
  • Last active: Oct 05 2015 08:13 PM
  • Joined: 23 Nov 2009

Is it possible to use SSL with this library somehow?



evilc
  • Members
  • 340 posts
  • Last active: Oct 27 2015 11:07 PM
  • Joined: 17 Nov 2005

Sorry for my lack of knowledge of German, but are SendFilePackages() and RecvFilePackages() to facilitate transfer of files?

If so, has anyone got sample code? I can't get it to work.

 



evilc
  • Members
  • 340 posts
  • Last active: Oct 27 2015 11:07 PM
  • Joined: 17 Nov 2005

Also, maybe I am missing something, but I am having trouble implementing the behavior that I want.

 

What I would like to do is to have a "Master" issue a "Slave" an instruction, then once the instruction has been carried out (Could be ages later - hours even), the Slave Reopens a socket back to the master to report job done.

 

So the message flow would look like:

 

MASTER: DoSomething

SLAVE: ACK

 

[Hours pass]

 

SLAVE: DidSomething

MASTER: ACK

 

Also, i would like this to be asynchronous, so the master could ask the slave to do a number of things, and as each is completed, it connects back to the master.

 

I knocked up some test code, but it does not seem to work properly.

On the slave, I set up an asynch thread (SetTimer -2000) to simulate a reply back later, but in reality I would like to call the func immediately (SetTimer -0) and have a Sleep in the func to simulate work taking some time to do.

However, if I do that, asynch calls only work once - you cannot queue up more than one DoSomething request, as the ACK does not seem to come back.

 

Any ideas?

 

Run both scripts on local machine, hit F12 to have the master send a "DoSomething" request to the slave.

Set this.mode := 1 in the Slave to enable to logic that I wish to use. Notice how in this mode, if you spam F12 quickly, the messages stop flowing.

 

 

Master:

#SingleInstance force
#MaxThreadsPerHotkey 1000
#include <Socket>

OutputDebug DBGVIEWCLEAR

;Client
m := new Master()

class Master {
	__New(){
		fn := this.Test.Bind(this)
		hotkey, F12, % fn
		
		myTcp := new SocketTCP()
		myTcp.bind("addr_any", 12346)
		myTcp.listen()
		myTcp.onAccept := this.OnTCPAccept.Bind(this, myTcp)
	}
	
	Test(){
		msg := "DoSomething"
		OutputDebug % "MASTER SENDING MESSAGE: " msg
		myTcp := new SocketTCP()
		myTcp.connect("localhost", 12345)
		myTcp.sendText(msg)
		OutputDebug, % "MASTER GOT REPLY: " myTcp.recvText()
	}
	
	OnTCPAccept(myTcp){
		newTcp := myTcp.accept()
		OutputDebug, % "MASTER GOT MESSAGE: " newTcp.recvText()
		OutputDebug, % "MASTER SENDING ACK"
		newTcp.sendText("Ack")
	}
}
 

Slave:

#SingleInstance force
#MaxThreadsPerHotkey 1000
#include <Socket>

s := new Slave()

class Slave {
	__New(){
		this.mode := 0
		fn := this.Test.Bind(this)
		hotkey, F11, % fn
		
		myTcp := new SocketTCP()
		myTcp.bind("addr_any", 12345)
		myTcp.listen()
		myTcp.onAccept := this.OnTCPAccept.Bind(this, myTcp)
	}

	Test(){
		if (this.mode){
			OutputDebug % "SLAVE SIMULATING PROCESSING"
			Sleep 2000		; Doesn't work. Why is this different to the SetTimer being on 2 sec?
		}
		; Fire back "Job Complete" message
		myTcp := new SocketTCP()
		myTcp.connect("localhost", 12346)	; different port for reply
		myTcp.sendText("DidSomething")
		OutputDebug, % "SLAVE GOT REPLY: " myTcp.recvText()
	}
	
	OnTCPAccept(myTcp){
		newTcp := myTcp.accept()
		OutputDebug, % "SLAVE GOT MESSAGE: " newTcp.recvText()
		OutputDebug, % "SLAVE SENDING ACK"
		newTcp.sendText("Ack")
		
		if (this.mode){
			t := "-0"
		} else {
			t := "-2000"
		}
		fn := this.Test.Bind(this)
		SetTimer, % fn, % t	; Works. Why can this not be fired immediately, and a Sleep in Test() create the delay?
		;SetTimer, % fn, -0
	}
}