VB6 client application which talks to one or multiple servers - sockets

I have a VB6 client application, which creates 1 or more (upto 4) sockets and connects to one or more TCP servers.
The client is supposed to continuously send requests to the server and wait for the server to respond for a certain responseTime. If the response does not arrive in the "responseTime", the client should send the next request on one of the sockets.
What is best way to make the client wait till the response arrives on the socket?
I do the following to have the client wait for the response/data to arrive: (Here the dataProcessed flag is set to True by the helper function invoked from the dataArrival() routine. This flag indicates that a response has been received and processed.
*Do While ((Timer < SentRequestTime) + responseTimeout) And (dataProcessed = False))
'DoEvents OR Sleep
Sleep 50
End If
Loop*
If I use "DoEvents" in the while loop, the application works fine for a while but later even though the response comes back to TCP layer (which I have examined through wireshark), the application does not get the DataArrival event.
If I use "sleep", the dataArrival event does not get delivered during the while loop, but arrives as soon as the loop is over. Using sleep makes the application non responsive.
What is the best way to have a single threaded VB6 socket client application to send a request, "wait for the data " to arrive for a certain time and then move on to the next request?

I would forget about both DoEvents() and Sleep() here. Those are tools of last resort, and nearly no program should contain either one. You need to "think 4th dimensionally" i.e. "Trust the Events, Luke!" This ain't your daddy's QBasic.
Here's a simulation where four Command buttons act as the servers, i.e. you click them manually as they become enabled. Two Timer controls are used here because we need to simulate processing time and transmission delay.
Option Explicit
'Use 4 Command buttons to simulate TCP sockets making server
'requests and getting back responses. Each "send" must get
'a response within RESPONSE_TIME_MS or be counted as a "miss."
'A new request is sent in either case.
Private Const PROCESS_TIME_MS As Long = 2000
Private Const PROCESS_TICKS As Long = PROCESS_TIME_MS \ 10
Private Const PROCESS_TICK_MS As Long = PROCESS_TIME_MS \ PROCESS_TICKS
Private Const RESPONSE_TIME_MS As Long = 4000
Private Const RESPONSE_TICKS As Long = RESPONSE_TIME_MS \ 10
Private Const RESPONSE_TICK_MS As Long = RESPONSE_TIME_MS \ RESPONSE_TICKS
Private ProcessCountdowns(0 To 3)
Private ResponseCountdowns(0 To 3)
Private Misses(0 To 3)
Private Sub SendRequest(ByVal Socket As Integer)
ResponseCountdowns(Socket) = RESPONSE_TICKS
cmdResponse(Socket).Enabled = True
End Sub
Private Sub cmdResponse_Click(Index As Integer)
'This is a "DataArrival" event.
'Process the response, then send a new request:
cmdResponse(Index).Enabled = False
ResponseCountdowns(Index) = 0
ProcessCountdowns(Index) = PROCESS_TICKS
End Sub
Private Sub Form_Load()
Dim Socket As Integer
For Socket = 0 To 3
SendRequest Socket
Next
tmrProcess.Interval = PROCESS_TICK_MS
tmrProcess.Enabled = True
tmrResponse.Interval = RESPONSE_TICK_MS
tmrResponse.Enabled = True
End Sub
Private Sub tmrProcess_Timer()
'This just simulates delay involved in processing responses and
'then sending new ones.
Dim Socket As Integer
For Socket = 0 To 3
If ProcessCountdowns(Socket) > 0 Then
ProcessCountdowns(Socket) = ProcessCountdowns(Socket) - 1
If ProcessCountdowns(Socket) <= 0 Then
SendRequest Socket
End If
End If
Next
End Sub
Private Sub tmrResponse_Timer()
Dim Socket As Integer
For Socket = 0 To 3
If ResponseCountdowns(Socket) > 0 Then
ResponseCountdowns(Socket) = ResponseCountdowns(Socket) - 1
If ResponseCountdowns(Socket) <= 0 Then
Misses(Socket) = Misses(Socket) + 1
lblMisses(Socket).Caption = CStr(Misses(Socket))
SendRequest Socket
End If
End If
Next
End Sub
Running the simulation requires two control arrays: one of 4 Command buttons and one of 4 Labels. Then it becomes a game of "Whack a Mole."
Pretty routine stuff actually, and the main reason we have Timer controls in the first place.

Related

Read UART transmision Input buffer in Matlab

I'm trying to make a serial communication between two ESP8266 Wifi chips.
To start, I tried sending a sample data 10 times in a for loop. Here is the code:
Transmiter:
for Packets = 1 : 10
SendData(client,Data(Packets));
end
Receiver:
Packets = 1
while(1)
Data(Packets) = ReceiveData(Server);
Packets = Packets + 1;
if (packets == 10)
break
end
end
it works good. The problem is when I want to send data with some delays, the transmitter should connect to receiver again and the server (receiver) receives some data indicating that connection is made again.
The received Buffer should be:
+IPD,0,1024:ùüþþþýýþþÿÿûûýþýûúþÿúóýÿþþþþþýúøûýþ...
but after reconnecting the received Buffer is:
0,CLOSED %Receiver Prompt, disconnected from Transmiter
0,CONNECT %Receiver Prompt,connected to Transmiter
+IPD,0,1024:ùüþþþýýþþÿÿûûýþýûúþÿúóýÿþþþþþýúøûýþ...
The remaining part of data will be read in next packet and same for next packets.
what should I do to receive just the data?
The send and receive functions:
function ReceivedBuffer = ReceiveData(SerialPort)
ReceivedBuffer = fread(server,1038); %Size data = 1038 Bytes
end
function SendData(SerialPort,Data)
fwrite(SerialPort,Data);
end

Best way to handle multiple connections at the same time

I have an application which listens to multiple connections and verifies whether the user is active or not
I use a 1 thread socket handling method with WSAASyncSelect.
The problem is that sometimes when a lot of users connecting at the same time some users get no reply
i think it is because the "send" hasn't been called yet and the program has received another connection so it goes again to handle the new connection ignoring the previous one. Like WSAASyncSelect has triggered and now it processing a new connection instead of completing the previous request.
So what to do to fix this issue? i tried to stop the events from WSAASyncSelect temporary by calling it with zero parameters when handling the connection until finish it then re enable network events but that didn't help either.
Here are the codes that handling the events (recieve then decrypt and then compare the bytes then send data according to what in listbox ie Active user or not)
This called upon receive of FD_READ
WSAAsyncSelect s, frmMain.hwnd, 0, 0 'Disabling Notifications event
Do Until bytesRecieved = SOCKET_ERROR
bytesRecieved = recv(wParam, buffer(Bytes), 500, 0)
If bytesRecieved > 0 Then
Bytes = Bytes + bytesRecieved
ElseIf bytesRecieved = 0 Then
Exit Sub
End If
Loop
Call MemCopy(ByVal decryptedArrival, buffer(0), Bytes)
WSAAsyncSelect s, frmMain.hwnd, WINSOCKMSG, FD_CONNECT + FD_READ + FD_CLOSE + FD_ACCEPT + FD_WRITE
If frmMain.chkSaveLog.value = vbChecked Then
frmMain.txtConnectionsLog.Text = frmMain.txtConnectionsLog.Text & Now & " Receiving a connection (" & wParam & ")" & vbNewLine
AutoScroll
If frmMain.chkAutoSave.value = vbChecked Then
strCurrentLogLine = Now & " Receiving a connection (" & wParam & ")"
AutoSaveLog (strCurrentLogLine)
frmMain.cmdClearLogs.Enabled = True
End If
End If
Below here is a decryption of bytes then comparing by ID as byte identifier like 1 = check for update
2 - send user info etc
in a Select Case statement following by a send Api.
And the accepting procedure
This called upon receive of FD_ACCEPT
Function AcceptConnection(wParam As Long)
lpString = String(32, 0)
AcSock = accept(wParam, sockaddress, Len(sockaddress))
strTempIP = getascip(sockaddress.sin_addr)
frmMain.txtConnectionsLog.Text = frmMain.txtConnectionsLog.Text & Now & " Getting a connection from IP address: " & _
strTempIP & " (" & AcSock & ")" & vbNewLine
AutoScroll
If frmMain.chkAutoSave.value = vbChecked Then
strCurrentLogLine = Now & " Getting a connection from IP address: " & strTempIP & " (" & AcSock & ")" & vbNewLine
AutoSaveLog (strCurrentLogLine)
End If
End Function
Are there any suggestions for a better performance?
What you showed is NOT the correct way to use WSAAsyncSelect(). Try something more like this instead:
When creating a listening socket:
lSock = socket(...)
bind(lSock, ...)
listen(lSock, ...)
WSAAsyncSelect lSock, frmMain.hwnd, WINSOCKMSG, FD_ACCEPT
When a listening socket receives FD_ACCEPT:
Function AcceptConnection(wParam As Long)
AcSock = accept(wParam, sockaddress, Len(sockaddress))
If AcSock = INVALID_SOCKET Then
Exit Sub
End If
WSAAsyncSelect AcSock, frmMain.hwnd, WINSOCKMSG, FD_READ + FD_CLOSE + FD_WRITE
...
End Function
When an accepted client socket receives FD_READ:
Function ReadConnection(wParam As Long)
Do
bytesRecieved = recv(wParam, ReadBuffer(ReadBytes), 500, 0)
If bytesRecieved = SOCKET_ERROR Then
If WSAGetLastError() <> WSAEWOULDBLOCK Then
Exit Sub
End If
ElseIf bytesRecieved = 0 Then
Exit Sub
Else
ReadBytes = ReadBytes + bytesRecieved
End If
Loop Until bytesRecieved = SOCKET_ERROR
' process ReadBuffer up to ReadBytes number of bytes as needed...
' remove processed bytes from front of ReadBuffer and decrement ReadBytes accordingly
...
End Function
When an accepted client socket receives FD_WRITE:
Function WriteConnection(wParam As Long)
While SendBytes > 0
bytesSent = send(wParam, SendBuffer(0), SendBytes, 0)
If bytesSent = SOCKET_ERROR Then
Exit Sub
End If
' remove bytesSent number of bytes from front of SendBuffer ...
SendBytes = SendBytes - bytesSent;
End While
End Function
The trick is that you need to allocate separate ReadBuffer and SendBuffer buffers for each accepted client. Make sure that each time you receive FD_READ that you are appending bytes only to the ReadBuffer of the socket that triggered FD_READ, and each time you receive FD_WRITE that you are removing bytes only from the SendBuffer of the socket that triggered FD_WRITE.
When recv() has no more bytes to read, process that socket's ReadBuffer as needed, removing only complete messages from the front and leaving incomplete messages for later processing.
When send() fails with WSAEWOULDBLOCK, append any unsent bytes to the SendBuffer of the socket that caused send() to fail. When you receive an FD_WRITE event for a socket, check that socket's SenBuffer and resend any bytes that are in it, stopping when the buffer is exhausted or an WSAEWOULDBLOCK error occurs.
Very easy, and quite effective, way to do it is to fork out for every incoming connection. This will most likely require you to restructure your application, but the basic flow should be as follows:
1. New connection is opened to the server
2. Server accepts the connection and forks out
3. The fork closes the original socket for listening, so only the parent will be accepting new connections
4. And then your magic happens, separate from the original thread.
This way you do not have to worry about issues of concurrency, as long as your machine can handle all the traffic and load because each connections is independent.

VB6 RS232 not receiving full data from device using MSCOMM Controll

I have a clinical device that sends data on com port, I want to receive data from device
it also received First Frame (254) character after send ACK on ENQ
it receive [ETB] [CR][LF] characters
then I again send ACK for next frame, but not receive data
only receiving EOT char
Device Communication as per device is:
<-[ENQ]
->[ACK]
<-[STX]1H|**********************-[ETB]21[CR][LF]
->[ACK]
<-[STX]1H|**********************-[ETX]8E[CR][LF]
->[ACK]
<-[EOT]
my code is:
'MSComm1.Settings = "9600,n,8,1"
'MSComm1.InputLen = 1
Private Sub MSComm1_OnComm()
Dim InBuff As String
InBuff = MSComm1.Input
if Chr$(5)=InBuff then 'ENQ received
MSComm1.Output=Chr$(6) & VbCr
elseif Chr$(10)=InBuff then 'LF received
MSComm1.Output=Chr$(6) & VbCr
else
text1.text=text1.text & InBuff
end if
End Sub
Device sending full data because 1 software comes with device which receive full data as
but I didn't receive next frame after send ACK again,
if any one have idea what output have to send FOR next ACK, please advice me
thanks in advance
Do something like this...
MSComm1.InputLen = 1 ' for sending single character from device
MSComm1.RThreshold = 1 ' for firing events on receiving a single character
Dim InBuff As String
if MSComm1.CommEvent = comEvReceive then
do
InBuff = MSComm1.Input
Loop Until MSComm1.InBufferCount < 1
Firstly receive all the data and after that use that in your own way.

Moai: Graphics that reacts to commands via Sockets

I need a program that can create pre-defined shapes on screen according to that commands I send to it via TCP.
I'm trying to listen to a port and so that I can use them. Before waiting of a command (via network) I have the commands required to create a square (I plan to change its attributes via network commands)
The problem is it is not creating any graphics or opening the window as it should be..
require "socket"
require "mime"
require "ltn12"
host = "localhost"
port = "8080"
server, error = socket.bind(host, port)
if not server then print("server: " .. tostring(error)) os.exit() end
screen=MOAISim.openWindow ( "test", 640, 640 )
viewport = MOAIViewport.new (screen)
viewport:setSize ( 640, 640 )
viewport:setScale ( 640, 640 )
layer = MOAILayer2D.new ()
layer:setViewport ( viewport )
MOAISim.pushRenderPass ( layer )
function fillSquare (x,y,radius,red,green,blue)
a = red/255
b = green/255
c = blue/255
MOAIGfxDevice.setPenColor ( a, b, c) -- green
MOAIGfxDevice.setPenWidth ( 2 )
MOAIDraw.fillCircle ( x, y, radius, 4 ) -- x,y,r,steps
end
function onDraw ( )
fillSquare(0,64,64, 0,0,255)
end
scriptDeck = MOAIScriptDeck.new ()
scriptDeck:setRect ( -64, -64, 64, 64 )
scriptDeck:setDrawCallback ( onDraw)
prop = MOAIProp2D.new ()
prop:setDeck ( scriptDeck )
layer:insertProp ( prop )
while 1 do
print("server: waiting for client command...")
control = server:accept()
command, error = control:receive()
print(command,error)
error = control:send("hi from Moai\n")
end
It is waiting of the command from client at control = server:accept() but it is not opening up the graphics window as it should.. Is there any command to force it to open or render
Thank you
MOAI doesn't run your scripts in a separate thread. A blocking call (server:accept) or forever loop (while true do) will block your MOAI app and it will appear to freeze while it merrily sits in your script forever.
So you have to do two things:
Use non-blocking calls. In this case, you need to set your server's timeout to 0. That makes server:accept return immediately. Check it's return value to see if you got a connection.
Put your while loop in a coroutine and yield once per iteration.
You'll need to handle the client the same way, using non-blocking calls in a coroutine loop.
function clientProc(client)
print('client connected:', client)
client:settimeout(0) -- make client socket reads non-blocking
while true do
local command, err = client:receive('*l')
if command then
print('received command:', command)
err = client:send("hi from Moai\n")
elseif err == 'closed' then
print('client disconnected:', client)
break
elseif err ~= 'timeout' then
print('error: ', err)
break
end
coroutine.yield()
end
client:close()
end
function serverProc()
print("server: waiting for client connections...")
server:settimeout(0) -- make server:accept call non-blocking
while true do
local client = server:accept()
if client then
MOAICoroutine.new():run(clientProc, client)
end
coroutine.yield()
end
end
MOAICoroutine.new():run(serverProc)
Set the timeout for the server socket, since the accept is a blocking call.
server:settimeout(1)
Thanks Mud...I found that before u replied so the following coroutine works
function threadFunc()
local action
while 1 do
stat, control = server:accept()
--print(control,stat)
while 1 do
if stat then
command, error = stat:receive()
print("Comm: ", command, error)
if command then
stat:close()
print("server: closing connection...")
break
else
break
end
--[[
error = stat:send("hi")
if error then
stat:close()
print("server: closing connection...",error)
break
end ]] --
else
break
end
end
coroutine.yield()
end
end
That was very helpful though

Looking for mailing solution

I'm using ASP code and AspEmail component to send emails to our clinets, but I have some problmes...
I have more then 1000 email address that I need to send them an email, becuase of my SMTP provider limitation, I can't add them all as BCC in one email but I need to send each email seperatly, therefor looping on +1000 times witch takes forever and fires the server timeout error.
I need to send those emails about 20 times a day.
This is my script:
on error resume next
msg = "SOME TEXT HERE"
Set Mail = Server.CreateObject("Persits.MailSender")
Mail.Host = "SMPT.HOST.ADDRESS"
Mail.Port = 25
Mail.CharSet = "UTF-8"
Mail.ContentTransferEncoding = "Quoted-Printable"
Mail.From = "noreply#mydomain.co.il"
MailSubject = "email subject"
Mail.Subject = Mail.EncodeHeader(MailSubject, "utf-8")
Mail.Body = msg
Mail.IsHTML = True
zBcc = split(zBcc, ";") '1000 emails here
for i=0 to Ubound(zBcc)
zBcc(i) = trim(zBcc(i))
if len(zBcc(i))>0 then
if inStr(zBcc(i), " ")>0 then
else
if (Mail.ValidateAddress(zBcc(i)) = 0) then
Mail.Reset()
Mail.AddAddress zBcc(i)
Mail.Send
end if
end if
end if
next
set Mail=nothing
why dont you do it using a pagination type logic i.e.
limit by 100,
loop thru that batch,
once that has completed,
reload the page with the next offset in mind like send-email.asp?offset=100, send-email.asp?offset=200, etc.
use that offset value to get next batch
repeat process until end of recordset.
At least you have less chance of it timing out altho you can increase it: server.ScriptTimeout = 180
First of all I'd maximize the number of BCC's per cycle. Let's say you can email 50 BCC's in one go; you should: especially when you need this page about 20 times a day.
Anyway. Before you start; maximize the scripttimeout
Server.ScriptTimeout = 2147483647