Socket Class - 2022/09/07 - beta.8

Post your working scripts, libraries and tools.
antenne
Posts: 7
Joined: 18 Jan 2023, 07:48

Re: Socket Class - 2022/09/07 - beta.8

Post by antenne » 19 Jan 2023, 03:41

@TheArkive

A blocking implementation is easier I guess?

User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: Socket Class - 2022/09/07 - beta.8

Post by TheArkive » 19 Jan 2023, 13:58

@antenne

Yes, but less convenient when trying to handle connection issues. Depending on how you code, your script can freeze until the connection finally times out, or connects.

ASYNC is better for a more robust application, but also more complex.

antenne
Posts: 7
Joined: 18 Jan 2023, 07:48

Re: Socket Class - 2022/09/07 - beta.8

Post by antenne » 20 Jan 2023, 02:54

@TheArkive

I think this won't be a issue, I'm only sending locally.

antenne
Posts: 7
Joined: 18 Jan 2023, 07:48

Re: Socket Class - 2022/09/07 - beta.8

Post by antenne » 07 Mar 2023, 03:37

@TheArkive

Sorry for bothering you again. But I seem to be to dumb to send a UDP String.
Could you please write an example to send an UDP String to localhost?

Thank you :)

User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: Socket Class - 2022/09/07 - beta.8

Post by TheArkive » 08 Mar 2023, 18:19

My apologies. I meant to get to this sooner.

The following code will perform a udp example:

Code: Select all

F8::test_server_udp()
F9::test_client_udp()

test_server_udp() { ; server - uses async to accept sockets
    sock := winsock("server",cb,"IPV4","UDP","DGram")
    sock.Bind("0.0.0.0",27015) ; "0.0.0.0",27015
    sock.Listen()
    print_gui("Server listening...`r`n`r`n")
}

test_client_udp() { ; client
    sock := winsock("client",cb,"IPV4","UDP","DGram")
    sock.Connect("127.0.0.1",27015) ; 127.0.0.1",27015 ; www.google.com",80
    print_gui("Client connecting...`r`n`r`n")
}

Sadly this example isn't as simple as it should be. It deviates a bit too much from the win32 docs. I'll probably revert some of this stuff in the future to better mirror the original MS docs.

I tried to simplify usage, but I don't think i achieved that.

EDIT:. paste this in the example file. Run script, and press F7, then F8. This is still an async example, not blocking. I'll work on that next.

The main change to use udp is this:

Code: Select all

sock := winsock("server",cb,"IPV4","UDP","DGram")

antenne
Posts: 7
Joined: 18 Jan 2023, 07:48

Re: Socket Class - 2022/09/07 - beta.8

Post by antenne » 09 Mar 2023, 06:41

Thank you very much for Posting the example.

I managed to build my own minimal example.

Code: Select all

#Include _socket.ahk

sock := winsock("client",cb,"IPV4","UDP","DGram")
sock.Connect("127.0.0.1",33333)
MsgBox

cb(sock, event, err) {
	get_req := "Test"
	strbuf := Buffer(StrLen(get_req)+1,0)
	StrPut(get_req,strbuf,"UTF-8")
	sock.Send(strbuf)
}
Unfortunately it doesn't really work reliably. It only sends the String when there is this MsgBox :? and then it will be sent twice.
What am I doing wrong?

User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: Socket Class - 2022/09/07 - beta.8

Post by TheArkive » 09 Mar 2023, 18:35

Read up on the async events. At a minimum, if you are only sending, you will have 2 events. See the original example for event handling.

Read up here: WSAAsyncSelect

- FD_CONNECT
On return from connect command.
Err code will indicate success or reason for fail.

- FD_WRITE
Socket is ready to write, now is the time to use send cmd

I'll clean up the example when I have more time.

As for "needing the MsgBox", that makes no sense. My example only uses msgbox to show events.

Check err codes in callback to see what is happening.

User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: Socket Class - 2022/09/07 - beta.8

Post by TheArkive » 09 Mar 2023, 18:48

You probably "need" the msgbox because of timing issues. Use the events properly and check err codes. That should fix the root of this issue.

antenne
Posts: 7
Joined: 18 Jan 2023, 07:48

Re: Socket Class - 2022/09/07 - beta.8

Post by antenne » 10 Mar 2023, 02:41

Thank you for alle your time and effort!
I will try to dig deeper into the topic.

char101
Posts: 11
Joined: 10 Mar 2017, 04:15

Re: Socket Class - 2022/09/07 - beta.8

Post by char101 » 03 Jun 2023, 02:20

Basic http server, I use it to call ahk running in another computer (VM actually). Thanks for the winsock class.

Code: Select all

#include <_socket>
Persistent(true)

class HTTPRequest {
	method := ''
	path := ''
	queries := Map()
	headers := Map()

	static URIDecode(value) {
		static _UrlUnescape := DllCall(
			'kernel32.dll\GetProcAddress',
			'Ptr', DllCall('kernel32.dll\GetModuleHandle', 'Str', 'shlwapi.dll', 'Ptr'),
			'AStr', 'UrlUnescapeW',
			'Ptr'
		)
		return DllCall(
			_UrlUnescape,
			'Str', value,
			'Ptr', 0,
			'UInt', 0,
			'UInt', 0x00140000,
			'UInt'
		) ? '' : value
	}

	__New(request) {
		loop parse request, '`n', '`r' {
			if !A_LoopField
				break
			if !this.method {
				arr := StrSplit(A_LoopField, ' ')
				this.method := arr[1]
				if pos := InStr(arr[2], '?') {
					this.path := SubStr(arr[2], 1, pos - 1)
					loop parse SubStr(arr[2], pos + 1), '&' {
						if pos := InStr(A_LoopField, '=') {
							param := HTTPRequest.URIDecode(SubStr(A_LoopField, 1, pos - 1))
							value := HTTPRequest.URIDecode(SubStr(A_LoopField, pos + 1))
							this.queries[param] := value
						} else {
							param := HTTPRequest.URIDecode(A_LoopField)
							this.queries[param] := ''
						}
					}
				} else {
					this.path := arr[2]
				}
			} else {
				arr := StrSplit(A_LoopField, ':', ' ', 2)
				this.headers[arr[1]] := arr[2]
			}
		}
	}
}

class HTTPResponse {
	static STATUS_CODES := Map(
		200, 'OK',
		404, 'Not Found'
	)

	__New(sock) {
		this.sock := sock
	}

	Send(status, headers := Map(), content := '') {
		if !IsObject(headers) && !content {
			content := headers
			headers := Map()
		}

		NL := '`r`n'
		response := 'HTTP/1.1 ' status ' ' HTTPResponse.STATUS_CODES[status] NL
		if !headers.Has('Content-Type') {
			headers['Content-Type'] := 'text/plain'
		}
		if headers {
			for key, value in headers {
				response .= key ': ' value NL
			}
		}
		response .= 'Content-Length: ' (StrPut(content, 'UTF-8') - 1) NL ; -1 to exclude the terminating \0
		response .= NL
		response .= content

		buf := Buffer(StrPut(response, 'UTF-8') - 1)
		StrPut(response, buf, 'UTF-8')
		this.sock.Send(buf)
	}
}

class HTTPServer {
	paths := Map()

	__Item[name] {
		get => this.paths[name]
		set => this.paths[name] := value
	}

	_Callback(sock, event, err) {
		switch event {
			case 'Read':
				request := ''
				while true {
					buf := sock.Recv()
					if !buf.Size
						break
					request .= StrGet(buf, 'UTF-8')
				}

				if request {
					request := HTTPRequest(request)
					response := HTTPResponse(sock)
					if (this.paths.Has(request.path)) {
						this.paths[request.path](request, response)
					} else {
						response.Send(404, 'Not Found')
					}
				}

				sock.Close()
		}
	}

	Run(host := '127.0.0.1', port := 80) {
		this.sock := winsock('server', this._Callback.Bind(this), 'IPV4')
		this.sock.Bind(host, port)
		this.sock.Listen()
	}
}

Hello(request, response) {
	response.Send(200, 'Hello World')
}

server := HTTPServer()
server['/'] := Hello
server.Run('127.0.0.1', 8001)

Black_dog
Posts: 3
Joined: 23 Oct 2021, 03:54

Re: Socket Class - 2022/09/07 - beta.8

Post by Black_dog » 20 Dec 2023, 03:11

Big Thanks for the winsock.
Start server:

Code: Select all

sock := winsock("server",cb,"IPV4")
		sock.Bind("0.0.0.0",27015) ; "0.0.0.0",27015
		sock.Listen()
Close server?

Code: Select all

sock.close()
If start server again

Code: Select all

sock := winsock("server",cb,"IPV4")
		sock.Bind("0.0.0.0",27015) ; "0.0.0.0",27015
		sock.Listen()
I have Error:
WSAASyncSelect failed. Line: 287: (!block) ? this.RegisterEvents() : ""
Code RegisterEvents:

Code: Select all

RegisterEvents(lEvent:=0x3FF) { ; FD_ALL_EVENTS = 0x3FF
        this.block := !lEvent ? false : true
       
        If (result:=this._WSAAsyncSelect(this.desc, A_ScriptHwnd, winsock.wm_msg, lEvent) = -1)
            throw Error("WSAASyncSelect failed.", -1)
        
        ;If !lEvent
            ; result := this._ioctlsocket(this.desc, 0x8004667E, &r:=0) ; FIONBIO := 0x8004667E (non-blocking mode)
            
        return !result
What I am wrong?

Black_dog
Posts: 3
Joined: 23 Oct 2021, 03:54

Re: Socket Class - 2022/09/07 - beta.8

Post by Black_dog » 20 Dec 2023, 09:28

And second question:
how to set "wait time" response from sever (for example - 20 milliseconds)?

User avatar
TheArkive
Posts: 1027
Joined: 05 Aug 2016, 08:06
Location: The Construct
Contact:

Re: Socket Class - 2022/09/07 - beta.8

Post by TheArkive » 20 Dec 2023, 11:49

@Black_dog

Thanks for the post, I'll look into it.

In case you have more time to experiment before I get to it, my first thought is that maybe the port is no longer open? But I don't actually have a method that will check for specific port availability currently. That is a loose "To-Do" item.

Post Reply

Return to “Scripts and Functions (v2)”