Oh okay, that makes more sense
Okay, so the
AHKSock uses
TCP, which is a streaming connection.
Para1: So if I wanted to use the
AHKsock_Listen() as a
Client, and then use
AHKsock_Send() as a
Server and send data to the
Client, how can I do this?
I have looked at the examples @TheGood has provided on that link above, and I played around with it a little, I amended it to suit my needs (
para1), however, Instead of
AHKsock_Send(), it uses the
AHKsock_Connect() function, and it uses an additional "Send" and "Recv" (receive) function which @TheGood designed. So basically, what @TheGood has done is designed the SEND and RECV function to convert the data/file that you want to send into Packets (I think) and then send it accordingly, and then you tell the receive function how you want the file to be named and what extension it should have (hope that makes sense).
So for instance:
If I have a file called
Song.mp3 which I want to send. The
AHKsock_Connect() will then connect to the SEND function which will convert this and loose it's
name and
extension and only preserve the data itself.
It then sends it to the pre-defined IP/Port.
At that port, you have the
AHKsock_Listen() listening for connections, soon as the
AHKsock_Connect() connects, it will transfer the data into the remote computer and whilst doing so, it will use the "RECV" function to put the packets back together to create one single file as it was at its originating source.
However, as the
name and
extension was lost, you have to rename the file to whatever name it should have and also provide it's extension. So in this case, the file would be transferred on to the second computer as, for example
File.dat, which itself is not useable as its Data is Audio type. So it would have to be manually renamed to
Song.mp3 before you can use it.
So my question is, how can I replace the
AHKsock_Connect() with
AHKsock_Send() and would it be possible for the file to be transferred just as it was sent, rather than having to be manually renamed?
Here is the working code I have put together using the example by @TheGood.
ITS FOR Ahk_L and you must also Download AHKSock Lib for Ahk_L (it's included using #Include)
Receiver (Listener)
Code:
#SingleInstance, Force
sPort := 999 ; Port on the current IP
sFile := A_ScriptDir "\File.dat" ; Name and location to save incomming file
ConfirmFileReceived=No ; Yes or No
AHKsock_Listen(sPort, "Recv") ; Automatically start listening on the port
OnExit, CloseAHKsock
Return
CloseAHKsock:
AHKsock_Close()
ExitApp
#Include AHKSock.ahk ; Main SOCK Lib
;---------------------------------------------------------
;Other functions by @TheGood needed in this example
;
; Recv()
;
; CopyBinData()
; File_Open()
; File_Read()
; File_Write()
; File_Pointer()
; File_Size()
; File_Close()
;---------------------------------------------------------
Recv(sEvent, iSocket = 0, sName = 0, sAddr = 0, sPort = 0, ByRef bNewData = 0, bNewDataLength = 0) {
Static bPastData, bPastDataLength
Static hFile := -1, iFileSize, qpTstart
Global qpFreq, bSilent, sFile
If (sEvent = "CONNECTED") {
;Check if the connection attempt was succesful
If (iSocket = -1) {
OutputDebug, % "Client - AHKsock_Connect() failed."
MsgBox, 0x10, Error, Could not connect to the server at: %sName%`nEither try again or change the server address.
}
OutputDebug, % "Client - AHKsock_Connect() successfully connected!"
} Else If (sEvent = "DISCONNECTED") {
OutputDebug, % "Client - The server closed the connection."
;If the file isn't closed, then it means that we abruptly ended the transfer
If (hFile <> -1) {
File_Close(hFile)
hFile := -1 ;Reset value to indicate that we closed the file
;Delete the incomplete file
FileDelete, %sFile%
}
If FileExist(sFile) ;Make sure it exists
Run % "explorer.exe /select," sFile
Tooltip
If ConfirmFileReceived=Yes
MsgBox, Receiver, File Received:`n%sFile%
} Else If (sEvent = "RECEIVED") {
;Don't uncomment this line if receiving large files, or otherwise the log will quickly fill up.
;OutputDebug, % "Client - We received " bNewDataLength " bytes." ;FOR DEBUGGING PURPOSES ONLY
;Check if the target file is ready for writing
If (hFile = -1) {
;We need to get the file ready for writing
;Rename file if it already exists
SplitPath, sFile , , Dir, Extension, Name
NewPath = %Dir%\%Name%.%Extension%
Loop {
IfExist, %NewPath%
{
NewPath= %Dir%\%Name% (%A_Index%).%Extension%
}
Else
{
sFile:=NewPath
Break
}
}
;Open the file for writing
hFile := File_Open("Write", sFile)
If (hFile = -1) { ;Check for error
OutputDebug, % "Client - Could not open the file! ErrorLevel = " ErrorLevel
MsgBox, % "Client - Could not open the file! ErrorLevel = " Errorlevel "`nFunction: Recv"
Return
}
}
;Check if we have any data to prepend
If bPastDataLength {
bDataLength := bNewDataLength + bPastDataLength
;Prep the variable which will hold past and new data
VarSetCapacity(bData, bDataLength, 0)
;Copy old data and then new data
CopyBinData(&bPastData, &bData, bPastDataLength)
CopyBinData(&bNewData, &bData + bPastDataLength, bNewDataLength)
;We can now delete the old data
VarSetCapacity(bPastData, 0) ;Clear the variable to free some memory since it won't be used
bPastDataLength := 0 ;Reset the value
;Set the data pointer to the new data we just created
bDataPointer := &bData
;Set the data pointer to the newly arrived data
} Else bDataPointer := &bNewData, bDataLength := bNewDataLength
;Check if we fully received the 8-byte file size integer yet.
If Not iFileSize {
;Check if only part of the 8 bytes are here
If (bDataLength < 8) {
;Save what we have and leave
VarSetCapacity(bPastData, 8, 0)
CopyBinData(bDataPointer, &bPastData, bDataLength)
Return
}
;Extract the 8 bytes
iFileSize := NumGet(bDataPointer + 0, 0, "int64")
OutputDebug, % "Client - File size is " iFileSize " bytes!"
;Check if there is data after the 64-bit integer that we have to write to the file
If (bDataLength = 8) {
;Reset the performance counter value so that it will be
;queried just before writing the first bytes to the file
qpTstart := -1
Return ;Nothing to write
}
;We're about to write the first bytes. Query the performance counter before!
DllCall("QueryPerformanceCounter", "int64*", qpTstart)
;Write the data after the 64-bit integer to the file (that's why we do + 8)
iWritten := File_Write(hFile, bDataPointer + 8, bDataLength - 8)
} Else {
If (qpTstart = -1) ;Check if it hasn't already been queried
DllCall("QueryPerformanceCounter", "int64*", qpTstart)
;Append the data we received to the file
iWritten := File_Write(hFile, bDataPointer, bDataLength)
}
;Don't uncomment this line if receiving large files, or otherwise the log will quickly fill up.
;OutputDebug, % "Client - Data written to file: " iWritten ;FOR DEBUGGING PURPOSES ONLY
;Get the current file pointer (i.e. the number of bytes written to file so far)
iPointer := File_Pointer(hFile)
;Check if we have received the whole file
If (iPointer >= iFileSize) {
;We can close the file handle and reset the value
File_Close(hFile)
hFile := -1 ;Reset value to indicate that we closed the file
;Output the average speed for the transfer
DllCall("QueryPerformanceCounter", "int64*", qpTend)
OutputDebug, % "Average speed = " Round((iFileSize / 1024) / ((qpTend - qpTstart) / qpFreq)) " kB/s"
}
If iPointer/1000 < 1000
ToolTip, % Sep("0"RegExReplace(iPointer/1000,".*","","",-1,3),".") " / " Sep(RegExReplace(iFileSize/1000,".*","","",-1,4),".") " MB"
Else
ToolTip, % Sep(RegExReplace(iPointer/1000,".*","","",-1,4),".") " / " Sep(RegExReplace(iFileSize/1000,".*","","",-1,4),".") " MB"
}
}
Sep(Number, Separator=",") {
Return RegExReplace(Number,"\G\d+?(?=(\d{2})+(?:\D|$))","$0" Separator)
}
CopyBinData(ptrSource, ptrDestination, iLength) {
If iLength ;Only do it if there's anything to copy
DllCall("RtlMoveMemory", "Ptr", ptrDestination, "Ptr", ptrSource, "UInt", iLength)
}
File_Open(sType, sFile) {
bRead := InStr(sType, "READ")
bSeq := sType = "READSEQ"
;Open the file for writing with GENERIC_WRITE/GENERIC_READ, NO SHARING/FILE_SHARE_READ & FILE_SHARE_WRITE, and
;OPEN_ALWAYS/OPEN_EXISTING, and FILE_FLAG_SEQUENTIAL_SCAN
hFile := DllCall("CreateFile", "Str", sFile, "UInt", bRead ? 0x80000000 : 0x40000000, "UInt", bRead ? 3 : 0, "Ptr", 0
, "UInt", bRead ? 3 : 4, "UInt", bSeq ? 0x08000000 : 0, "Ptr", 0, "Ptr")
If (hFile = -1 Or ErrorLevel) { ;Check for any error other than ERROR_FILE_EXISTS
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return -1 ;Return INVALID_HANDLE_VALUE
} Else Return hFile
}
File_Read(hFile, ByRef bData, iLength = 0) {
;Check if we're reading up to the rest of the file
If Not iLength ;Set the length equal to the remaining part of the file
iLength := File_Size(hFile) - File_Pointer(hFile)
;Prep the variable
VarSetCapacity(bData, iLength, 0)
;Read the file
r := DllCall("ReadFile", "Ptr", hFile, "Ptr", &bData, "UInt", iLength, "UInt*", iLengthRead, "Ptr", 0)
If (Not r Or ErrorLevel) {
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return -1
} Else Return iLengthRead
}
File_Write(hFile, ptrData, iLength) {
;Write to the file
r := DllCall("WriteFile", "Ptr", hFile, "Ptr", ptrData, "UInt", iLength, "UInt*", iLengthWritten, "Ptr", 0)
If (Not r Or ErrorLevel) {
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return -1
} Else Return iLengthWritten
}
File_Pointer(hFile, iOffset = 0, iMethod = -1) {
;Check if we're on auto
If (iMethod = -1) {
;Check if we should use FILE_BEGIN, FILE_CURRENT, or FILE_END
If (iOffset = 0)
iMethod := 1 ;We're just retrieving the current pointer. FILE_CURRENT
Else If (iOffset > 0)
iMethod := 0 ;We're moving from the beginning. FILE_BEGIN
Else If (iOffset < 0)
iMethod := 2 ;We're moving from the end. FILE_END
} Else If iMethod Is Not Integer
iMethod := (iMethod = "BEGINNING" ? 0 : (iMethod = "CURRENT" ? 1 : (iMethod = "END" ? 2 : 0)))
r := DllCall("SetFilePointerEx", "Ptr", hFile, "Int64", iOffset, "Int64*", iNewPointer, "UInt", iMethod)
If (Not r Or ErrorLevel) {
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return -1
} Else Return iNewPointer
}
File_Size(hFile) {
r := DllCall("GetFileSizeEx", "Ptr", hFile, "Int64*", iFileSize)
If (Not r Or ErrorLevel) {
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return -1
} Else Return iFileSize
}
File_Close(hFile) {
If Not (r := DllCall("CloseHandle", "Ptr", hFile)) {
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return False
} Return True
}
Sender (This is where you the file you want to send)
Code:
#SingleInstance, Force
OnExit, CloseAHKsock
sServer := "81.100.20.215" ; Hostname/IP you want to connect to. Localhost = this computer
sPort := 999 ; Port on the above Hostname/IP (sServer)
sFile := A_ScriptFullPath ; Path of the file you want to send
ConfirmFinishSending=No ; Yes or No
AHKsock_Connect(sServer, sPort, "Send")
Return
CloseAHKsock:
AHKsock_Close()
ExitApp
#Include AHKSock.ahk ; Main SOCK Lib
;---------------------------------------------------------
;Other functions by @TheGood needed in this example
;
; Send()
;
; File_Open()
; File_Read()
; File_Write()
; File_Pointer()
; File_Size()
; File_Close()
;---------------------------------------------------------
Send(sEvent, iSocket = 0, sName = 0, sAddr = 0, sPort = 0, ByRef bData = 0, iLength = 0) {
Global sFile
If (sEvent = "ACCEPTED") {
OutputDebug, % "Server - A client connected!"
} Else If (sEvent = "DISCONNECTED") {
OutputDebug, % "Server - A client disconnected!"
} Else If (sEvent = "SEND") {
;Open the file for reading
hFile := File_Open("ReadSeq", sFile)
If (hFile = -1) { ;Check for error
OutputDebug, % "Server - Could not open the file! ErrorLevel = " ErrorLevel
;Disconnect the client
AHKsock_Close(iSocket)
Return
}
;Get the size
iFileSize := File_Size(hFile)
If (iFileSize = -1) { ;Check for error
OutputDebug, % "Server - Could not get the file size! ErrorLevel = " ErrorLevel
;Disconnect the client and close the file
File_Close(hFile)
AHKsock_Close(iSocket)
Return
}
;Prep the TRANSMIT_FILE_BUFFERS struct so that the size of the file will be sent before sending the file (see MSDN
;of TransmitFile() for details).
;We need to create an actual 64-bit integer manually, because AHK keeps variables as strings
VarSetCapacity(bFileSize, 8, 0)
NumPut(iFileSize, bFileSize, 0, "Int64")
VarSetCapacity(fTransmitFileBuffers, 4 * A_PtrSize, 0)
NumPut(&bFileSize, fTransmitFileBuffers)
NumPut(8, fTransmitFileBuffers, A_PtrSize)
;Call TransmitFile
r := DllCall("mswsock\TransmitFile", "Ptr", iSocket, "Ptr", hFile, "UInt", 0, "UInt", 0, "UInt", 0
, "Ptr", &fTransmitFileBuffers, "UInt", 0)
If (Not r Or ErrorLevel) {
ErrorLevel := ErrorLevel ? ErrorLevel : AHKsock_LastError()
OutputDebug, % "Server - TransmitFile() failed with error code = " ErrorLevel
}
;Close the file and the socket
File_Close(hFile)
AHKsock_Close(iSocket)
If ConfirmFinishSending=Yes
MsgBox,, Sender, File send complete!
ExitApp
}
}
File_Open(sType, sFile) {
bRead := InStr(sType, "READ")
bSeq := sType = "READSEQ"
;Open the file for writing with GENERIC_WRITE/GENERIC_READ, NO SHARING/FILE_SHARE_READ & FILE_SHARE_WRITE, and
;OPEN_ALWAYS/OPEN_EXISTING, and FILE_FLAG_SEQUENTIAL_SCAN
hFile := DllCall("CreateFile", "Str", sFile, "UInt", bRead ? 0x80000000 : 0x40000000, "UInt", bRead ? 3 : 0, "Ptr", 0
, "UInt", bRead ? 3 : 4, "UInt", bSeq ? 0x08000000 : 0, "Ptr", 0, "Ptr")
If (hFile = -1 Or ErrorLevel) { ;Check for any error other than ERROR_FILE_EXISTS
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return -1 ;Return INVALID_HANDLE_VALUE
} Else Return hFile
}
File_Read(hFile, ByRef bData, iLength = 0) {
;Check if we're reading up to the rest of the file
If Not iLength ;Set the length equal to the remaining part of the file
iLength := File_Size(hFile) - File_Pointer(hFile)
;Prep the variable
VarSetCapacity(bData, iLength, 0)
;Read the file
r := DllCall("ReadFile", "Ptr", hFile, "Ptr", &bData, "UInt", iLength, "UInt*", iLengthRead, "Ptr", 0)
If (Not r Or ErrorLevel) {
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return -1
} Else Return iLengthRead
}
File_Write(hFile, ptrData, iLength) {
;Write to the file
r := DllCall("WriteFile", "Ptr", hFile, "Ptr", ptrData, "UInt", iLength, "UInt*", iLengthWritten, "Ptr", 0)
If (Not r Or ErrorLevel) {
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return -1
} Else Return iLengthWritten
}
File_Pointer(hFile, iOffset = 0, iMethod = -1) {
;Check if we're on auto
If (iMethod = -1) {
;Check if we should use FILE_BEGIN, FILE_CURRENT, or FILE_END
If (iOffset = 0)
iMethod := 1 ;We're just retrieving the current pointer. FILE_CURRENT
Else If (iOffset > 0)
iMethod := 0 ;We're moving from the beginning. FILE_BEGIN
Else If (iOffset < 0)
iMethod := 2 ;We're moving from the end. FILE_END
} Else If iMethod Is Not Integer
iMethod := (iMethod = "BEGINNING" ? 0 : (iMethod = "CURRENT" ? 1 : (iMethod = "END" ? 2 : 0)))
r := DllCall("SetFilePointerEx", "Ptr", hFile, "Int64", iOffset, "Int64*", iNewPointer, "UInt", iMethod)
If (Not r Or ErrorLevel) {
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return -1
} Else Return iNewPointer
}
File_Size(hFile) {
r := DllCall("GetFileSizeEx", "Ptr", hFile, "Int64*", iFileSize)
If (Not r Or ErrorLevel) {
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return -1
} Else Return iFileSize
}
File_Close(hFile) {
If Not (r := DllCall("CloseHandle", "Ptr", hFile)) {
ErrorLevel := ErrorLevel ? ErrorLevel : A_LastError
Return False
} Return True
}
Thanks