Jump to content

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

About Winsock and TCP/IP


  • Please log in to reply
36 replies to this topic
Zaelia
  • Members
  • 754 posts
  • Last active: Jan 17 2015 02:38 AM
  • Joined: 31 Oct 2008
They have already a lot of information about tcp and winsock, for example

MSDN...
<!-- m -->http://www.autohotke...pic.php?t=13829<!-- m -->
<!-- m -->http://www.autohotke...pic.php?t=32455<!-- m -->
<!-- m -->http://www.autohotke...pic.php?t=48616<!-- m -->

but I'm a noob... I try to understand scripts and to have a better knowledge or skill...

At this moment, I ask to you if they are a good way to do this ? Because script use loop or settimer or very hard function that I don't understand (byReff, buffer and other...). I want to know general problem for a TCP connection and winsock ( I have searshed a lot^^ )...

Finnaly my basic script, I think it's a good skeletor, but I don't know if this server can be strong because he use very often OnMessage.
Even if it is bad it has the merit of being compact for a reader and use another aproach...

Tiny Server
IPAddress = 127.0.0.1
Port = 8765

Gui, Add, Edit, w320 r10 vLogEdit ReadOnly,
Gui, Add, Edit, section w200 R1 Limit255 vSendText,
Gui, Add, Button, ys w100 gSendButton, Send
Gui, Show,, Tiny Server
OnExit, ExitSub

VarSetCapacity(wsaData, 32)
DllCall("Ws2_32\WSAStartup", "UShort", 2, "UInt", &wsaData)
socket := DllCall("Ws2_32\socket", "Int", 2, "Int", 1, "Int", 6)
VarSetCapacity(sockaddr, 16)
NumPut(2, sockaddr, 0, "UShort")
NumPut(DllCall("Ws2_32\htons", "UShort", Port), sockaddr, 2, "UShort")
NumPut(DllCall("Ws2_32\inet_addr", "Str", IPAddress), sockaddr, 4)

DllCall("Ws2_32\bind", "UInt", socket, "UInt", &sockaddr, "Int", 16)
DllCall("Ws2_32\listen", "UInt", socket, "UInt", "SOMAXCONN")
socketList:= ","

DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", WinExist("A"), "UInt", 0x5555, "Int", 1|8|10|32)
OnMessage(0x5555, "WinsockMessage")

log := (log := DllCall("Ws2_32\WSAGetLastError")) ? "Winsock error " log : "Server online"
Guicontrol,, LogEdit, %log%
return

WinsockMessage(socket, event)
{
Critical
global log, socketList

  if event=1
  {
    VarSetCapacity(recvdata, 256, 0)
    DllCall("Ws2_32\recv", "UInt", socket, "Str", recvdata, "Int", 256, "Int", 0)
    log .= "`n" socket ": " recvdata
  }
  if event=8
  {
    socket := DllCall("Ws2_32\accept", "UInt", socket, "UInt", 0, "Int", 0)
    socketList .= socket ","
    log .= "`nClient connected " socket
  }
  if ( event=658833440 || event=32 )
  {
    log .= event=32 ? "`nClient offline " socket : "`nClient crashed " socket
    StringReplace, socketList, socketList, `,%socket%`,, `,
    DllCall("Ws2_32\closesocket" , "UInt", socket)
  }

Guicontrol,, LogEdit, %log%
controlsend, edit1, ^{end}, Tiny Server
}

SendButton:
GuiControlGet, SendText
if !SendText
return

if InStr(SendText, "/kick") = 1
  {
  socket := SubStr(SendText, 7)
  log .= DllCall("Ws2_32\closesocket" , "UInt", socket) ? "`nKick error" : "`nClient Kicked " socket
  StringReplace, socketList, socketList, `,%socket%`,, `,
  }
else
  {
  Loop, Parse, socketList , `,
    {
    if A_LoopField <>
    DllCall("Ws2_32\send", "UInt", A_LoopField, "Str", SendText, "Int", StrLen(SendText), "Int", 0)
    }
  log .= "`n-> " SendText
  }

Guicontrol,, LogEdit, %log%
controlsend, edit1, ^{end}, Tiny Server
Guicontrol,, SendText,
return

GuiClose:
ExitSub:
DllCall("Ws2_32\WSACleanup")
ExitApp

Tiny Client
IPAddress = 127.0.0.1
Port = 8765

Gui, Add, Edit, w320 r10 vLogEdit ReadOnly,
Gui, Add, Edit, section w200 R1 Limit255 vSendText,
Gui, Add, Button, ys gSendButton, Send
Gui, Show,, Tiny Client
OnExit, ExitSub

VarSetCapacity(wsaData, 32)
DllCall("Ws2_32\WSAStartup", "UShort", 2, "UInt", &wsaData)
socket := DllCall("Ws2_32\socket", "Int", 2, "Int", 1, "Int", 6)
VarSetCapacity(sockaddr, 16)
NumPut(2, sockaddr, 0, "UShort")
NumPut(DllCall("Ws2_32\htons", "UShort", Port), sockaddr, 2, "UShort")
NumPut(DllCall("Ws2_32\inet_addr", "Str", IPAddress), sockaddr, 4)

DllCall("Ws2_32\connect", "UInt", socket, "UInt", &sockaddr, "Int", 16)

DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", WinExist("A"), "UInt", 0x5555, "Int", 1|32)
OnMessage(0x5555, "WinsockMessage")

log := (log := DllCall("Ws2_32\WSAGetLastError")) ? "Winsock error " log : "Client online " socket
Guicontrol,, LogEdit, %log%
return

WinsockMessage(socket, event)
{
Critical
global log, disconn

  if event=1
  {
    VarSetCapacity(recvdata, 256, 0)
    DllCall("Ws2_32\recv", "UInt", socket, "Str", recvdata, "Int", 256, "Int", 0)
    log .= "`n" socket ": " recvdata
  }
  if event=32
  {
    log .= disconn=1 ? "`nClient offline" : "`nClient kicked"
    DllCall("Ws2_32\WSACleanup")
  }
  if event=658833440
  {
    log .= "`nServer crashed"
    DllCall("Ws2_32\WSACleanup")
  }

Guicontrol,, LogEdit, %log%
controlsend, edit1, ^{end}, Tiny Client
} 

SendButton:
GuiControlGet, SendText
if !SendText
return

if ( SendText = "/logout" )
  {
  disconn = 1
  DllCall("Ws2_32\shutdown", "UInt", socket, "Int", 2)
  }
else
  {
  DllCall("Ws2_32\send", "UInt", socket, "Str", SendText, "Int", StrLen(SendText), "Int", 0)
  log .= "`n-> " SendText
  }

Guicontrol,, LogEdit, %log%
controlsend, edit1, ^{end}, Tiny Client
Guicontrol,, SendText,
return

GuiClose:
ExitSub:
DllCall("Ws2_32\WSACleanup")
ExitApp

Thanks, to share your experience about ahk,winsock,tcp with me :)

Edit: (Title)

TLM
  • Administrators
  • 3864 posts
  • Last active:
  • Joined: 21 Aug 2006
For byRef, it doesnt get any clearer than the helpfile:
<!-- m -->http://www.autohotke...tions.htm#param<!-- m -->

I'm a noob... I try to understand scripts and to have a better knowledge or skill...

Your code looks very good for your level of experience. When in doubt, learn buy example..

<!-- m -->http://www.autohotke...pic.php?t=32455<!-- m -->
<!-- m -->http://www.autohotke...pic.php?t=48616<!-- m -->

..or search ;)

Posted Image

don't duplicate, iterate!


Zaelia
  • Members
  • 754 posts
  • Last active: Jan 17 2015 02:38 AM
  • Joined: 31 Oct 2008
This forums is a gold mine for me, but to be self-educated with reverse engineering don't give me the good search words, or how to do things on problems that I have not already view...

However, thanks, I have no specific question to ask but I hoped to pull some advice or experience from here. I often see things built in the same way without special explanation, I understand, but something escape me because it must surely be reason that it is like that :)

krisj209
  • Members
  • 10 posts
  • Last active: Feb 13 2012 09:01 AM
  • Joined: 16 Nov 2007
These forums are full of great nuggets! I just used this script to trigger events on a remote computer via hotkey. Best of all, it works both ways. I can have an event on a remote computer trigger an event on my local computer.

I have been looking for a way to do this (that I could understand) in AHK for weeks. It all paid off.

I am using this to run my x10 home automation system. I can now by hotkey turn on and off lights from my laptop. I can also now press a button on one of the x10 controllers and have it trigger an event on another computer (could always have it trigger an event on the computer that the x10 controller was connected to).

The obvious application for this is the "Shut the heck up" button. When this button is pressed in the middle of the night, the volume on the computer in my garage will increase to 90% and play a .wav file that screams "Shut the heck up!" at my barking dog (in my voice which she obeys nicely) , then returns the volume back to 20%.


Thank you Zaelia! I hope you are still around on here to see this. Your code was great!
Men occasionally stumble over the truth, but most of them pick themselves up and hurry off as if nothing ever happened.

-Sir Winston Churchill

TLM
  • Administrators
  • 3864 posts
  • Last active:
  • Joined: 21 Aug 2006

The obvious application for this is the "Shut the heck up" button. When this button is pressed in the middle of the night, the volume on the computer in my garage will increase to 90% and play a .wav file that screams "Shut the heck up!" at my barking dog (in my voice which she obeys nicely) , then returns the volume back to 20%.

:lol: Your pooch has a comp?

+1, these clients/servers are wonderful. If the OP is around still, consider adding them to Scripts & Functions.
I wish I mentioned this before..

Posted Image

don't duplicate, iterate!


Zaelia
  • Members
  • 754 posts
  • Last active: Jan 17 2015 02:38 AM
  • Joined: 31 Oct 2008
Thanks krisj209, I'm happy that my script help you to understand winsock and TCP/IP protocol :)

Now I am working with some command like:
/FUNC mylabelorfunc ; easy way to gosub %mylabelorfunc%, like your app with play a sound
/WISP user ; about log display or not send/receiv action/reaction and esay way to do private message
/USER myname mypass , hard to think without make the mess
It's easy to implement but I don't want to loose "Tiny" concept and to have readable "script for Dummies" , easy revers-engineering is very powerfull for to learn. If I forget a script for a few days, I struggle to rework on it if it is not compact and sleek...
/JOIN ; create chat room , it's the mess , need good managment with list or file with server

Edit: (small fix)
Critical is inherited :)
<!-- m -->http://www.autohotke...pic.php?t=57827<!-- m -->

I think I do somethings like this: (more elegant, and maybe help to include more things)
if event=1
  {
    VarSetCapacity(recvdata, 256, 0)
    DllCall("Ws2_32\recv", "UInt", socket, "Str", recvdata, "Int", 256, "Int", 0)

    if InStr(recvdata, "/") = 1
       {
       cmd := SubStr(recvdata, 2, 4)
       sub := (cmd = "kick") ? "LabelKick"
            : (cmd = "user") ? "LabelUser"
            : (cmd = "Play") ? "PlayMySound"
            : (cmd = "msg0") ? "DisplayMessage"
            : (cmd = "cmd1") ? "command1"
            : (cmd = "cmd2") ? "command2"
            : cmd ; sub := cmd
       If IsLabel(sub)
         gosub %sub% ; goto can be use (return inside), function too with params store with %recvdata% and If IsFunc(FunctionName) security
       }
  }


wolf_II
  • Members
  • 343 posts
  • Last active: Jul 23 2010 05:19 PM
  • Joined: 18 Oct 2007
I came here just to say thank you Zaelia. :D :D :D

I don't know if anybody here knows about the DonationCoder.com Programming School's assignment number 5 for AHK, but I was about to give up on that, until I've seen your wonderful "Tiny Server" in action. Studying that helped me to be able to write code for DCCPSA#5.

Thank you. :D :D :D
Wolf

Schön wär's, wenn's schön wär!

TLM
  • Administrators
  • 3864 posts
  • Last active:
  • Joined: 21 Aug 2006
Wow those additions are gorgeous.

Thanks again Zaelia!

Posted Image

don't duplicate, iterate!


Zaelia
  • Members
  • 754 posts
  • Last active: Jan 17 2015 02:38 AM
  • Joined: 31 Oct 2008
Thanks Wolf.

Always with Tiny concept, about user registration :
socket := 223 ; winsock return example
name := "ClientA" ; register return example

%socket% := name
%name% := socket

msgbox % " socket used by ClientA = " ClientA " <=> user name at socket 223 = " %ClientA%
; we use 223 like a variable ( % %Client% it's like  % 223 )

It's an easy switch between socket and name (as equivalences) , but very confusing, however we avoid to use files and/or list with loops managment... private message is easy with this method because name=socket, I don't know if it's the good method, I always work on it...

Z_Gecko
  • Guests
  • Last active:
  • Joined: --

I don't know if it's the good method, I always work on it...

Nice idea, but dangerous.
Imagine what happens if i choose Clipboard as name,
or even worse this:%willbreaktheserver.

But i have no idea on doing it better, that doesn´t involve functions, loops and lists.
You should atleast give the vars a prefix (e.g.
soc_%socket% := name
name_%name% := socket
) and ensure that usernames only consist of letters, numbers and the following punctuation # _ @ $ ? [ ]

Zaelia
  • Members
  • 754 posts
  • Last active: Jan 17 2015 02:38 AM
  • Joined: 31 Oct 2008
Thx Z_Gecko

In fact, it's dangerous and confusing...
And I can't delete variable or "array", so I think to go to classic method, client can ask list of all client so file managment or loop or list is better.

I think to go to "List Method" in memory, Instr() is very fast (tested with 1M users...)
SU_list:="[260][ClientA][264][User264][268][ClientC][272][ClientD][666][ClientX]"

name:="User23"
if ( RegExMatch(name, "\W") || name+0 || !name ) ; no special char, no pure number, no empty name = WORD with az AZ 09 _ char
msgbox invalid name

msgbox % "264 <=> " parity(264)
msgbox % "ClientD <=> " parity("ClientD")

PARITY(var=0) {
global SU_list
If ( !(p0 := InStr(SU_list, "[" var "]" )) || var=0 )
  return "NULL"
p1 := InStr(SU_list, "[", 0, p0 += var+0 ? 4 : -8 ) , p2 := InStr(SU_list, "]", 0, p1)
return SubStr(SU_list, p1+1, p2-p1-1)
}

What is the range for socket value (minimum and maximum handle of socket ) ? I don't find it.
I have only find the default value of 64 "sockets" limitations (maybe 48^^) with FD_SETSIZE <wsock32.h>, that I don't know how to change it...

Zaelia
  • Members
  • 754 posts
  • Last active: Jan 17 2015 02:38 AM
  • Joined: 31 Oct 2008
Step by step, I always work on it...

Small AddOn for use URL: (insert "IF" after Socket)
IPAdress=www.google.com ;  smtp.gmail.com / uploads.google.com
Port=80 ;  25 /21

If ( RegExMatch(IPAddress, "[^0-9.]") )  ; check Ipv4 format
{
  noUrl := NumGet(DllCall("Ws2_32\gethostbyname", "str", IPAddress)+24)
  IPAddress := NumGet(noUrl+0, 0, "UChar")
  Loop 3
  IPAddress .= "." NumGet(noUrl+0, a_index, "UChar") ; transform URL in IPv4 adress ( xxx.xxx.xxx.xxx )
}


Zaelia
  • Members
  • 754 posts
  • Last active: Jan 17 2015 02:38 AM
  • Joined: 31 Oct 2008
After hard work week, I hope this attempt to IpV6 run at your home, I can't test it, only in local... Need feedback

IPAddress = 0:0:0:0:0:0:0:1
;IPAddress = 127.0.0.1
Port = 8765
Mode = Client

; ...

VarSetCapacity(wsaData, 128, 0)
  DllCall("Ws2_32\WSAStartup", "UShort", 2, "UInt", &wsaData)

StringReplace, IPAddress, IPAddress, :, :, UseErrorLevel
IPver := ( errorlevel = 7 ) ? 23 : 2

socket := DllCall("Ws2_32\socket", "Int", IPver, "Int", 1, "Int", 6)

; ...

VarSetCapacity(sockaddr, 128, 0)
  NumPut(IPver, sockaddr, 0, "Short")
  NumPut(DllCall("Ws2_32\htons", "UShort", Port), sockaddr, 2, "UShort")

if IPver = 2
  NumPut(DllCall("Ws2_32\inet_addr", "Str", IPAddress), sockaddr, 4)

if IPver = 23
  Loop, Parse, IPAddress, :
    NumPut(DllCall("Ws2_32\htons", "UShort", "0x" . a_loopfield ), sockaddr, (a_index*2)-2+8, "UShort")

if ( Mode = "Client" )
  DllCall("Ws2_32\connect", "Int", socket, "UInt", &sockaddr, "Int", 128)

if ( Mode = "Server" )
{
  DllCall("Ws2_32\bind", "UInt", socket, "UInt", &sockaddr, "Int", 128)
  DllCall("Ws2_32\listen", "UInt", socket, "UInt", "SOMAXCONN")
}

DllCall("Ws2_32\WSAAsyncSelect", "UInt", socket, "UInt", WinExist("A"), "UInt", 0x5555, "Int", 0x01|0x08|0x20)
OnMessage(0x5555, "WinsockMessage")

; ...

According to MSDN

If an application uses Windows Sockets 1.1 functions and requires IPv6 addresses, it may continue to use all the old functions that take the sockaddr structure as one of the parameters (bind (Windows Sockets), connect (Windows Sockets), sendto, recvfrom, accept (Windows Sockets), and so forth). The only change that is required is to use sockaddr_in6 instead of sockaddr_in.

If I use other function it's work but at half^^ and it needs Vista or more for ipv6 functions, so I think it's a good idea to just change the structure of "sockaddr"

edit: (small tool)
IPV4 = 127.0.0.1

stringsplit, sub, IPV4, .
  var1 := sub1*256+sub2 , var2:= sub3*256+sub4

SetFormat, IntegerFast, Hex
  var1 += 0 , Var1 .="" , var2 += 0, Var2 .=""
    SetFormat, IntegerFast, Dec

IPV6 := "0:0:0:0:0:0:" var1 ":" var2
  StringReplace, IPV6, IPV6, 0x, , All

msgbox IPV4 compatible`n%IPV6%


tomoe_uehara
  • Members
  • 2166 posts
  • Last active: Jun 11 2015 05:33 PM
  • Joined: 05 Sep 2009
Wow.... Nice scripts =D

Btw, I have no clue about these lines... How to obtain these parameters?

IPAddress = 127.0.0.1
Port = 8765



Zaelia
  • Members
  • 754 posts
  • Last active: Jan 17 2015 02:38 AM
  • Joined: 31 Oct 2008
protocol (TCP/IP) -> conveyance (car, road)
address (IPAddress) -> building (country, city, street, house)
port (Port) -> apartment (door)
socket = link with addr+port and apps -> directory

So, IPAddress is like an URL, <!-- w -->www.whatismyip.com<!-- w -->
And port is just a door for in/out data transfert

If you are a server, for example use %a_IPAddress1%, it's your "PC address for internet" (carefull with firewall and router)
Else, as a client, you need to target address of server.

Port can be random if you are a Server, but as IPAddress you need to target port server if you are a Client. However port = 21 for FTP (file/data transfert) , 25 for SMTP (send email) , 80 for HTTP (internet page), and other port are special, don't use it...

Wiki is your friend :) sorry for metaphor, and check your network in control panel

An other thing, 127.0.0.1 is special , it's the local host = "my computer address"