Android Question Winsocket problems...

mbatgr

Active Member
Licensed User
Longtime User
I have the following code. This is a working code I used to send one digit number to the server (VB6) except that times the is a delay and the server received a message with more digits instead of 1 (i send a digit when i press a particular button, so pressing in a row eg 3 buttons, I expect the server to receive 3 messages with 1 digit length. But there are times I am receiving a message with 3 digits! is there any references regarding known bugs of Vb6 + MsWinsck.ocx ?

Thanking the colleges in advance :)
B4X:
Sub btnConnect_Click
       Select Case btnConnect.Text
           Case "Connect"
                btnConnect.Text = "Disconnect"
                Socket1.Initialize("Socket1")
                Socket1.Connect("xxx.xxx.x.xxx",9000,15000)
           Case "Disconnect"
                 btnConnect.Text = "Connect"
                 Socket1.Close
           Case Else
      End Select
End Sub

Sub Socket1_Connected(Connected As Boolean)As Boolean
     If Connected = True Then
         ToastMessageShow("Connected",True)
         AStreams.Initialize(Socket1.InputStream,Socket1.OutputStream,"Astreams")
         SendData("+") 'send + as a sign of succesfull link
    Else
         ToastMessageShow("Server not available",True)
         btnConnect.Text = "Connect"
    End If
    btnConnect.Enabled = True
End Sub

Sub AStreams_NewData (Buffer() As Byte)
     Dim msg As String
     msg = BytesToString(Buffer, 0, Buffer.Length, "Windows-1252")
     log(msg)
End Sub

Sub SendData(msg As String)
     Dim Buffer() As Byte
     Buffer = Msg.GetBytes("Windows-1252")
     AStreams.Write(Buffer)
End Sub

Sub Buttons_Click
    Dim btn As Button
    Dim msg As String
    Dim b As Beeper

    btn = Sender
    b.Beep
    msg = btn.Tag

    SendData(msg)

End Sub
  • Also while the code above is working, sometimes when the program runs for the very first time I got a crash. The reference line where the eroor happens is the "AStreams.Initialize(Socket1.InputStream,Socket1.OutputStream,"Astreams")"


    LogCat connected to: 07961702
    --------- beginning of /dev/log/system
    --------- beginning of /dev/log/main
    ** Activity (main) Create, isFirst = true **
    1920 x 1104, scale = 2.0 (320 dpi)
    ** Activity (main) Resume **
    ** Activity (main) Pause, UserClosed = false **
    sending message to waiting queue (socket1_connected)
    running waiting messages (1)
    Error occurred on line: 123 (main)
    main$ResumeMessagerun (java line: 252)


    java.lang.NullPointerException
    at anywheresoftware.b4a.objects.SocketWrapper.getInputStream(SocketWrapper.java:133)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:525)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:485)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:229)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:174)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:525)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:93)
    at anywheresoftware.b4a.BA$3.run(BA.java:312)
    at anywheresoftware.b4a.BA.setActivityPaused(BA.java:382)
 
Last edited by a moderator:

Erel

B4X founder
Staff member
Licensed User
Longtime User
You cannot assume that messages you send over the network will be "packaged" exactly as you sent them. A message can be split or multiple messages can be combined to a single message. It depends on internal buffers which you cannot control.

You can read about it in the AsyncStreams tutorial. This is exactly the reason for "prefix" mode. In prefix mode each message starts with 4 bytes that specify the message length. AsyncStreams uses this header to manage the messages. However you will not be able to use this mode without implementing it in the VB6 code.

The short answer is that you should expect messages to be combined and you should take care of it in your code.

About the second error. It seems like it happens when the activity is resumed after it was paused. The event is then raised but the socket is no longer valid. The solution is to move the code to a service so it will not be paused.
 
Upvote 0

mbatgr

Active Member
Licensed User
Longtime User
thank you for your reply. However, it seems that most of the time the code works ok, this happens all the time with the same messages which have no any obvious differences with the other ones that no problem happens... this is the strange thing :)
Is there around a sample little code how to implement the "prefix" mode in VB6? Or is better to use the ASyncStreamsText...generally I use the tablet to send key codes (numbers and characters) to the server in order to control the flow of a desktop program i have made.

I have observe that the same happens (certain multiple messages are combined to a single message without any particular reason) when the server send messages (happens all the time with the same messages which have no any obvious differences with the other ones that no problem happens) to the tablet client,too.
 
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
Hi

when you send some data to the vb6 server(we do this currently) you should send and End of Transmission character(EOT),asc 4, also. When server process the receive buffer and finds EOT char it sould send an ACK back to the app. The app then know to send the next block of data.

Regards

John.
 
Upvote 0

mbatgr

Active Member
Licensed User
Longtime User
Thank you so much about your quick and simple (!) responce
So you mean the the solution to my problem is to add the code "& chr$(4)" to the end of each message to be tranmitted on B4A client and VB6 server application?
mike

ps. John, as I am novice in programming server-client application, I would like to ask you, a maybe silly question.
My desktop server application uses an ms access database. It works fine (except the issues we were discussing with ack matter) with one client user connected.
Is there any modifications I should do on the server app in order this to work with more that one client?...i guess that no, but not sure about it...
thanking you again in advance
mike
 
Last edited:
Upvote 0

Jmu5667

Well-Known Member
Licensed User
Longtime User
Yes, you can make the winsock control an array. In the vb6 ide set the index value of this winsock control to 0. You listen on 0. Here's some code from a WiFi Data receiver server.

B4X:
Private Sub ws_public_Close(index As Integer)
   
  Dim mRemoteIP As String
   
  mRemoteIP = ws_public(index).RemoteHostIP
  ws_public(index).Tag = Now
  ws_public(index).Close
  Call writelog("connection " & index & ", " & mRemoteIP & " closed by remote user," & ws_public(index).state)
  writelog ""
   

End Sub

Private Sub ws_public_ConnectionRequest(index As Integer, ByVal requestID As Long)



  Dim x As Integer, free_socket As Integer

  On Error GoTo jmp99

  free_socket = -1
   
  If index = 0 Then
   
  If ws_public.UBound = 0 Then
  GoTo jmp20
  End If
   
   
  ' // find a free control
  For x = 1 To ws_public.UBound
  If ws_public(x).state <> sckClosed Then
  writelog "socket " & x & " " & sckstatus(ws_public(x).state) & ", IP  " & ws_public(x).RemoteHostIP
  Else
  writelog "socket " & x & " " & sckstatus(ws_public(x).state)
  End If
  ' // kill the socket, it has been open for too long with no data
  If ws_public(x).state = sckConnected Then
  If Abs(DateDiff("n", Now, CDate(ws_public(x).Tag))) >= 2 Then
  writelog "killing socket " & x & " " & ws_public(x).Tag & ", " & Now & ", open for too long without receiving data."
  ws_public(x).Close
  ws_public(x).Tag = ""
  End If
  End If
   
  If ws_public(x).state = sckClosed Then
  GoTo jmp00
  Else
  GoTo jmp10
  End If
jmp00:  ' // got a free connection
  free_socket = x
   
  Exit For
jmp10:
   
  Next x
   
jmp20:
  If free_socket > 0 Then
  ' // process the new connection
  'ws_public(free_socket).Close
  ws_public(free_socket).LocalPort = 0
  ws_public(free_socket).Accept requestID
  ws_public(free_socket).Tag = Now
  Call writelog("session " & requestID & " granted access on  " & free_socket & " - " & ws_public(free_socket).RemoteHostIP)
   
  Else
  free_socket = ws_public.UBound + 1
  ' // new socket
  Load ws_public(free_socket)
  GoTo jmp20
  End If
   
  End If
   
  If free_socket > UBound(appSettings.UCONN) Then
  ReDim Preserve appSettings.UCONN(free_socket)
  End If
   
  ' // init buffer
  appSettings.UCONN(free_socket).buffer = ""
  On Error GoTo 0
  Exit Sub

jmp99:
   
  If err.Number <> 0 Then
  writelog "ws_public_ConnectionRequest() error - " & err.Description & ", " & err.Number
  End If


End Sub

Private Sub ws_public_DataArrival(index As Integer, ByVal bytesTotal As Long)

  Dim bdata As String, x As Integer

jmp00:
  'ws_public(Index).GetData bdata, , bytesTotal
  ws_public(index).PeekData bdata, , bytesTotal
  ws_public(index).Tag = Now
  ' // look for termintor, a full packet
  x = InStr(1, bdata, PKT_TERM)
  If x > 0 Then
  ws_public(index).GetData bdata, , x + (Len(PKT_TERM) - 1)
  writelog "DATA=>" & bdata & "<= " & ws_public(index).RemoteHostIP & ":" & ws_public(index).RemotePort
  appSettings.public_buffer = appSettings.public_buffer & bdata
  ws_public(index).SendData PKT_ACK
  GoTo jmp00
  End If

End Sub

Private Sub ws_public_Error(index As Integer, ByVal Number As Integer, Description As String, ByVal Scode As Long, ByVal Source As String, ByVal HelpFile As String, ByVal HelpContext As Long, CancelDisplay As Boolean)

  ws_public(index).Close
  writelog "ws_public_Error() " & Number & "," & Description

End Sub

Private Sub ws_public_SendComplete(index As Integer)

  writelog (index & ", send complete")
   
End Sub

There are many different ways to hadle the data that is received. You can explore these once you get your head around handling multiple connenctions, but really it's the same as doing 1, the index tells you which connection is being managed.


Hope this gets you started.

:)
 
Upvote 0

mbatgr

Active Member
Licensed User
Longtime User
I think that is even simpler than that. I just add an "EOT" at the end of each message and then in new DataArrival/AStreams_NewData, I use that character to separate the messages in case that there are send multiple messages instead of one by one. No SOT, no ACK!

But because I am not familiar with TCP/IP working, the only case that i have no taken in account is in the case transmitting broken messages... How winsocket manage this case? the data remains at the buffer waiting to receive the remaining message (with EOT character) or the data vanished and I should take care by code, that is to declare a Global variable and then to check for EOT and if not, then to add the new buffer data to that Global variable and when an EOT exists to manipulate the message and reset the Global variable?
 
Upvote 0
Top