Events and Delegates

Nate Bross

Contributor
Joined
Apr 6, 2005
Messages
601
Location
Chicago, IL
I have this code (posting the whole class for context) I am creating a collection of 'clients' each client has a received data event.

If you look at the Test() sub, these are the values of the variables, and the unexpected results.

First Time Called:
Data() = "Test" (in byte array)
str = nothing
ols = Encoding.ASCII.GetString(Data) = "Test" (this part works)
str = String.Concat(str, ols) = "Test"

Second Time Called:
Data() = "a Second Test" (in byte array)
str = "Test"
ols = Encoding.ASCII.GetString(Data) = "a Second Test" (this part works)
str = String.Concat(str, ols) = "Test"

Third Time Called:
Data() = "a Third Test" (in byte array)
str = "Test"
ols = Encoding.ASCII.GetString(Data) = "a Third Test" (this part works)
str = String.Concat(str, ols) = "Test"


The variable ols has the correct value, but it never gets concatonated to the other string. No matter what I do, the String.Concat NEVER concats the second string to the first one, on any subsequent calling of the method. It ONLY works the first time. Does anyone have any sugestions or ideas to follow?

Visual Basic:
Public Class Listener
        Dim TcpIpListener As TcpListener
        Dim ListenThread As Thread
        Dim Clients As Connections

        Dim str As String




        Public Sub New(ByVal IP As IPAddress, ByVal iPort As Integer, ByRef ClientCollection As Connections)
            TcpIpListener = New TcpListener(IP, iPort)

            Clients = ClientCollection


            ListenThread = New Thread(AddressOf Listen)
            ListenThread.Start()
        End Sub

        Sub Listen()
            TcpIpListener.Start()
            While True
                If TcpIpListener.Pending = True Then
                    Dim tcpInConn As Communication.Client
                    tcpInConn = New Communication.Client(TcpIpListener.AcceptTcpClient())
                    Clients.Add(tcpInConn)
                    AddHandler tcpInConn.DataReceived, AddressOf Test
                End If
                Application.DoEvents()
            End While
            TcpIpListener.Stop()
        End Sub


        Private Sub Test(ByVal sender As Object, ByVal Data() As Byte)
            Dim ols As String = ""

            ols = Encoding.ASCII.GetString(Data)

            str = String.Concat(str, ols)
        End Sub

        Public ReadOnly Property S()
            Get
                Return str
            End Get
        End Property

    End Class
 
Are you doing a debugger step through to see these values? I can't imagine why this does not work. Is is possible that each time Test() is called it is being done on a different instance of Listener? And, out of curiousity, have you tried simply using the concatenation operator (&) yet? What happens when you try a very simple test case such as:
Visual Basic:
MessageBox.Show(String.Concat("Message", "Box"))
And what happens if you condense Test() into:
Visual Basic:
Private Sub Test(ByVal sender As Object, ByVal Data() As Byte)
    str &= Encoding.ASCII.GetString(Data)
End Sub
 
I tried &=, +=, and String.Concat. All with the same result.

Visual Basic:
MessageBox.Show(String.Concat("Message", "Box"))

Returns "MessageBox" as expected

Strangly enough however,
Visual Basic:
String.Concat("Message: ", Encoding.ASCII.GetString(Data))
Returns the expected data "Message : ByteArray". Additionally, the original str variable contains the first value.

Is there a better way to achieve the functionaliy here? Basically what I want to do is create a general purpos server application. That accepts clients and spanws them into a new thread, whenever a client receives data I want the same method to be called. (Like an array of Winsock Controls in VB6, I understand it was only a collection in VB6, but I have been unable to achieve the same functionallity.)
 
Are you positive that this method is being called on the same instance of Listener each time? Does anything change if you declare str and Test() statically (i.e. as shared)?
 
I only have one instance of listener in the entire program. So I'm pretty confidant it's the same one every time through. When I declared str and Test as Shared, the results were exactly the same as before.

Maybe there is something wrong with my 'Client' class.

Visual Basic:
Imports System.Net
Imports System.Net.Sockets
Imports System.Text
Imports system.Threading

Namespace Communication
    Public Class Client
        Dim TcpIpClient As TcpClient

        Dim NetStream As NetworkStream
        Dim ReceiverThread As Thread


        Public Event DataReceived As DataReceivedHandler

        ''' <summary>
        ''' Creates the Object
        ''' </summary>
        ''' <param name="IP">IP Address This Client Will Connect To.</param>
        ''' <param name="Port">Port This Client Will Connect On.</param>
        ''' <param name="sOpenMessage">Message That Will Be Sent Upon Connection.</param>
        ''' <remarks>Connects to the IP Address on the Port Provided.</remarks>
        Public Sub New(ByVal IP As IPAddress, ByVal Port As Integer, Optional ByVal sOpenMessage As String = "")
            TcpIpClient = New TcpClient
            TcpIpClient.Connect(IP, Port)

            If TcpIpClient.Connected = True Then
                NetStream = TcpIpClient.GetStream()

                ReceiverThread = New Thread(AddressOf ProcessData)
                ReceiverThread.Start()
            End If
        End Sub

        ''' <summary>
        ''' Creates the Object
        ''' </summary>
        ''' <param name="ClientTCP">A TcpClient Object Already Connected.</param>
        ''' <remarks></remarks>
        Public Sub New(ByRef ClientTCP As TcpClient)
            TcpIpClient = ClientTCP

            If TcpIpClient.Connected = True Then
                NetStream = TcpIpClient.GetStream()

                ReceiverThread = New Thread(AddressOf ProcessData)
                ReceiverThread.Start()
            End If
        End Sub

        ''' <summary>
        ''' Creates the Object
        ''' </summary>
        ''' <param name="sIP">IP Address (Big-Endian) This Client Will Connect To.</param>
        ''' <param name="Port">Port This Client Will Connect On.</param>
        ''' <param name="sOpenMessage">Message That Will Be Sent Upon Connection.</param>
        ''' <remarks>Connects to the IP Address on the Port Provided.</remarks>
        Public Sub New(ByVal sIP As String, ByVal Port As Integer, Optional ByVal sOpenMessage As String = "")
            Me.New(IPAddress.Parse(sIP), Port, sOpenMessage)
        End Sub


        Public Sub ProcessData()
            While TcpIpClient.Connected = True
                If TcpIpClient.Connected = True Then
                    ' Get the stream
                    Dim networkStream As NetworkStream = TcpIpClient.GetStream()
                    If networkStream.DataAvailable = True Then
                        ' Read the stream into a byte array
                        Dim bytes(TcpIpClient.ReceiveBufferSize) As Byte
                        networkStream.Read(bytes, 0, CInt(TcpIpClient.ReceiveBufferSize))

                        RaiseEvent DataReceived(Me, bytes)
                    End If
                End If
            End While
        End Sub

        Public Sub SendData(ByVal buff() As Byte, ByVal iOffset As Integer, ByVal iLength As Integer)
            If NetStream.CanRead = True And NetStream.CanWrite = True Then
                NetStream.Write(buff, iOffset, iLength)
                NetStream.Flush()
            End If
        End Sub


        Public Delegate Sub DataReceivedHandler(ByVal sender As Object, ByVal Data() As Byte)
    End Class


    Public Class Listener
        Dim TcpIpListener As TcpListener
        Dim ListenThread As Thread
        Dim Clients As Connections

        Shared str As String

        Public Sub New(ByVal IP As IPAddress, ByVal iPort As Integer, ByRef ClientCollection As Connections)
            TcpIpListener = New TcpListener(IP, iPort)

            Clients = ClientCollection


            ListenThread = New Thread(AddressOf Listen)
            ListenThread.Start()
        End Sub

        Sub Listen()
            TcpIpListener.Start()
            While True
                If TcpIpListener.Pending = True Then
                    Dim tcpInConn As Communication.Client
                    tcpInConn = New Communication.Client(TcpIpListener.AcceptTcpClient())
                    Clients.Add(tcpInConn)
                    AddHandler tcpInConn.DataReceived, AddressOf Test
                End If
                Application.DoEvents()
            End While
            TcpIpListener.Stop()
        End Sub

        Public Shared Sub Test(ByVal sender As Object, ByVal Data() As Byte)
            str &= Encoding.ASCII.GetString(Data)
            MessageBox.Show(String.Concat("Message: ", Encoding.ASCII.GetString(Data)))
        End Sub

        Public ReadOnly Property S()
            Get
                Return str
            End Get
        End Property
    End Class
End Namespace

This code, is exactly the same as what I am running, I haven't stripped anything out; additionally, this (below) is the code for the Form1 that is the main (and only) form in the application.

Visual Basic:
Imports System.Net
Imports system.net.sockets
Imports System.Text

Public Class Form1
    Dim tcpInConn As TcpClient
    Dim tcpOutConn As TcpClient
    Dim tcpListen As TcpListener

    Dim WithEvents c As Communication.Client
    Dim l As Communication.Listener

    Dim Clients As New Connections


    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    End Sub

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        
        l = New Communication.Listener(IPAddress.Parse("127.0.0.1"), 6666, Clients)
        l.Listen()

    End Sub

    Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
        c = New Communication.Client(IPAddress.Parse("127.0.0.1"), 6666)
    End Sub

    Private Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
        c.SendData(Encoding.ASCII.GetBytes(TextBox2.Text), 0, TextBox2.Text.Length())
    End Sub



    Private Sub c_DataReceived(ByVal sender As Object, ByVal Data() As Byte) Handles c.DataReceived
        MessageBox.Show(Encoding.ASCII.GetString(Data))
    End Sub

    Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
        TextBox1.Text = l.S
    End Sub
End Class

I know there are some unused variables, I was using them before just haven't gotten around to deleting them all yet.

I don't know what other information you might need to help me out, but let me know and I'll see what I can find.
 
Back
Top