vb6 - GetData from winsocket when State = closing - tcp

I am building an HTTP client in Visual Basic 6, that sends data to server with the header:
Connection: Close
Wich forces the web server to close connection right after all data has been sent. I have this code to receive the data:
Private Sub rqt_DataArrival(Index As Integer, ByVal bytesTotal As Long)
On Error GoTo errorHandler
Dim buff As String
If bytesTotal > 0 Then
rqt(Index).GetData buff
WSDataBuffer(Index) = WSDataBuffer(Index) & buff
End If
Exit Sub
errorHandler:
MsgBox "error in state: " & rqt(Index).State & ". Bytes left: " & bytesTotal
End Sub
The problem is that 1 out of 5 times or so, I get an error '40006' :
Wrong protocol or connection state for the requested transaction or request. in the line:
rqt(Index).GetData buff
So I placed that error handler to see what was going on and the problem is that the .State = 8 (closing state). But there is still data to be received, because bytesTotal is 195.
What am I doing wrong?

Related

How can I listening "controller board" with indy 10 tcpip?

I have a code in Visual Basic, that have a timer and listening a tcpip stream. I´m try to code it in deplhi, but I have problems.
I get connect sucessful in board with tcpip like this : IdTCPClient1.Connect;
Board tcpip is 192.168.0.180, port 2000, my server is 192.168.0.30.
I´m try this code :
procedure TForm1.Button8Click(Sender: TObject);
var StrStream: TMemoryStream;
begin
if IdTCPClient1.Connected then
begin
StrStream := TMemoryStream.Create;
if IdTCPClient1.IOHandler.Connected then
IdTCPClient1.IOHandler.ReadStream(StrStream,-1,false);
Memo1.Lines.Add('hello');
end;
end;
The problem is, in line IdTCPClient1.IOHandler.ReadStream(StrStream,-1,false); the application stop, no error, no message and I don´t undestand.
VB code
Private Sub Timer2_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Timer2.Tick
Try
If tcp1.Available > 1 Then
Dim leitura As NetworkStream = tcp1.GetStream
Dim bytes(tcp1.ReceiveBufferSize) As Byte
leitura.Read(bytes, 0, CInt(tcp1.ReceiveBufferSize))
returndata = Encoding.ASCII.GetString(bytes)
txtSerial1.AppendText(returndata)
End If
If tcp2.Available > 1 Then
Dim leitura As NetworkStream = tcp2.GetStream
Dim bytes(tcp2.ReceiveBufferSize) As Byte
leitura.Read(bytes, 0, CInt(tcp2.ReceiveBufferSize))
returndata = Encoding.ASCII.GetString(bytes)
txtSerial2.AppendText(returndata)
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
When calling the IOHandler's ReadStream() method, setting AByteCount=-1 and AReadUntilDisconnect=False as you are tells ReadStream() to expect the incoming data to be preceded by a multi-byte integer (either 4 or 8 bytes, depending on the IOHandler's LargeStream property) that specifies the number of subsequent bytes in the data, and it then waits for that many bytes to actually arrive. Is that what the server's data actually looks like? I doubt it, because that is not what your VB code is expecting.
Using the IOHandler's ReadBytes() method with AByteCount=-1 would be a closer equivalent to your VB code than using the ReadStream() method, eg:
procedure TForm1.Button8Click(Sender: TObject);
var
Bytes: TIdBytes;
begin
if IdTCPClient1.Connected then
begin
IdTCPClient1.IOHandler.ReadBytes(Bytes, -1);
// use Bytes as needed...
Memo1.Lines.Add('hello');
end;
end;
Also, keep in mind that Indy uses blocking socket I/O by default. You really shouldn't perform blocking operations in the main UI thread. You should move your reading logic to a separate worker thread instead. Or, at the very least, make sure you set a small ReadTimeout on the TIdTCPClient so ReadBytes() does not block the UI for very long.

Get result from chilkatHttp.PostUrlEncodedAsync in VB6

I am using chilkat Http.PostUrlEncodedAsync to send some data to a server and get a JSON response. When the Http_TaskCompleted Event is fired, the task.ResultType returns "object" but there is no task.GetResultObject.
The reply is received correctly (it is present in httpSessionLog.txt) but how do I get it ?
After a different search I have found it:
Private Sub Http_TaskCompleted(ByVal task As Chilkat_v9_5_0.IChilkatTask)
Dim response As New ChilkatHttpResponse
Dim success As Long
success = response.LoadTaskResult(task)
If (success <> 1) Then
Debug.Print response.LastErrorText
Exit Sub
End If
Debug.Print response.BodyStr 'this is the response string
End Sub

Problems With Paypal Express Checkout Integration (WEBREQUEST)

So I was struggling with making head or tail out of the PayPal documentation and always felt that something was not right with my Webrequest.
So I stripped all the code back to basic and simply submitted the request via HTTP and the PLUS side is that I now get a response back from the PayPal sandbox server where ACK=Success and TOKEN=Valid-token-value-here there are some other variables returned too, such as CORRELATIONID and TIMESTAMP.
And hence so I tried some of the webrequest samples and I simply get a blank screen instead of being redirected to Paypal for the (sandbox) customer to complete payment.
So if anyone can post their WebRequest method that would be great.
Here is the code I used for my webrequest, I'm sure its wrong but cannot pinpoint where it is going wrong.
Also, when I run the code on my localhost during debugging, everything works fine and the call is completed with SUCCESS and a TOKEN is received.
When I run it live, I recieve Error Number 5 in the Error exception and also the text `Remote host failed to connect' in the STATUS DESCRIPTION.
THIS IS THE UPDATED CODE
Function MakeWebRequest(ByVal pUseSandbox As Boolean, ByVal pRequestMethod As String, ByVal pReturnUrl As String, ByVal pCancelUrl As String, ByRef pRtnStatus As String, ByRef pRtnStatusId As HttpStatusCode, ByRef pRtnResponseString As String) As Boolean
'
Dim _sxHost As String = Nothing
Dim _sxEndpoint As String = Nothing
Dim _sxNameValCol As System.Collections.Specialized.NameValueCollection = Nothing
Dim _sxResponseCol As System.Collections.Specialized.NameValueCollection = Nothing
Dim _sxCounta As Integer = Nothing
Dim _sxParamsString As String = Nothing
'
'-> Init
_sxParamsString = ""
MakeWebRequest = False
_sxNameValCol = New System.Collections.Specialized.NameValueCollection()
_sxResponseCol = New System.Collections.Specialized.NameValueCollection()
If pUseSandbox Then
_sxHost = "http://www.sandbox.paypal.com"
_sxEndpoint = "https://api-3t.sandbox.paypal.com/nvp"
Else
_sxHost = "http://www.paypal.com"
_sxEndpoint = "https://api-3t.paypal.com/nvp"
End If
'-> Create Request
Try
'-> Key/Value Collection Params
_sxNameValCol.Add("METHOD", "SetExpressCheckout")
_sxNameValCol.Add("USER", _UserName)
_sxNameValCol.Add("PWD", _Password)
_sxNameValCol.Add("SIGNATURE", _Signature)
_sxNameValCol.Add("PAYMENTREQUEST_0_AMT", Format(_Basket.BasketTotalIncDelivery / 100, "0.00"))
_sxNameValCol.Add("PAYMENTREQUEST_0_PAYMENTACTION", "Sale")
_sxNameValCol.Add("PAYMENTREQUEST_0_CURRENCYCODE", "GBP")
_sxNameValCol.Add("RETURNURL", pReturnUrl)
_sxNameValCol.Add("CANCELURL", pCancelUrl)
_sxNameValCol.Add("REQCONFIRMSHIPPING", "0")
_sxNameValCol.Add("NOSHIPPING", "2")
_sxNameValCol.Add("LOCALECODE", "EN")
_sxNameValCol.Add("BUTTONSOURCE", "PP-ECWizard")
_sxNameValCol.Add("VERSION", "93.0")
'-> UrlEncode
For _sxCounta = 0 To _sxNameValCol.Count - 1
If _sxCounta = 0 Then
_sxParamsString = _sxParamsString & _sxNameValCol.Keys(_sxCounta) & "=" & HttpUtility.UrlEncode(_sxNameValCol(_sxCounta))
Else
_sxParamsString = _sxParamsString & "&" & _sxNameValCol.Keys(_sxCounta) & "=" & HttpUtility.UrlEncode(_sxNameValCol(_sxCounta))
End If
Next
'-> Credentials (not used)
'_sxRequest.Credentials = CredentialCache.DefaultCredentials
Try
Dim _sxRequest As WebRequest = DirectCast(System.Net.WebRequest.Create(_sxEndpoint), System.Net.HttpWebRequest)
'-> Convert request to byte-array
Dim _sxByteArray As Byte() = Encoding.UTF8.GetBytes(_sxParamsString)
_sxRequest.Method = "POST" 'Our method is post, otherwise the buffer (_sxParamsString) would be useless
_sxRequest.ContentType = "application/x-www-form-urlencoded" 'We use form contentType, for the postvars
_sxRequest.ContentLength = _sxByteArray.Length 'The length of the buffer (postvars) is used as contentlength
Dim _sxPostDataStream As System.IO.Stream = _sxRequest.GetRequestStream() 'We open a stream for writing the postvars
_sxPostDataStream.Write(_sxByteArray, 0, _sxByteArray.Length) 'Now we write, and afterwards, we close
_sxPostDataStream.Close() 'Closing is always important!
'-> Create Response
Dim _sxResponse As HttpWebResponse = DirectCast(_sxRequest.GetResponse(), HttpWebResponse)
'-> Get Response Status
pRtnStatus = _sxResponse.StatusDescription
pRtnStatusId = _sxResponse.StatusCode
'-> Reponse Stream
Dim _sxResponseStream As Stream = _sxResponse.GetResponseStream() 'Open a stream to the response
'-> Response Stream Reader
Dim _sxStreamReader As New StreamReader(_sxResponseStream) 'Open as reader for the stream
pRtnResponseString = _sxStreamReader.ReadToEnd() 'Read the response string
MakeWebRequest = True
'-> Tidy up
_sxStreamReader.Close()
_sxResponseStream.Close()
_sxResponse.Close()
_sxByteArray = Nothing
_sxPostDataStream = Nothing
_sxRequest = Nothing
_sxResponse = Nothing
_sxResponseStream = Nothing
_sxStreamReader = Nothing
Catch ex As Exception
pRtnStatusId = Err.Number
pRtnStatus = "response(" & ex.Message & ")"
Decode(pRtnResponseString, _sxResponseCol)
pRtnResponseString = Stringify(_sxResponseCol)
End Try
Catch ex As Exception
pRtnStatusId = Err.Number
pRtnStatus = "request(" & ex.Message & ")"
Decode(pRtnResponseString, _sxResponseCol)
pRtnResponseString = Stringify(_sxResponseCol)
End Try
'-> Tidy Up
_sxHost = Nothing
_sxEndpoint = Nothing
_sxNameValCol = Nothing
_sxResponseCol = Nothing
_sxCounta = Nothing
_sxParamsString = Nothing
'
End Function
OK, so it's now clear that you're not getting any response from the server because your server isn't able to connect to PayPal's servers at all. Hence, you got no server-response and the message Unable to connect to the remote server. When I tested, I got a HTTP 200 response with the following body:
TIMESTAMP=2015-07-07T09:07:39Z&CORRELATIONID=7f4d2313c9696&ACK=Failure&VERSION=93.0&BUILD=17276661&L_ERRORCODE0=10002&L_SHORTMESSAGE0=Authentication/Authorization Failed&L_LONGMESSAGE0=You do not have permissions to make this API call&L_SEVERITYCODE0=Error
Obviously that's because I tested with a blank username and password.
So, something is wrong with your server setup that's preventing you from making outside connections, either at the IIS level or due to your firewall configuration.
Without physically being present at your machine, there's not a lot we can do to track down what's blocking it, but you can try opening HTTP requests to other public websites like Google.com and see if those succeed.

Classic asp - response.redirect in a sub - error handling

I wish to do the following ... Provide a page a developer can redirect to provided an error occurs ... like a vb error connection couldn't open or object couldn't be found ... or a database error is raised ... but since I moved the redirect into a sub the page doesn't actually redirect ... Is it possible that I simply can't redirect from a sub? Seems weird.
Run a stored procedure that raises an error
Dim cmd
Set cmd = Server.CreateObject("ADODB.Command")
cmd.ActiveConnection = con
cmd.CommandType = adCmdStoredProc
cmd.CommandText = "spReturnDBException"
cmd.Execute
Call a handle errors function that sets up some session parms and redirects if neccessary
HandleErrors con _
, Request.ServerVariables("PATH_INFO") _
, "An error occurred while trying to save sessions." _
, "Actual Error: " + Err.Description + " EmpNo: " + Session("EmpNo") _
+ ". QueryString: " + Request.Querystring _
, 0
This would be the sub called.
sub HandleErrors( connection, WebPagePath, GenericErrorMessage, DebugInfo, Severity)
//Check for vb errors
if Err.Number <> 0 Then
Session("WebPagePath") = WebPagePath
Session("SafeErrorMessage") = GenericErrorMessage 'Session("SafeErrorMessage") + "A connection was dropped while trying to complete sessions."
Session("DebugInfo") = DebugInfo ' Err.Description
Session("LineNo") = Err.Line
Session("StackTrace") = ""
Session("Severity") = Severity
response.redirect("Error.asp")
//error occurs
elseif connection.Errors.count <> 0 then
response.write("a database error occurred.")
// Store safe error message / # in session
Session("WebPagePath") = WebPagePath
Session("SafeErrorMessage") = GenericErrorMessage 'Session("SafeErrorMessage") + "An error has occurred while trying to save sessions."
Session("DebugInfo") = DebugInfo '"Some extra added debug info from the webpage"
Session("LineNo") = 0
Session("StackTrace") = ""
Session("Severity") = Severity
Dim objError
for each objError in connection.Errors
// Store safe error number in session
Session("SafeErrorNumbers") = Session("SafeErrorNumbers") + objError.Description
if connection.Errors.Count > 1 then
Session("SafeErrorNumbers") = Session("SafeErrorNumbers") + "|"
end if
next
response.Redirect("Error.asp")
end if
Err.Clear
end sub
To display the Error line number:
set objError = Server.GetLastError()
strErrorLine = objError.Line
Here are a couple threads on using Err.line:
http://www.daniweb.com/web-development/asp/threads/11615
http://www.sitepoint.com/forums/showthread.php?279612-ASP-Error-Handling.-Err.Line-weird-behavior
I can't explain why you are getting the results you are. I can tell you that if your code reachs a line that contains Response.Redirect that redirect will happen regardless of whether its a in a Sub procdure or not.
I would make this suggestion. Stop using using On Error Resume Next. It is very painful way to deal with exceptions.
Instead change HandleErrors into GenerateConnectionError. Its job would be to compose error source and description strings and deliberately call Err.Raise with a user define error number (I tend to use 1001).
Now your Error.asp should be installed in the root of your application as the handler for the 500.100 http status code. When a script error occurs IIS will look to the current error pages collection for the location to determine what to do. You will have set this to execute a URL and specified your Error.asp as the URL.
When Error.asp is executed it will find details about the page being requested from QueryString. Here you can also use Server.GetLastError to get an ASPError object from which you can get other details about the error.
Using this approach will detail with any script error without requiring the developer to remember to pepper their code with HandleError code. The developer need on remember to call GenerateConnectionError when performing code that is using an ADODB.Connection and even then you could probably abstract that inside an .asp include file.

ASP.NET HttpWebRequest stop sending requests, high CPU usage

I have an old ASP.NET application. We send out httpWebRequest to a remote REST server and retrieve XML back, most of the time the application works fine. Recently, we got some high CPU usage issue several times a day.
During the high CPU usage time, we monitored those httpWebRequest connections (by checking netstat for the w3wp process). At the very beginning, the connections change to "CLOSE_WAIT" status from "ESTABLISHED", then after those connections timeout, those connections disappeared one by one, and then there is no connection any more.
After resetting the IIS, when the w3wp.exe process start again, we still could not find any connections to httpWebRequest target server. So the CPU usage keep staying at high level. Even after several round of reset, it won't solve the issue, until we saw some connections start to connect to httpWebRequest target server, the CPU usage went down.
I actually thought it could be the issue of my code not handling the httpWebRequest properly, I posted another question here: How to close underlying connections after catch httpwebrequest timeout?.
As mentioned in that question, I also found lots of timeout exceptions for System.Net.HttpWebRequest.GetResponse(). We found 3500 of the same exception within 5 minutes when CPU usage is really high.
What could cuase this type of issue and what could be the medicine? Why won't the application send out request any more (since there is no connection in netstat)?
Here is the source code just in case:
System.Net.HttpWebResponse httpWebResponse = null;
System.IO.Stream stream = null;
XmlTextReader xmlTextReader = null;
try
{
System.Net.HttpWebRequest httpWebRequest = (System.Net.HttpWebRequest)System.Net.WebRequest.Create(request);
httpWebRequest.ReadWriteTimeout = 10000;
httpWebRequest.Timeout = 10000;
httpWebRequest.KeepAlive = false;
httpWebRequest.Method = "GET";
httpWebResponse = (System.Net.HttpWebResponse)httpWebRequest.GetResponse();
stream = httpWebResponse.GetResponseStream();
xmlTextReader = new XmlTextReader(stream);
xmlTextReader.Read();
xmlDocument.Load(xmlTextReader);
//Document processing code.
//...
}
catch
{
//Catch blcok with error handle
}
finally
{
if (xmlTextReader != null)
xmlTextReader.Close();
if (httpWebResponse != null)
httpWebResponse.Close();
if (stream != null)
stream.Close();
}
From your description, it is not clear to me that your high CPU utilization is related to your outgoing HTTP requests. High CPU utilization could be due to a bug in your code, a bug in CLR, IIS, or something else. Without knowing which component is consuming the CPU, you wont be able to do anything further.
If I were you, I would first try to attach a sampling profiler to the W3WP process, and see which component is consuming the CPU. That should point you to the next steps in resolving this issue.
I would suggest you to try sending requests asynchronously to avoid blocking the main thread:
using (var client = new WebClient())
{
client.OpenReadCompleted += (sender, e) =>
{
using (var reader = XmlReader.Create(e.Result))
{
// Process the XML document here
}
};
client.OpenReadAsync(new Uri("http://www.example.com"));
}
Finding the reason for the high CPU utilization can take some time since you will have to locate the code that is causing the problem. I am working through this right now on a vb.net app that I recently developed. In the meantime, I have developed a page that has a button which an Administrative user can click to stop the W3WP.exe process. It is a great stop gap measure until the problem code can be identified and updated. Here is the code I used. Just create a .aspx page with a button that call the following code on the corresponding .aspx.vb page. The code uses the command prompt to get the Tasklist and writes this to a file. I then parse the text file for the PID of the W3WP.exe worker process. Then, I access the command prompt programmatically again to terminate the W3WP.exe process using the PID.
Imports System.Web
Partial Class TAP
Inherits System.Web.UI.Page
Protected Sub btnStop_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnStop.Click
Call thStop_IIS()
End Sub
Function thStop_IIS()
Dim varRetval As String
Dim varPID As String
dim x as long
Dim savePath As String = Request.PhysicalApplicationPath + "exports\"
If Dir(savePath + "filename.txt") = "filename.txt" Then
Kill(savePath + "filename.txt")
End If
varRetval = Shell("cmd /c tasklist > " + savePath + "filename.txt")
For x = 1 To 90000000
Next x
varPID = thParse_File_Return_PID(savePath + "filename.txt")
varRetval = Shell("cmd /c taskkill /pid " & varPID & " /F")
Return True
End Function
Function thParse_File_Return_PID(ByVal varFileToParse As String) As Integer
On Error GoTo handler_err
'Dim FILE_NAME As String = "C:\Users\Owner\Documents\test.txt"
Dim FILE_NAME As String = varFileToParse
Dim TextLine As String
Dim varPID As Integer
Dim x As Long
If System.IO.File.Exists(FILE_NAME) = True Then
Dim objReader As New System.IO.StreamReader(FILE_NAME)
Do While objReader.Peek() <> -1
'TextLine = TextLine & objReader.ReadLine() & vbNewLine
TextLine = objReader.ReadLine() & vbNewLine
If InStr(TextLine, "w3wp.exe") Then
varPID = Mid(TextLine, 31, 4)
End If
Loop
thParse_File_Return_PID = varPID
Else
thParse_File_Return_PID = 0
End If
handler_exit:
Exit Function
handler_err:
'MsgBox(Err.Number & " " & Err.Description & ":" & "thParse_File_Return_Pages")
Resume
End Function
End Class

Resources