' ' FD_FILE_RECEIVER : a Server by Jacques Philippe - July 2001 ' ' This Software receives files Sent by FD_FILESENDER.EXE/BAS ' ' !!!!!!!! this Server Accepts Only ONE SINGLE Client !!!!!!!! ' ' It Uses and Tests the Windows Messages (FD_XXXX) sent by the System -Windows- ' to manage the connection. There are no 'local' timers involved anymore and ' no IsServerReady, IsClientReady, If Transfered = 0 ... either ' ' NOTE : socket.WMessage and socket.LParam dont seem to work with Servers in rapidQ ???? ' It looks like the WMessage and LParam are not inherited from master server socket ' by Accepted Socket in QSocket ??? So we here use API function WSAAsyncSelect ! ' ' A ?good? help is the Winsock function WSAAsyncSelect documentation (see below) ' ' ' Hand Shaking : ' ' TX : connect ' Tx : "****SaveThisFile " 'space separator ' Rx : "****OK You Can Upload The File" ' TX : sends the file ' Rx : "****OK File Received" ' when it has received filelength bytes ' TX : disconnect ' ' Tx is the client transmitting the file and Rx the server receiving it ' $APPTYPE GUI $ESCAPECHARS ON $TYPECHECK ON $INCLUDE "RAPIDQ.INC" ' API Winsock Declarations Declare Function WSAAsyncSelect Lib "wsock32.dll" Alias "WSAAsyncSelect" (s As Long, hwnd As Long, wMsg As Long, lEvent As Long) As Long Declare Function WSAGetLastError Lib "WSOCK32" Alias "WSAGetLastError" () As Long ' API DECLARE FUNCTION Setfocus Lib "user32" Alias "SetFocus"(hwnd As Long) As Long Declare Sub FormWndProc (Hwnd as Long, uMsg as Long, wParam as long, lParam as Long) Declare Sub OnClic_btnDisConnect Declare Sub AddToRxWin (sText as string) Declare Sub SendFile Declare Sub ReInitialise Declare Sub GetFileNameAndLength (sText As String) ' You may change these two constants CONST WM_SOCK = 100696 ' Make sure it's > 1024, whatever you want CONST TEST_PORT = "88" ' WSA Constants defining type of messages CONST FD_READ = 1 ' 001h there is something to read on the socket CONST FD_WRITE = 2 ' 002h one sent on connect, and one **AFTER** a WSAEWOULDBLOCK error had occured ' as soon as the send buffer is free again. ' The way FD_WRITE works is not easy to understand ! Not "intuitive" CONST FD_OOB = 4 ' 004h dont wanna know :) OUT OF BAND datas CONST FD_ACCEPT = 8 ' 008h for servers only, a connection request has been received : Accept it ? Or Not ? CONST FD_CONNECT = 16' 010h not used CONST FD_CLOSE = 32 ' 020h socket is now closed (by remote host) CONST WSAEWOULDBLOCK = 10035 ' Used for/by FD_WRITE : occurs on a Send, means that Not enough space in send buffer, ' nothing done, wait for the next FD_WRITE msg to RESEND these datas CONST PACKETMAXLENGTH = 984 ' ' Global Variables Dim numActiveSck as Long ' The "Number" Of The "Active" Socket : the one on which datas are exchanged Dim numFileReceiverMasterServer as Long ' The "Number" of The Master Server Socket Dim RxWinText As String ' A buffer to rchRxWin.Text (The receiving Window) Dim receivedFile as QFileStream ' Dim sFilename as String ' a variable for the selected filename Dim iFileLength As Long Dim iBytesReceived As Long Dim transferStartTime As Single ' to measure the file transfer time Dim transferEndTime As Single ' Form CREATE frmFileReceiver AS QForm Width = 750 Height = 300 Center Caption = "Hellionor FILE RECEIVER : Server Version 1.0.1 (accepts only one client)" Autoscroll = false 'Color = &HC39459 CREATE sckFileReceiver AS QSocket END CREATE CREATE btnDisConnect AS QButton Left = frmFileReceiver.ClientWidth - 120 Top = 5 Width = 116 Height = 25 Font.Bold = True Caption = "DIS&Connect" OnClick = OnClic_btnDisConnect END CREATE CREATE btnRestartServer AS QButton ' To Set New Port Left = 63 Top = 5 Width = 110 Height = 25 Font.Bold = True Caption = "&Restart Server" OnClick = ReInitialise END CREATE CREATE edtPort AS QEdit ' the HOSTNAME or IP ADDRESS to Connect Left = 3 Top = 5 Width = 50 Height = 25 ShowHint = True Hint = " Enter here a Port Number \n Then Clic Restart Server To Set The New Port Number \n ex : TELNET 23, CHAT 87, CONVERS 3600 " Text = TEST_PORT END CREATE CREATE rchRxWin AS QRichEdit ' will disply the received datas Top = 35 Left = 3 Font.Name = "courier" Width = frmFileReceiver.ClientWidth - 6 Height = frmFileReceiver.ClientHeight - 40 ' ? ReadOnly = True WordWrap = False ScrollBars = ssBoth HideSelection = False END CREATE WndProc = FormWndProc END CREATE ' Creates necessary directories MkDir "rxFiles" ' for rxFile DefStr strTmp ' Opened as a Server on start strTmp = edtPort.Text strTmp = strTmp - " " numFileReceiverMasterServer = sckFileReceiver.Open(Val(strTmp)) ' only accept FD_MESSAGE for now If WSAASyncSelect (numFileReceiverMasterServer, frmFileReceiver.Handle, WM_SOCK, FD_ACCEPT) <> 0 Then ShowMessage ("Error While Opening Server WSA error = " & Str$(WSAGetLastError) & "\n\n ANY KEY TO END/EXIT") END End If ' Some Help Text :) AddToRxWin (Time$ & " , " & Date$ & "\n\n*** FD_FILE_RECEIVER is now ON, WAITING for a Connection on Port " & (edtPort.Text - " ") & _ "\n*** at IpAddress : " & sckFileReceiver.GetHostIp & " ..... WAITING .....\n") SetFocus (rchRxWin.Handle) ' ******************************************************************** frmFileReceiver.ShowModal ' ******************************************************************** ' This sub receives the Window Messages SUB FormWndProc (Hwnd as Long, uMsg as Long, wParam as Long, lParam as Long) Dim Line as String Dim sTmp As String Dim LowLparam as Long Dim HighLparam as Long LowLParam = LParam and &H0000FFFF ' contain the message Type FD_XXXX HighLParam = (LParam and &HFFFF0000)/&H10000 ' may contain Error Code, Not Used For Now IF uMsg = WM_SOCK THEN ' filters the message WM_SOCK (= OURS) Select Case LowLParam ' contains the type of message Case FD_READ '=1 : there is something new to read on the socket OK Line = sckFileReceiver.Read(numActiveSck, PACKETMAXLENGTH) ' detect hand shaking If Left$ (Line, 8) = "****Save" Then ' avoid long check at each frame GetFileNameAndLength (Line) receivedFile.Open ("rxFiles\\" & sFileName, fmCreate) sTmp = "****OK You Can Upload The File" sckFileReceiver.Write (numActiveSck, sTmp, Len(sTmp)) AddToRxWin ("*** RECEIVING FILE : \"" & sFileName & "\" of Length = " & Str$(iFileLength) & "\n") transferStartTime = Timer Else ' Receives the file receivedFile.Write(Line) iBytesReceived = iBytesReceived + sckFileReceiver.Transferred btnRestartServer.Caption = Str$(iBytesReceived) & " xmit" If iBytesReceived = iFileLength Then sTmp = "****OK File Received" sckFileReceiver.Write (numActiveSck, sTmp, Len(sTmp)) transferEndTime = Timer AddToRxWin ("*** FILE \"" & sFileName & "\" of length " & Str$(iFileLength) & " RECEIVED and SAVED\n" _ & "*** in " & Str$(transferEndTime - transferStartTime) _ & " seconds, bit rate of " & Str$(iFileLength * 8/(transferEndTime - transferStartTime)) & "\n") btnRestartServer.Caption = "&Restart Server" receivedFile.Close End If End If Case FD_ACCEPT ' 8 '=8 The Server has received a Connection Request : Accept Y/N numActiveSck = sckFileReceiver.Accept(numFileReceiverMasterServer) If WSAASyncSelect (numActiveSck, frmFileReceiver.Handle, WM_SOCK, FD_READ & FD_WRITE & FD_CLOSE) <> 0 Then ShowMessage ("*** ERROR : WSA Error = " & Str$(WSAGetLastError) & Str$(WSAGetLastError) & "\n\n ANY KEY TO END/EXIT") End End If ' socket.WMessage and socket.LParam dont seem to work with Servers ???? ' It looks like the WMessage and LParam are not inherited from ' master server in QSocket ??? So I use WSAAsyncSelect ' Welcome Msg To Conecting Client AddToRxWin ("*** SERVER ACCEPT a Client\n") Case FD_CLOSE '=32 connection closed by remote host (???or error???) AddToRxWin ("*** DISCONNECTED By Remote Host !\n") ReInitialise End Select END IF END SUB ' Btn Connect/Disconnect ... CLIENT MODE SUB OnClic_btnDisConnect AddToRxWin ("*** DISCONNECTED By Local Host\n") ReInitialise ' Restarts the Server SetFocus (rchRxWin.Handle) END SUB ' Add a Text To The rchRxWin (Displaying received text Upper richText One) Sub AddToRxWin (sText as String) RxWinText = RxWinText & sText If Len(RxWinText) > 10000 Then RxWinText = Right$(RxWinText, 8000) ' limits the length of rchRxWin.Text rchRxWin.Text = RxWinText rchRxWin.SelStart = Len (RxWinText) ' AutoScroll Up of RxWindow End Sub ' ReInitialises in Server Mode Sub ReInitialise sckFileReceiver.Close (numFileReceiverMasterServer) sckFileReceiver.Close (numActiveSck) iBytesReceived = 0 iFileLength = 0 numFileReceiverMasterServer = sckFileReceiver.Open(Val(edtPort.Text - " ")) If WSAASyncSelect (numFileReceiverMasterServer, frmFileReceiver.Handle, WM_SOCK, FD_ACCEPT) <> 0 Then ShowMessage ("Error While Opening Server WSA error = " & Str$(WSAGetLastError) & "\n\n ANY KEY TO END/EXIT") END End If AddToRxWin ("\n*** FD_FILE_RECEIVER REINITIALISED, SERVER WAITING for a Connection\n" _ & "*** on PORT " & (edtPort.Text - " ") & " at IPADDRESS : " & sckFileReceiver.GetHostIp & " ..... WAITING .....\n") SetFocus (rchRxWin.Handle) End Sub Sub GetFileNameAndLength (sText As String) DefInt iTmp iTmp = Rinstr (sText, " ") sFileName = Mid$(sText, 18, iTmp - 18) iFileLength = Val(Right$ (sText, Len(sText) - iTmp)) End Sub ' SOME DOCS ' --------- ' ' RAPIDQ DOC ' ------------------ ' WndProc SUB (Hwnd%, Msg%, wParam%, lParam%) Messages posted/sent to form ' only one WndProc per Form is allowed. So if you have multiple forms, only one may be able to ' receive messages at a time. (Future consideration to correct this) ' ' Hwnd = yourForm.Handle that have received the message ' Msg = the number your have chosen for your messages WM_SOCK here ' wParam = the number of the socket on which the events has happened ' lParam = Low byte : the type of event (FD_XXXX) High Byte : ?? an eventual error code ??? ' ' For more info, search the web with WSAAsyncSelect & FD_ACCEPT '