Jeff - thank you for your post.
My code works fine when I don't send too much information. It also works
if I do send information and use my own ACK and NAK. But, I should not
have to do that. My only conclusion at this time is Winsock has bugs in
the control itself causing problems when sending a lot of data.
But, I'm still researching all this.
Post by Jeff GrippeThis is a more complex problem that fits a newsgroup posting but I can
explain some concepts and provide some samples.
I have message types defined and a packet protocol that I use which may
be too "heavy" for sending large files. I break up what I'm sending into
packets of a fixed length. I send one packet and wait for a confirmation
before sending the next packet. Each instance of my application has an IP
address and port which it listens on. All applications can both send and
receive.
You need to bind the winsock control to some port. I have multiple users
each of whom binds to a separate port. I begin with port 1002 and look
for an available port. Once a user has bound to a port, I save the
results in a table. I use UDP Protocol and I save all of my "messages" in
a table. This isn't necessary but I like having the trail. What I call a
message is just a string of characters. There's no reason why it couldn't
be the contents of a file.
*** PORT BINDING
this.nIPPort = 1002 && first port to try binding to
this.oMSWinSock.protocol = 1 && UDP
this.oMSWinSock.Bind(this.nIPPort)
*** ERROR HANDLING IN CASE THE PORT DOESN'T BIND
LPARAMETERS nError, cMethod, nLine
DO CASE
CASE nError = 1429 && Problem binding port
this.nIPPort = this.nIPPort + 1 && try next sequential port
RETRY
OTHERWISE
=MESSAGEBOX(STR(nError,4,0)+" "+cMethod+" "+STR(nLine,4,0)+MESSAGE())
ENDCASE
*** SENDING DATA
this.oMSWinSock.RemoteHost = lcToIP && whatever ip address you are
sending to
this.oMSWinSock.RemotePort = lnToPort && whatever port you are sending to
*** My particular message protocol
lcFullMessage = "<MESSAGE>"+;
"<"+ALLTRIM(STR(lnPacket,5,0))+">"+;
"<"+ALLTRIM(STR(lnPacketCount,5,0))+">"+;
"<"+this.oMSWinSock.localip+">"+;
"<"+ALLTRIM(STR(this.nIPPort,6,0))+">"+;
"<"+pcmessageid+">"+;
"<"+SYS(2007,lcMessageSegment)+">"+;
"|"+lcMessageSegment
*** Remember that I save my messages
SELECT SentMessages
APPEND BLANK
REPLACE type WITH "MESSAGE", ;
packet WITH lnPacket, ;
packetcount WITH lnPacketCount, ;
ToIP WITH this.oMSWinSock.RemoteHost, ;
ToPort WITH this.oMSWinSock.RemotePort, ;
MessageID WITH pcMessageID, ;
CheckSum WITH SYS(2007, lcMessageSegment), ;
MessageData WITH lcMessageSegment, ;
RawData WITH lcFullMessage
*** SENDING THE MESSAGE
this.oMSWinSock.SendData(lcFullMessage)
The message comes in to the WinSock control and fires the
DataArrivalEvent. This in turn uses the GetData method to return the
data. That is where your code has to go to pickup the data that was sent.
*** RECEIVING THE DATA IN THE DataArrivalEvent
*** ActiveX Control Event ***
LPARAMETERS bytestotal
lcData = SPACE(256) && Define string to pass to GetData
* get fields and data
lcType = STREXTRACT(lcData, "<", ">", 1)
*** Processing the data using my protocol
DO CASE
CASE lcType = "MESSAGE" && message
lnSplitPos1 = AT("|", lcData, 1)
lnPacketNumber = VAL(STREXTRACT(lcData, "<", ">", 2))
lnTotalPackets = VAL(STREXTRACT(lcData, "<", ">", 3))
lcIPFrom = STREXTRACT(lcData, "<", ">", 4)
lnPortFrom = VAL(STREXTRACT(lcData, "<", ">", 5))
lcMessageID = STREXTRACT(lcData, "<", ">", 6)
lcCheckSum = STREXTRACT(lcData, "<", ">", 7)
lcMessage = SUBSTR(lcData, lnSplitPos1+1, LEN(lcData) - lnSplitPos1+1)
*** only necessary if you want to save the data like I do. Very useful
for debugging
SELECT RecvMessages
APPEND BLANK
replace type WITH lcType, ;
packet WITH lnPacketNumber, ;
packetcount WITH lnTotalPackets, ;
FromIP WITH lcIPFrom, ;
FromPort WITH lnPortFrom, ;
MessageID WITH lcMessageID, ;
CheckSum WITH lcCheckSum, ;
MessageData WITH lcMessage, ;
RawData WITH lcData
IF lcCheckSum <> SYS(2007, lcMessage) && checksum mismatch
=MESSAGEBOX("Check Sum Error" + lcData)
* send Re-Transmit Request - Not yet coded. This hasn't happened
yet (fortunately)
ELSE
* send Packet Confirmation
this.Parent.SendConfirmation(lcMessageID, lnPacketNumber)
ENDIF
IF lnPacketNumber = lnTotalPackets && last packet received
*** if all packets received correctly
this.Parent.Messagereceived(lcMessageID)
ENDIF
CASE lcType = "CONFIRM"
lcMessageID = STREXTRACT(lcData, "<", ">", 2)
lnPacketNumber = VAL(STREXTRACT(lcData, "<", ">", 3))
SELECT SentMessages
SET ORDER TO MessageID
SEEK lcMessageID + STR(lnPacketNumber,5,0)
IF FOUND()
replace confirmed WITH .t.
ENDIF
SELECT RecvMessages
APPEND BLANK
replace type WITH lcType, ;
packet WITH lnPacketNumber, ;
MessageID WITH lcMessageID, ;
RawData WITH lcData
this.Parent.lastconfirm = lnPacketNumber
this.Parent.ConfirmationReceived(lcMessageID)
OTHERWISE
=MESSAGEBOX(lcData)
ENDCASE
Post by MikeADoes anyone have sample code using winsock to transfer a file with a VFP
program clientsample.prg and serversample.prg? I'm still having
problems with no answers from prior posts and every time I think I'm
that much closer to a solution it just doesn't happen. I would think
someone would have some sample code somewhere. I searched the UT last
night but found nothing and I'm just getting a little tired of staying
up til well after 3AM every night.
All help would be greatly appreciated.
Thanks,
Mike