How can I receive many strings close to each other? - vb.net

I'm making a VB.NET application that requires a very fast sending/receiving of strings ("commands") but have this little problem: when I send 2 or more consecutive string very quickly with the client, the server receives only a single string containing all the previous strings.
This is my sending Sub (Client):
Public Sub send(ByVal s As String)
Dim temp() As Byte = UTF8.GetBytes(s)
Try
stream.Write(temp, 0, temp.Length)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
This is my receiving Sub (Server, using a thread):
Public Sub listen()
Do
If client.Available > 0
Dim temp(client.Available - 1) As Byte
stream.Read(temp, 0, temp.Length)
Dim text As String = UTF8.GetString(temp)
'some checks on the string (example = if text.StartWith("something") then...)
End If
Loop
End Sub
Here's an example of what happens:
Client:
send("1"),
send("2"),
send("hello"),
send("18").
String received by Server: "12hello18".
How can I solve this?
Thanks.

You are assuming that Read reads a "message" at a time. TCP is not message-based. It offers you a boundaryless stream of bytes. Look into message framing. Often, this problem is solved by prepending the length of each message as an int before sending the message itself.

Related

Cross thread from a Class module

I have a class module that uses a named pipe as a listener. It's thread is started from the UI. When it receives data I want to return the data to the UI for processing.
I've tried several examples of Invoke but none of them work. The last error message was that the UI Window's handle needed to be assigned.
The following is in the frmUDPTest:
Delegate Sub ReturnDelegate(ByVal text As String)
Public doReturn As ReturnDelegate
Public Sub DisplayReturn(ByVal ServerResponse As String)
VBL_RETURN.Text = "Server Return: " & ServerResponse
End Sub
The following is the NamedPipe receiver in a Class named ClientPipe:
Public Shared Sub Receive()
'This is on a separate thread
'This is the receiver for the Local UDPServer's return to
'the requesting client
'The pipe's name is passed to the Local UDPserver in the request
'This routine is on a seperate thread and is started prior to any
'communications to the local UDPserver.
Dim RequestBytes(64) As Byte
Dim RequestByteCount As Integer = 0
Dim Response As String
Try
frmUDPTEST.doReturn = New frmUDPTEST.ReturnDelegate(AddressOf frmUDPTEST.DisplayReturn)
ClientReceive = New NamedPipeServerStream(ClientPipeName, PipeDirection.In, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous)
While True
ClientReceive.WaitForConnection()
RequestByteCount = ClientReceive.Read(RequestBytes, 0, ByteSize)
If RequestByteCount > 0 Then
Response = Encoding.ASCII.GetString(RequestBytes) 'Convert bytes back to string
'>>>>> Problem occurs here <<<<<
frmUDPTEST.Invoke(frmUDPTEST.doReturn, New Object() {Response})
End If
ClientReceive.Disconnect()
End While
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Need the returned data from the named pipe processed by UI routines to be passed on to the client that made the request.

visual basic .NET get user input without pressing enter

I know of the existence of the code character = System.Console.ReadKey().tostring.
This will read one character.
Also in another stack overflow post I found:
Public Shared Function ReadPipedInfo() As StreamReader
'call with a default value of 5 milliseconds
Return ReadPipedInfo(5000)
End Function
Public Shared Function ReadPipedInfo(waitTimeInMilliseconds As Integer) As StreamReader
'allocate the class we're going to callback to
Dim callbackClass As New ReadPipedInfoCallback()
'to indicate read complete or timeout
Dim readCompleteEvent As New AutoResetEvent(False)
'open the StdIn so that we can read against it asynchronously
Dim stdIn As Stream = Console.OpenStandardInput()
'allocate a one-byte buffer, we're going to read off the stream one byte at a time
Dim singleByteBuffer As Byte() = New Byte(0) {}
'allocate a list of an arbitary size to store the read bytes
Dim byteStorage As New List(Of Byte)(4096)
Dim asyncRead As IAsyncResult = Nothing
Dim readLength As Integer = 0
'the bytes we have successfully read
Do
'perform the read and wait until it finishes, unless it's already finished
asyncRead = stdIn.BeginRead(singleByteBuffer, 0, singleByteBuffer.Length, New AsyncCallback(AddressOf callbackClass.ReadCallback), readCompleteEvent)
If Not asyncRead.CompletedSynchronously Then
readCompleteEvent.WaitOne(waitTimeInMilliseconds)
End If
'end the async call, one way or another
'if our read succeeded we store the byte we read
If asyncRead.IsCompleted Then
readLength = stdIn.EndRead(asyncRead)
'If readLength > 0 Then
byteStorage.Add(singleByteBuffer(0))
'End If
End If
Loop While asyncRead.IsCompleted AndAlso readLength > 0
'we keep reading until we fail or read nothing
'return results, if we read zero bytes the buffer will return empty
Return New StreamReader(New MemoryStream(byteStorage.ToArray(), 0, byteStorage.Count))
End Function
Private Class ReadPipedInfoCallback
Public Sub ReadCallback(asyncResult As IAsyncResult)
'pull the user-defined variable and strobe the event, the read finished successfully
Dim readCompleteEvent As AutoResetEvent = TryCast(asyncResult.AsyncState, AutoResetEvent)
readCompleteEvent.[Set]()
End Sub
End Class
which reads input if the user pressed enter
how could I make some code that reads (multiple) character(s) without letting the user press enter all the time? But instead use time as the indicator to stop reading the console?
You can use System.Console.Read() here. It reads as character from standard input stream. https://msdn.microsoft.com/en-us/library/system.console.read(v=vs.110).aspx
According to here I found out how to deal with it:
Public Module ConsoleHelper
Sub Main()
msgbox(ReadKeyWithTimeOut(10000).key.ToString)
End Sub
Public Function ReadKeyWithTimeOut(timeOutMS As Integer) As ConsoleKeyInfo
Dim timeoutvalue As DateTime = DateTime.Now.AddMilliseconds(timeOutMS)
While DateTime.Now < timeoutvalue
If Console.KeyAvailable Then
Dim cki As ConsoleKeyInfo = Console.ReadKey()
Return cki
Else
System.Threading.Thread.Sleep(100)
End If
End While
Return New ConsoleKeyInfo(" "C, ConsoleKey.Spacebar, False, False, False)
End Function
End Module
Now just finding out how to give attribute key NULL or something if it timed out.

Sending multiple hex in array of bytes instead

I have problem about socket to send multiple hex in one send to socket
Here the detail :
Private Sub sendACK()
Dim msgACK As String
Dim sendBytes As Byte()
tmpStr = ""
list.Clear()
msgACK = "33CC"
For j = 1 To Len(msgACK)
list.Add(Mid(msgACK, j, 2))
j += 1
Next
For Each tmpStr In list
sendBytes = HexToBytes(tmpStr)
clientSocket.BeginSend(sendBytes, 0, sendBytes.Length, SocketFlags.None, New System.AsyncCallback(AddressOf OnSend), clientSocket)
Next
End Sub
Public Function HexToBytes(ByVal s As String) As Byte()
Dim bytes As String() = s.Split(" "c)
Dim retval(bytes.Length - 1) As Byte
For ix As Integer = 0 To bytes.Length - 1
retval(ix) = Byte.Parse(bytes(ix), System.Globalization.NumberStyles.HexNumber)
Next
Return retval
End Function
Private Sub OnSend(ByVal ar As IAsyncResult)
clientSocket = ar.AsyncState
clientSocket.EndSend(ar)
Thread.Sleep(100)
End Sub
==> List as arraylist
That code will be result :
Socket send 33
end send
Socket send CC
end send
============================
The program should be sending in one time like this :
Socket send 33 CC
end send
============================
is there any idea about convert string "33CC" into byte and then that program just sending 1 time in outter "for each" ?
thanks for reading and answering....
GBU
Your assumption is incorrect. It does indeed send both bytes at once. You are only calling BeginSend once and you are giving it both bytes, so if it is receiving them at all on the other end, then it is indeed sending them. In fact, there is no difference at all between sending them individually or sending them together. Since the socket works as a stream, the length of time between each byte being sent is largely irrelevant. There will be no way on the receiving end to know whether the two bytes were sent together or separately. All the receiving end will know is that the two bytes were sent in that order. If you think they are being sent as two separate "sends" on the receiving end, it sounds like you are making some invalid assumptions about how to read the data from the socket.
However, I should mention that the way you are sending the bytes, by first creating a hex string and then parsing it, is a bit silly. If you need to parse it as a string, because you are reading the hex values from a text file, or something, that's fine, but otherwise, you should just use hex byte literals in your code rather than strings, for instance:
Dim sendBytes As Byte() = {&H33, &HCC}

BeginReceive trailing Null character/chr(0)

I'm coding an ascynchronous socket client for transferring files (following this Microsoft article) and notice that using BeginReceive corrupts the transfer because it adds a single Null character/chr(0) at the end of each packet. What could be causing this issue? I thought it might be the sending side, but I tested it with SendFile and had the same result.
In the Microsoft article it converts the bytes to an ASCII string and appends it to a StringBuilder. I want to save the bytes on-the-fly, so I barely modified the ReceiveCallback like so:
Private Shared Sub ReceiveCallback(ByVal ar As IAsyncResult)
Dim state As StateObject = CType(ar.AsyncState, StateObject)
Dim client As Socket = state.workSocket
Dim bytesRead As Integer = client.EndReceive(ar)
If bytesRead > 0 Then
FileIO.FileSystem.WriteAllBytes(Application.StartupPath & "\test.exe", state.buffer, True)
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, New AsyncCallback(AddressOf ReceiveCallback), state)
Else
receiveDone.Set()
End If
End Sub
The problem is a misconception on how Receive, or BeginReceive & EndReceive work.
When you call Receive and give it a buffer and a size, you are specifying the maximum amount of data to receive. It is the bytesRead that tells you how much you actually received. You need to only write that number of bytes to your output file, as only that portion of your buffer was populated with data.
See here for more details:
http://msdn.microsoft.com/en-us/library/w3xtz6a5

Test telnet communication on remote computer using VB.net?

I have a central server and I need to write a bit of vb.net which will see if I can telnet to a specified server on a specified port.
Is there anyway this can be done in VB.net? I thought about sending command prompts to the remote server to execute telnet, then output the logs of netsh and read those and send the information back to the central server for review.
Its a very messy way of doing it, so I was just wondering if there was an easier way
You should just create a TcpClient object with the IP address of the target server and port (typically 23 for telnet). Then call Connect!
See here for more info:
http://msdn.microsoft.com/en-us/library/system.net.sockets.tcpclient.aspx
Something like this (may not be exact):
Try
Dim telnetServerIp As String = "192.168.100.55"
Dim telnetPort As Integer = 23
Dim client As New TcpClient(telnetServerIp, telnetPort)
MessageBox.Show("Server is reachable")
Catch ex As Exception
MessageBox.Show("Could not reach server")
End Try
Be advised this is sample code. You'd want to clean up (close/dispose) the connection (TcpClient) object when you were done, etc. But it should get you started.
You should try something to implement this. there are lots help available for Telnet communication using .net.
Take idea from these specified links and implement in vb.net..
How can I open a telnet connection and run a few commands in C#
Telnet connection using .net
You can use a System.Net.Sockets.TcpClient object instead of a
socket object, which already has the socket parameters configured to
use ProtocolType.Tcp
1.Create a new TcpClient object, which takes a server name and a port (no IPEndPoint necessary, nice).
2.Pull a NetworkStream out of the TcpClient by calling GetStream()
3.Convert your message into bytes using Encoding.ASCII.GetBytes(string)
4.Now you can send and receive data using the stream.Write and stream.Read methods, respectively. The stream.Read method returns the number of bytes written to your receiving array, by the way.
5.Put the data back into human-readable format using Encoding.ASCII.GetString(byte array).
6.Clean up your mess before the network admins get mad by calling stream.Close() and client.Close().
Ref:C# 2.0* and Telnet - Not As Painful As It Sounds
// Create a TcpClient.
// Note, for this client to work you need to have a TcpServer
// connected to the same address as specified by the server, port
// combination.
Int32 port = 13000;
TcpClient client = new TcpClient(server, port);
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = System.Text.Encoding.ASCII.GetBytes(message);
// Get a client stream for reading and writing.
// Stream stream = client.GetStream();
NetworkStream stream = client.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
Console.WriteLine("Sent: {0}", message);
I made this for a project at work.
I think it's a complete general solution for most people's needs
However, This is my first real .net project so feel free to critique it.
Modeled on http://www.codeproject.com/Articles/63201/TelnetSocket
Imports System.Threading
Imports System.IO
Imports System.IO.Pipes
Public Class TelnetClient
Private Server As String
Private NetWorkProtocolClient As System.Net.Sockets.TcpClient
Private ServerStream As System.Net.Sockets.NetworkStream
Private DoReader As Boolean
Private ReaderThread As Thread
Private OutputPipe As AnonymousPipeServerStream
Private WaitForString As String
Private WaitForStringEvent As New AutoResetEvent(False)
ReadOnly Property IsConnected() As Boolean
Get
Return (Not (IsNothing(NetWorkProtocolClient)) AndAlso (NetWorkProtocolClient.Connected))
End Get
End Property
ReadOnly Property ConnectedTo() As String
Get
If (Not (IsNothing(NetWorkProtocolClient)) AndAlso (NetWorkProtocolClient.Connected)) Then
Return NetWorkProtocolClient.Client.RemoteEndPoint.ToString()
Else
Return "Nothing"
End If
End Get
End Property
'Set the Server string to connect to.
Public Sub SetServer(ByVal new_server As String)
'double check this later
Server = new_server
End Sub
'Connects if possilbe. If already conneced to some thing it Disconnects from old Telnet and connects to new Telnet.
Public Sub Connect()
Try
If (Not (IsNothing(NetWorkProtocolClient))) AndAlso NetWorkProtocolClient.Connected Then
Disconnect()
End If
If Not IsNothing(Server) Then
NetWorkProtocolClient = New System.Net.Sockets.TcpClient(Server, 23)
If NetWorkProtocolClient.Connected Then
'clear on a new client
WaitForString = Nothing
WaitForStringEvent.Reset()
NetWorkProtocolClient.NoDelay = True
ServerStream = NetWorkProtocolClient.GetStream()
ServerStream.ReadTimeout = 1000
DoReader = True
ReaderThread = New Thread(AddressOf ReaderTask)
ReaderThread.IsBackground = True
ReaderThread.Priority = ThreadPriority.AboveNormal
ReaderThread.Start()
End If
End If
Catch ex As System.Net.Sockets.SocketException
Console.WriteLine("SocketException Connect: {0}", ex)
End Try
End Sub
'Disconnects if connected, otherwise does nothing.
Public Sub Disconnect()
Try
If ReaderThread.IsAlive Then
DoReader = False
ReaderThread.Join(1000)
End If
If (Not (IsNothing(NetWorkProtocolClient))) Then
ServerStream.Close()
NetWorkProtocolClient.Close()
End If
Catch ex As System.Net.Sockets.SocketException
Console.WriteLine("SocketException Disconnect: {0}", ex)
End Try
End Sub
'Returns true if found before timeout milliseconds. Use -1 to have infinite wait time.
'Returns false if timeout occured.
Public Function WaitFor(ByVal command As String, ByVal timeout As Integer) As Boolean
WaitForString = New String(command)
WaitForStringEvent.Reset()
Dim was_signaled As Boolean = False
'Block until a the right value from reader or user defined timeout
was_signaled = WaitForStringEvent.WaitOne(timeout)
WaitForString = Nothing
Return was_signaled
End Function
Public Sub Write(ByVal command As String)
Try
If (Not (IsNothing(NetWorkProtocolClient))) Then
If NetWorkProtocolClient.Connected Then
'Write the value to the Stream
Dim bytes() As Byte = System.Text.Encoding.ASCII.GetBytes(command)
SyncLock ServerStream
ServerStream.Write(bytes, 0, bytes.Length)
End SyncLock
End If
End If
Catch ex As System.Net.Sockets.SocketException
Console.WriteLine("SocketException Write: {0}", ex)
End Try
End Sub
'appends CrLf for the caller
Public Sub WriteLine(ByVal command As String)
Try
If (Not (IsNothing(NetWorkProtocolClient))) Then
If NetWorkProtocolClient.Connected Then
'Write the value to the Stream
Dim bytes() As Byte = System.Text.Encoding.ASCII.GetBytes(command & vbCrLf)
SyncLock ServerStream
ServerStream.Write(bytes, 0, bytes.Length)
End SyncLock
End If
End If
Catch ex As System.Net.Sockets.SocketException
Console.WriteLine("SocketException Write: {0}", ex)
End Try
End Sub
'Get a pipe to read output. Note: anything written by WriteLine may be echoed back if the other Telnet offers to do it.
Public Function GetPipeHandle() As String
If Not IsNothing(ReaderThread) AndAlso ReaderThread.IsAlive AndAlso Not IsNothing(OutputPipe) Then
Return OutputPipe.GetClientHandleAsString
Else
Return Nothing
End If
End Function
'Task that watches the tcp stream, passes info to the negotiation function and signals the WaitFor task.
Private Sub ReaderTask()
Try
OutputPipe = New AnonymousPipeServerStream(PipeDirection.Out)
Dim prevData As New String("")
While (DoReader)
If (Not (IsNothing(NetWorkProtocolClient))) Then
If ServerStream.DataAvailable Then
'Grab Data
Dim data As [Byte]() = New [Byte](NetWorkProtocolClient.ReceiveBufferSize) {}
Dim bytes As Integer = ServerStream.Read(data, 0, data.Length)
'Negotiate anything that came in
bytes = Negotiate(data, bytes)
If (bytes > 0) Then
'append previous to the search sting incase messages were fragmented
Dim s As New String(prevData & System.Text.ASCIIEncoding.ASCII.GetChars(data))
'If Pipe is connected send it remaining real data
If OutputPipe.IsConnected Then
OutputPipe.Write(data, 0, bytes)
End If
'Check remaining against WaitForString
If Not IsNothing(WaitForString) Then
If s.Contains(WaitForString) Then
WaitForStringEvent.Set()
'clear prevData buffer because the WaitForString was found
prevData = New String("")
Else
'Nothing found make the current string part of the next string.
prevData = New String(s)
End If
Else
prevData = New String("")
End If
End If
Else
Thread.Sleep(100)
End If
End If
End While
OutputPipe.Close()
OutputPipe.Dispose()
Catch ex As System.IO.IOException
Console.WriteLine("IO Error: {0}", ex)
Catch ex As System.Net.Sockets.SocketException
Console.WriteLine("SocketException Reader: {0}", ex)
End Try
End Sub
'Shamelessly adapted from http://www.codeproject.com/Articles/63201/TelnetSocket
'The basic algorithm used here is:
' Iterate across the incoming bytes
' Assume that an IAC (byte 255) is the first of a two- or three-byte Telnet command and handle it:
' If two IACs are together, they represent one data byte 255
' Ignore the Go-Ahead command
' Respond WONT to all DOs and DONTs
' Respond DONT to all WONTs
' Respond DO to WILL ECHO and WILL SUPPRESS GO-AHEAD
' Respond DONT to all other WILLs
' Any other bytes are data; ignore nulls, and shift the rest as necessary
' Return the number of bytes that remain after removing the Telnet command and ignoring nulls
Private Function Negotiate(ByVal data As Byte(), ByVal length As Int32) As Int32
Dim index As Int32 = 0
Dim remaining As Int32 = 0
While (index < length)
If (data(index) = TelnetBytes.IAC) Then
Try
Select Case data(index + 1)
Case TelnetBytes.IAC
data(remaining) = data(index)
remaining += 1
index += 2
Case TelnetBytes.GA
index += 2
Case TelnetBytes.WDO
data(index + 1) = TelnetBytes.WONT
SyncLock ServerStream
ServerStream.Write(data, index, 3)
End SyncLock
index += 3
Case TelnetBytes.DONT
data(index + 1) = TelnetBytes.WONT
SyncLock ServerStream
ServerStream.Write(data, index, 3)
End SyncLock
index += 3
Case TelnetBytes.WONT
data(index + 1) = TelnetBytes.DONT
SyncLock ServerStream
ServerStream.Write(data, index, 3)
End SyncLock
index += 3
Case TelnetBytes.WILL
Dim action As Byte = TelnetBytes.DONT
Select Case data(index + 2)
Case TelnetBytes.ECHO
action = TelnetBytes.WDO
Case TelnetBytes.SUPP
action = TelnetBytes.WDO
End Select
data(index + 1) = action
SyncLock ServerStream
ServerStream.Write(data, index, 3)
End SyncLock
index += 3
End Select
Catch ex As System.IndexOutOfRangeException
index = length
End Try
Else
If (data(index) <> 0) Then
data(remaining) = data(index)
remaining += 1
End If
index += 1
End If
End While
Return remaining
End Function
Private Structure TelnetBytes
'Commands
Public Const GA As Byte = 249
Public Const WILL As Byte = 251
Public Const WONT As Byte = 252
Public Const WDO As Byte = 253 'Actually just DO but is protected word in vb.net
Public Const DONT As Byte = 254
Public Const IAC As Byte = 255
'Options
Public Const ECHO As Byte = 1
Public Const SUPP As Byte = 3
End Structure
End Class